0de34262679fa44baa71294f7c23801227387b3f
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3951  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3952  * @cfg {String} size (sm|lg|xl) default empty
3953  * @cfg {Number} max_width set the max width of modal
3954  * @cfg {Boolean} editableTitle can the title be edited
3955
3956  *
3957  *
3958  * @constructor
3959  * Create a new Modal Dialog
3960  * @param {Object} config The config object
3961  */
3962
3963 Roo.bootstrap.Modal = function(config){
3964     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3965     this.addEvents({
3966         // raw events
3967         /**
3968          * @event btnclick
3969          * The raw btnclick event for the button
3970          * @param {Roo.EventObject} e
3971          */
3972         "btnclick" : true,
3973         /**
3974          * @event resize
3975          * Fire when dialog resize
3976          * @param {Roo.bootstrap.Modal} this
3977          * @param {Roo.EventObject} e
3978          */
3979         "resize" : true,
3980         /**
3981          * @event titlechanged
3982          * Fire when the editable title has been changed
3983          * @param {Roo.bootstrap.Modal} this
3984          * @param {Roo.EventObject} value
3985          */
3986         "titlechanged" : true 
3987         
3988     });
3989     this.buttons = this.buttons || [];
3990
3991     if (this.tmpl) {
3992         this.tmpl = Roo.factory(this.tmpl);
3993     }
3994
3995 };
3996
3997 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3998
3999     title : 'test dialog',
4000
4001     buttons : false,
4002
4003     // set on load...
4004
4005     html: false,
4006
4007     tmp: false,
4008
4009     specificTitle: false,
4010
4011     buttonPosition: 'right',
4012
4013     allow_close : true,
4014
4015     animate : true,
4016
4017     fitwindow: false,
4018     
4019      // private
4020     dialogEl: false,
4021     bodyEl:  false,
4022     footerEl:  false,
4023     titleEl:  false,
4024     closeEl:  false,
4025
4026     size: '',
4027     
4028     max_width: 0,
4029     
4030     max_height: 0,
4031     
4032     fit_content: false,
4033     editableTitle  : false,
4034
4035     onRender : function(ct, position)
4036     {
4037         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4038
4039         if(!this.el){
4040             var cfg = Roo.apply({},  this.getAutoCreate());
4041             cfg.id = Roo.id();
4042             //if(!cfg.name){
4043             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4044             //}
4045             //if (!cfg.name.length) {
4046             //    delete cfg.name;
4047            // }
4048             if (this.cls) {
4049                 cfg.cls += ' ' + this.cls;
4050             }
4051             if (this.style) {
4052                 cfg.style = this.style;
4053             }
4054             this.el = Roo.get(document.body).createChild(cfg, position);
4055         }
4056         //var type = this.el.dom.type;
4057
4058
4059         if(this.tabIndex !== undefined){
4060             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4061         }
4062
4063         this.dialogEl = this.el.select('.modal-dialog',true).first();
4064         this.bodyEl = this.el.select('.modal-body',true).first();
4065         this.closeEl = this.el.select('.modal-header .close', true).first();
4066         this.headerEl = this.el.select('.modal-header',true).first();
4067         this.titleEl = this.el.select('.modal-title',true).first();
4068         this.footerEl = this.el.select('.modal-footer',true).first();
4069
4070         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4071         
4072         //this.el.addClass("x-dlg-modal");
4073
4074         if (this.buttons.length) {
4075             Roo.each(this.buttons, function(bb) {
4076                 var b = Roo.apply({}, bb);
4077                 b.xns = b.xns || Roo.bootstrap;
4078                 b.xtype = b.xtype || 'Button';
4079                 if (typeof(b.listeners) == 'undefined') {
4080                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4081                 }
4082
4083                 var btn = Roo.factory(b);
4084
4085                 btn.render(this.getButtonContainer());
4086
4087             },this);
4088         }
4089         // render the children.
4090         var nitems = [];
4091
4092         if(typeof(this.items) != 'undefined'){
4093             var items = this.items;
4094             delete this.items;
4095
4096             for(var i =0;i < items.length;i++) {
4097                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4098             }
4099         }
4100
4101         this.items = nitems;
4102
4103         // where are these used - they used to be body/close/footer
4104
4105
4106         this.initEvents();
4107         //this.el.addClass([this.fieldClass, this.cls]);
4108
4109     },
4110
4111     getAutoCreate : function()
4112     {
4113         // we will default to modal-body-overflow - might need to remove or make optional later.
4114         var bdy = {
4115                 cls : 'modal-body ' + (this.fitwindow ? 'overflow-auto' : ''), 
4116                 html : this.html || ''
4117         };
4118
4119         var title = {
4120             tag: 'h4',
4121             cls : 'modal-title',
4122             html : this.title
4123         };
4124
4125         if(this.specificTitle){ // WTF is this?
4126             title = this.title;
4127         }
4128
4129         var header = [];
4130         if (this.allow_close && Roo.bootstrap.version == 3) {
4131             header.push({
4132                 tag: 'button',
4133                 cls : 'close',
4134                 html : '&times'
4135             });
4136         }
4137
4138         header.push(title);
4139
4140         if (this.editableTitle) {
4141             header.push({
4142                 cls: 'form-control roo-editable-title d-none',
4143                 tag: 'input',
4144                 type: 'text'
4145             });
4146         }
4147         
4148         if (this.allow_close && Roo.bootstrap.version == 4) {
4149             header.push({
4150                 tag: 'button',
4151                 cls : 'close',
4152                 html : '&times'
4153             });
4154         }
4155         
4156         var size = '';
4157
4158         if(this.size.length){
4159             size = 'modal-' + this.size;
4160         }
4161         
4162         var footer = Roo.bootstrap.version == 3 ?
4163             {
4164                 cls : 'modal-footer',
4165                 cn : [
4166                     {
4167                         tag: 'div',
4168                         cls: 'btn-' + this.buttonPosition
4169                     }
4170                 ]
4171
4172             } :
4173             {  // BS4 uses mr-auto on left buttons....
4174                 cls : 'modal-footer'
4175             };
4176
4177             
4178
4179         
4180         
4181         var modal = {
4182             cls: "modal",
4183              cn : [
4184                 {
4185                     cls: "modal-dialog " + size,
4186                     cn : [
4187                         {
4188                             cls : "modal-content",
4189                             cn : [
4190                                 {
4191                                     cls : 'modal-header',
4192                                     cn : header
4193                                 },
4194                                 bdy,
4195                                 footer
4196                             ]
4197
4198                         }
4199                     ]
4200
4201                 }
4202             ]
4203         };
4204
4205         if(this.animate){
4206             modal.cls += ' fade';
4207         }
4208
4209         return modal;
4210
4211     },
4212     getChildContainer : function() {
4213
4214          return this.bodyEl;
4215
4216     },
4217     getButtonContainer : function() {
4218         
4219          return Roo.bootstrap.version == 4 ?
4220             this.el.select('.modal-footer',true).first()
4221             : this.el.select('.modal-footer div',true).first();
4222
4223     },
4224     initEvents : function()
4225     {
4226         if (this.allow_close) {
4227             this.closeEl.on('click', this.hide, this);
4228         }
4229         Roo.EventManager.onWindowResize(this.resize, this, true);
4230         if (this.editableTitle) {
4231             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4232             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4233             this.headerEditEl.on('keyup', function(e) {
4234                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4235                         this.toggleHeaderInput(false)
4236                     }
4237                 }, this);
4238             this.headerEditEl.on('blur', function(e) {
4239                 this.toggleHeaderInput(false)
4240             },this);
4241         }
4242
4243     },
4244   
4245
4246     resize : function()
4247     {
4248         this.maskEl.setSize(
4249             Roo.lib.Dom.getViewWidth(true),
4250             Roo.lib.Dom.getViewHeight(true)
4251         );
4252         
4253         if (this.fitwindow) {
4254             
4255            
4256             this.setSize(
4257                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4258                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4259             );
4260             return;
4261         }
4262         
4263         if(this.max_width !== 0) {
4264             
4265             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4266             
4267             if(this.height) {
4268                 this.setSize(w, this.height);
4269                 return;
4270             }
4271             
4272             if(this.max_height) {
4273                 this.setSize(w,Math.min(
4274                     this.max_height,
4275                     Roo.lib.Dom.getViewportHeight(true) - 60
4276                 ));
4277                 
4278                 return;
4279             }
4280             
4281             if(!this.fit_content) {
4282                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4283                 return;
4284             }
4285             
4286             this.setSize(w, Math.min(
4287                 60 +
4288                 this.headerEl.getHeight() + 
4289                 this.footerEl.getHeight() + 
4290                 this.getChildHeight(this.bodyEl.dom.childNodes),
4291                 Roo.lib.Dom.getViewportHeight(true) - 60)
4292             );
4293         }
4294         
4295     },
4296
4297     setSize : function(w,h)
4298     {
4299         if (!w && !h) {
4300             return;
4301         }
4302         
4303         this.resizeTo(w,h);
4304     },
4305
4306     show : function() {
4307
4308         if (!this.rendered) {
4309             this.render();
4310         }
4311         this.toggleHeaderInput(false);
4312         //this.el.setStyle('display', 'block');
4313         this.el.removeClass('hideing');
4314         this.el.dom.style.display='block';
4315         
4316         Roo.get(document.body).addClass('modal-open');
4317  
4318         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4319             
4320             (function(){
4321                 this.el.addClass('show');
4322                 this.el.addClass('in');
4323             }).defer(50, this);
4324         }else{
4325             this.el.addClass('show');
4326             this.el.addClass('in');
4327         }
4328
4329         // not sure how we can show data in here..
4330         //if (this.tmpl) {
4331         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4332         //}
4333
4334         Roo.get(document.body).addClass("x-body-masked");
4335         
4336         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4337         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4338         this.maskEl.dom.style.display = 'block';
4339         this.maskEl.addClass('show');
4340         
4341         
4342         this.resize();
4343         
4344         this.fireEvent('show', this);
4345
4346         // set zindex here - otherwise it appears to be ignored...
4347         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4348
4349         (function () {
4350             this.items.forEach( function(e) {
4351                 e.layout ? e.layout() : false;
4352
4353             });
4354         }).defer(100,this);
4355
4356     },
4357     hide : function()
4358     {
4359         if(this.fireEvent("beforehide", this) !== false){
4360             
4361             this.maskEl.removeClass('show');
4362             
4363             this.maskEl.dom.style.display = '';
4364             Roo.get(document.body).removeClass("x-body-masked");
4365             this.el.removeClass('in');
4366             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4367
4368             if(this.animate){ // why
4369                 this.el.addClass('hideing');
4370                 this.el.removeClass('show');
4371                 (function(){
4372                     if (!this.el.hasClass('hideing')) {
4373                         return; // it's been shown again...
4374                     }
4375                     
4376                     this.el.dom.style.display='';
4377
4378                     Roo.get(document.body).removeClass('modal-open');
4379                     this.el.removeClass('hideing');
4380                 }).defer(150,this);
4381                 
4382             }else{
4383                 this.el.removeClass('show');
4384                 this.el.dom.style.display='';
4385                 Roo.get(document.body).removeClass('modal-open');
4386
4387             }
4388             this.fireEvent('hide', this);
4389         }
4390     },
4391     isVisible : function()
4392     {
4393         
4394         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4395         
4396     },
4397
4398     addButton : function(str, cb)
4399     {
4400
4401
4402         var b = Roo.apply({}, { html : str } );
4403         b.xns = b.xns || Roo.bootstrap;
4404         b.xtype = b.xtype || 'Button';
4405         if (typeof(b.listeners) == 'undefined') {
4406             b.listeners = { click : cb.createDelegate(this)  };
4407         }
4408
4409         var btn = Roo.factory(b);
4410
4411         btn.render(this.getButtonContainer());
4412
4413         return btn;
4414
4415     },
4416
4417     setDefaultButton : function(btn)
4418     {
4419         //this.el.select('.modal-footer').()
4420     },
4421
4422     resizeTo: function(w,h)
4423     {
4424         this.dialogEl.setWidth(w);
4425         
4426         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4427
4428         this.bodyEl.setHeight(h - diff);
4429         
4430         this.fireEvent('resize', this);
4431     },
4432     
4433     setContentSize  : function(w, h)
4434     {
4435
4436     },
4437     onButtonClick: function(btn,e)
4438     {
4439         //Roo.log([a,b,c]);
4440         this.fireEvent('btnclick', btn.name, e);
4441     },
4442      /**
4443      * Set the title of the Dialog
4444      * @param {String} str new Title
4445      */
4446     setTitle: function(str) {
4447         this.titleEl.dom.innerHTML = str;
4448         this.title = str;
4449     },
4450     /**
4451      * Set the body of the Dialog
4452      * @param {String} str new Title
4453      */
4454     setBody: function(str) {
4455         this.bodyEl.dom.innerHTML = str;
4456     },
4457     /**
4458      * Set the body of the Dialog using the template
4459      * @param {Obj} data - apply this data to the template and replace the body contents.
4460      */
4461     applyBody: function(obj)
4462     {
4463         if (!this.tmpl) {
4464             Roo.log("Error - using apply Body without a template");
4465             //code
4466         }
4467         this.tmpl.overwrite(this.bodyEl, obj);
4468     },
4469     
4470     getChildHeight : function(child_nodes)
4471     {
4472         if(
4473             !child_nodes ||
4474             child_nodes.length == 0
4475         ) {
4476             return 0;
4477         }
4478         
4479         var child_height = 0;
4480         
4481         for(var i = 0; i < child_nodes.length; i++) {
4482             
4483             /*
4484             * for modal with tabs...
4485             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4486                 
4487                 var layout_childs = child_nodes[i].childNodes;
4488                 
4489                 for(var j = 0; j < layout_childs.length; j++) {
4490                     
4491                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4492                         
4493                         var layout_body_childs = layout_childs[j].childNodes;
4494                         
4495                         for(var k = 0; k < layout_body_childs.length; k++) {
4496                             
4497                             if(layout_body_childs[k].classList.contains('navbar')) {
4498                                 child_height += layout_body_childs[k].offsetHeight;
4499                                 continue;
4500                             }
4501                             
4502                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4503                                 
4504                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4505                                 
4506                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4507                                     
4508                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4509                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4510                                         continue;
4511                                     }
4512                                     
4513                                 }
4514                                 
4515                             }
4516                             
4517                         }
4518                     }
4519                 }
4520                 continue;
4521             }
4522             */
4523             
4524             child_height += child_nodes[i].offsetHeight;
4525             // Roo.log(child_nodes[i].offsetHeight);
4526         }
4527         
4528         return child_height;
4529     },
4530     toggleHeaderInput : function(is_edit)
4531     {
4532         if (!this.editableTitle) {
4533             return; // not editable.
4534         }
4535         if (is_edit && this.is_header_editing) {
4536             return; // already editing..
4537         }
4538         if (is_edit) {
4539     
4540             this.headerEditEl.dom.value = this.title;
4541             this.headerEditEl.removeClass('d-none');
4542             this.headerEditEl.dom.focus();
4543             this.titleEl.addClass('d-none');
4544             
4545             this.is_header_editing = true;
4546             return
4547         }
4548         // flip back to not editing.
4549         this.title = this.headerEditEl.dom.value;
4550         this.headerEditEl.addClass('d-none');
4551         this.titleEl.removeClass('d-none');
4552         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4553         this.is_header_editing = false;
4554         this.fireEvent('titlechanged', this, this.title);
4555     
4556             
4557         
4558     }
4559
4560 });
4561
4562
4563 Roo.apply(Roo.bootstrap.Modal,  {
4564     /**
4565          * Button config that displays a single OK button
4566          * @type Object
4567          */
4568         OK :  [{
4569             name : 'ok',
4570             weight : 'primary',
4571             html : 'OK'
4572         }],
4573         /**
4574          * Button config that displays Yes and No buttons
4575          * @type Object
4576          */
4577         YESNO : [
4578             {
4579                 name  : 'no',
4580                 html : 'No'
4581             },
4582             {
4583                 name  :'yes',
4584                 weight : 'primary',
4585                 html : 'Yes'
4586             }
4587         ],
4588
4589         /**
4590          * Button config that displays OK and Cancel buttons
4591          * @type Object
4592          */
4593         OKCANCEL : [
4594             {
4595                name : 'cancel',
4596                 html : 'Cancel'
4597             },
4598             {
4599                 name : 'ok',
4600                 weight : 'primary',
4601                 html : 'OK'
4602             }
4603         ],
4604         /**
4605          * Button config that displays Yes, No and Cancel buttons
4606          * @type Object
4607          */
4608         YESNOCANCEL : [
4609             {
4610                 name : 'yes',
4611                 weight : 'primary',
4612                 html : 'Yes'
4613             },
4614             {
4615                 name : 'no',
4616                 html : 'No'
4617             },
4618             {
4619                 name : 'cancel',
4620                 html : 'Cancel'
4621             }
4622         ],
4623         
4624         zIndex : 10001
4625 });
4626
4627 /*
4628  * - LGPL
4629  *
4630  * messagebox - can be used as a replace
4631  * 
4632  */
4633 /**
4634  * @class Roo.MessageBox
4635  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4636  * Example usage:
4637  *<pre><code>
4638 // Basic alert:
4639 Roo.Msg.alert('Status', 'Changes saved successfully.');
4640
4641 // Prompt for user data:
4642 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4643     if (btn == 'ok'){
4644         // process text value...
4645     }
4646 });
4647
4648 // Show a dialog using config options:
4649 Roo.Msg.show({
4650    title:'Save Changes?',
4651    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4652    buttons: Roo.Msg.YESNOCANCEL,
4653    fn: processResult,
4654    animEl: 'elId'
4655 });
4656 </code></pre>
4657  * @singleton
4658  */
4659 Roo.bootstrap.MessageBox = function(){
4660     var dlg, opt, mask, waitTimer;
4661     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4662     var buttons, activeTextEl, bwidth;
4663
4664     
4665     // private
4666     var handleButton = function(button){
4667         dlg.hide();
4668         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4669     };
4670
4671     // private
4672     var handleHide = function(){
4673         if(opt && opt.cls){
4674             dlg.el.removeClass(opt.cls);
4675         }
4676         //if(waitTimer){
4677         //    Roo.TaskMgr.stop(waitTimer);
4678         //    waitTimer = null;
4679         //}
4680     };
4681
4682     // private
4683     var updateButtons = function(b){
4684         var width = 0;
4685         if(!b){
4686             buttons["ok"].hide();
4687             buttons["cancel"].hide();
4688             buttons["yes"].hide();
4689             buttons["no"].hide();
4690             dlg.footerEl.hide();
4691             
4692             return width;
4693         }
4694         dlg.footerEl.show();
4695         for(var k in buttons){
4696             if(typeof buttons[k] != "function"){
4697                 if(b[k]){
4698                     buttons[k].show();
4699                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4700                     width += buttons[k].el.getWidth()+15;
4701                 }else{
4702                     buttons[k].hide();
4703                 }
4704             }
4705         }
4706         return width;
4707     };
4708
4709     // private
4710     var handleEsc = function(d, k, e){
4711         if(opt && opt.closable !== false){
4712             dlg.hide();
4713         }
4714         if(e){
4715             e.stopEvent();
4716         }
4717     };
4718
4719     return {
4720         /**
4721          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4722          * @return {Roo.BasicDialog} The BasicDialog element
4723          */
4724         getDialog : function(){
4725            if(!dlg){
4726                 dlg = new Roo.bootstrap.Modal( {
4727                     //draggable: true,
4728                     //resizable:false,
4729                     //constraintoviewport:false,
4730                     //fixedcenter:true,
4731                     //collapsible : false,
4732                     //shim:true,
4733                     //modal: true,
4734                 //    width: 'auto',
4735                   //  height:100,
4736                     //buttonAlign:"center",
4737                     closeClick : function(){
4738                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4739                             handleButton("no");
4740                         }else{
4741                             handleButton("cancel");
4742                         }
4743                     }
4744                 });
4745                 dlg.render();
4746                 dlg.on("hide", handleHide);
4747                 mask = dlg.mask;
4748                 //dlg.addKeyListener(27, handleEsc);
4749                 buttons = {};
4750                 this.buttons = buttons;
4751                 var bt = this.buttonText;
4752                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4753                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4754                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4755                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4756                 //Roo.log(buttons);
4757                 bodyEl = dlg.bodyEl.createChild({
4758
4759                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4760                         '<textarea class="roo-mb-textarea"></textarea>' +
4761                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4762                 });
4763                 msgEl = bodyEl.dom.firstChild;
4764                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4765                 textboxEl.enableDisplayMode();
4766                 textboxEl.addKeyListener([10,13], function(){
4767                     if(dlg.isVisible() && opt && opt.buttons){
4768                         if(opt.buttons.ok){
4769                             handleButton("ok");
4770                         }else if(opt.buttons.yes){
4771                             handleButton("yes");
4772                         }
4773                     }
4774                 });
4775                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4776                 textareaEl.enableDisplayMode();
4777                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4778                 progressEl.enableDisplayMode();
4779                 
4780                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4781                 var pf = progressEl.dom.firstChild;
4782                 if (pf) {
4783                     pp = Roo.get(pf.firstChild);
4784                     pp.setHeight(pf.offsetHeight);
4785                 }
4786                 
4787             }
4788             return dlg;
4789         },
4790
4791         /**
4792          * Updates the message box body text
4793          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4794          * the XHTML-compliant non-breaking space character '&amp;#160;')
4795          * @return {Roo.MessageBox} This message box
4796          */
4797         updateText : function(text)
4798         {
4799             if(!dlg.isVisible() && !opt.width){
4800                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4801                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4802             }
4803             msgEl.innerHTML = text || '&#160;';
4804       
4805             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4806             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4807             var w = Math.max(
4808                     Math.min(opt.width || cw , this.maxWidth), 
4809                     Math.max(opt.minWidth || this.minWidth, bwidth)
4810             );
4811             if(opt.prompt){
4812                 activeTextEl.setWidth(w);
4813             }
4814             if(dlg.isVisible()){
4815                 dlg.fixedcenter = false;
4816             }
4817             // to big, make it scroll. = But as usual stupid IE does not support
4818             // !important..
4819             
4820             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4821                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4822                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4823             } else {
4824                 bodyEl.dom.style.height = '';
4825                 bodyEl.dom.style.overflowY = '';
4826             }
4827             if (cw > w) {
4828                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4829             } else {
4830                 bodyEl.dom.style.overflowX = '';
4831             }
4832             
4833             dlg.setContentSize(w, bodyEl.getHeight());
4834             if(dlg.isVisible()){
4835                 dlg.fixedcenter = true;
4836             }
4837             return this;
4838         },
4839
4840         /**
4841          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4842          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4843          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4844          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4845          * @return {Roo.MessageBox} This message box
4846          */
4847         updateProgress : function(value, text){
4848             if(text){
4849                 this.updateText(text);
4850             }
4851             
4852             if (pp) { // weird bug on my firefox - for some reason this is not defined
4853                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4854                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4855             }
4856             return this;
4857         },        
4858
4859         /**
4860          * Returns true if the message box is currently displayed
4861          * @return {Boolean} True if the message box is visible, else false
4862          */
4863         isVisible : function(){
4864             return dlg && dlg.isVisible();  
4865         },
4866
4867         /**
4868          * Hides the message box if it is displayed
4869          */
4870         hide : function(){
4871             if(this.isVisible()){
4872                 dlg.hide();
4873             }  
4874         },
4875
4876         /**
4877          * Displays a new message box, or reinitializes an existing message box, based on the config options
4878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4879          * The following config object properties are supported:
4880          * <pre>
4881 Property    Type             Description
4882 ----------  ---------------  ------------------------------------------------------------------------------------
4883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4884                                    closes (defaults to undefined)
4885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4888                                    progress and wait dialogs will ignore this property and always hide the
4889                                    close button as they can only be closed programmatically.
4890 cls               String           A custom CSS class to apply to the message box element
4891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4892                                    displayed (defaults to 75)
4893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4894                                    function will be btn (the name of the button that was clicked, if applicable,
4895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4896                                    Progress and wait dialogs will ignore this option since they do not respond to
4897                                    user actions and can only be closed programmatically, so any required function
4898                                    should be called by the same code after it closes the dialog.
4899 icon              String           A CSS class that provides a background image to be used as an icon for
4900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4903 modal             Boolean          False to allow user interaction with the page while the message box is
4904                                    displayed (defaults to true)
4905 msg               String           A string that will replace the existing message box body text (defaults
4906                                    to the XHTML-compliant non-breaking space character '&#160;')
4907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4908 progress          Boolean          True to display a progress bar (defaults to false)
4909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4912 title             String           The title text
4913 value             String           The string value to set into the active textbox element if displayed
4914 wait              Boolean          True to display a progress bar (defaults to false)
4915 width             Number           The width of the dialog in pixels
4916 </pre>
4917          *
4918          * Example usage:
4919          * <pre><code>
4920 Roo.Msg.show({
4921    title: 'Address',
4922    msg: 'Please enter your address:',
4923    width: 300,
4924    buttons: Roo.MessageBox.OKCANCEL,
4925    multiline: true,
4926    fn: saveAddress,
4927    animEl: 'addAddressBtn'
4928 });
4929 </code></pre>
4930          * @param {Object} config Configuration options
4931          * @return {Roo.MessageBox} This message box
4932          */
4933         show : function(options)
4934         {
4935             
4936             // this causes nightmares if you show one dialog after another
4937             // especially on callbacks..
4938              
4939             if(this.isVisible()){
4940                 
4941                 this.hide();
4942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4944                 Roo.log("New Dialog Message:" +  options.msg )
4945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4947                 
4948             }
4949             var d = this.getDialog();
4950             opt = options;
4951             d.setTitle(opt.title || "&#160;");
4952             d.closeEl.setDisplayed(opt.closable !== false);
4953             activeTextEl = textboxEl;
4954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4955             if(opt.prompt){
4956                 if(opt.multiline){
4957                     textboxEl.hide();
4958                     textareaEl.show();
4959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4960                         opt.multiline : this.defaultTextHeight);
4961                     activeTextEl = textareaEl;
4962                 }else{
4963                     textboxEl.show();
4964                     textareaEl.hide();
4965                 }
4966             }else{
4967                 textboxEl.hide();
4968                 textareaEl.hide();
4969             }
4970             progressEl.setDisplayed(opt.progress === true);
4971             if (opt.progress) {
4972                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4973             }
4974             this.updateProgress(0);
4975             activeTextEl.dom.value = opt.value || "";
4976             if(opt.prompt){
4977                 dlg.setDefaultButton(activeTextEl);
4978             }else{
4979                 var bs = opt.buttons;
4980                 var db = null;
4981                 if(bs && bs.ok){
4982                     db = buttons["ok"];
4983                 }else if(bs && bs.yes){
4984                     db = buttons["yes"];
4985                 }
4986                 dlg.setDefaultButton(db);
4987             }
4988             bwidth = updateButtons(opt.buttons);
4989             this.updateText(opt.msg);
4990             if(opt.cls){
4991                 d.el.addClass(opt.cls);
4992             }
4993             d.proxyDrag = opt.proxyDrag === true;
4994             d.modal = opt.modal !== false;
4995             d.mask = opt.modal !== false ? mask : false;
4996             if(!d.isVisible()){
4997                 // force it to the end of the z-index stack so it gets a cursor in FF
4998                 document.body.appendChild(dlg.el.dom);
4999                 d.animateTarget = null;
5000                 d.show(options.animEl);
5001             }
5002             return this;
5003         },
5004
5005         /**
5006          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5007          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5008          * and closing the message box when the process is complete.
5009          * @param {String} title The title bar text
5010          * @param {String} msg The message box body text
5011          * @return {Roo.MessageBox} This message box
5012          */
5013         progress : function(title, msg){
5014             this.show({
5015                 title : title,
5016                 msg : msg,
5017                 buttons: false,
5018                 progress:true,
5019                 closable:false,
5020                 minWidth: this.minProgressWidth,
5021                 modal : true
5022             });
5023             return this;
5024         },
5025
5026         /**
5027          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5028          * If a callback function is passed it will be called after the user clicks the button, and the
5029          * id of the button that was clicked will be passed as the only parameter to the callback
5030          * (could also be the top-right close button).
5031          * @param {String} title The title bar text
5032          * @param {String} msg The message box body text
5033          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5034          * @param {Object} scope (optional) The scope of the callback function
5035          * @return {Roo.MessageBox} This message box
5036          */
5037         alert : function(title, msg, fn, scope)
5038         {
5039             this.show({
5040                 title : title,
5041                 msg : msg,
5042                 buttons: this.OK,
5043                 fn: fn,
5044                 closable : false,
5045                 scope : scope,
5046                 modal : true
5047             });
5048             return this;
5049         },
5050
5051         /**
5052          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5053          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5054          * You are responsible for closing the message box when the process is complete.
5055          * @param {String} msg The message box body text
5056          * @param {String} title (optional) The title bar text
5057          * @return {Roo.MessageBox} This message box
5058          */
5059         wait : function(msg, title){
5060             this.show({
5061                 title : title,
5062                 msg : msg,
5063                 buttons: false,
5064                 closable:false,
5065                 progress:true,
5066                 modal:true,
5067                 width:300,
5068                 wait:true
5069             });
5070             waitTimer = Roo.TaskMgr.start({
5071                 run: function(i){
5072                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5073                 },
5074                 interval: 1000
5075             });
5076             return this;
5077         },
5078
5079         /**
5080          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5081          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5082          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5083          * @param {String} title The title bar text
5084          * @param {String} msg The message box body text
5085          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5086          * @param {Object} scope (optional) The scope of the callback function
5087          * @return {Roo.MessageBox} This message box
5088          */
5089         confirm : function(title, msg, fn, scope){
5090             this.show({
5091                 title : title,
5092                 msg : msg,
5093                 buttons: this.YESNO,
5094                 fn: fn,
5095                 scope : scope,
5096                 modal : true
5097             });
5098             return this;
5099         },
5100
5101         /**
5102          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5103          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5104          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5105          * (could also be the top-right close button) and the text that was entered will be passed as the two
5106          * parameters to the callback.
5107          * @param {String} title The title bar text
5108          * @param {String} msg The message box body text
5109          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110          * @param {Object} scope (optional) The scope of the callback function
5111          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5112          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5113          * @return {Roo.MessageBox} This message box
5114          */
5115         prompt : function(title, msg, fn, scope, multiline){
5116             this.show({
5117                 title : title,
5118                 msg : msg,
5119                 buttons: this.OKCANCEL,
5120                 fn: fn,
5121                 minWidth:250,
5122                 scope : scope,
5123                 prompt:true,
5124                 multiline: multiline,
5125                 modal : true
5126             });
5127             return this;
5128         },
5129
5130         /**
5131          * Button config that displays a single OK button
5132          * @type Object
5133          */
5134         OK : {ok:true},
5135         /**
5136          * Button config that displays Yes and No buttons
5137          * @type Object
5138          */
5139         YESNO : {yes:true, no:true},
5140         /**
5141          * Button config that displays OK and Cancel buttons
5142          * @type Object
5143          */
5144         OKCANCEL : {ok:true, cancel:true},
5145         /**
5146          * Button config that displays Yes, No and Cancel buttons
5147          * @type Object
5148          */
5149         YESNOCANCEL : {yes:true, no:true, cancel:true},
5150
5151         /**
5152          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5153          * @type Number
5154          */
5155         defaultTextHeight : 75,
5156         /**
5157          * The maximum width in pixels of the message box (defaults to 600)
5158          * @type Number
5159          */
5160         maxWidth : 600,
5161         /**
5162          * The minimum width in pixels of the message box (defaults to 100)
5163          * @type Number
5164          */
5165         minWidth : 100,
5166         /**
5167          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5168          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5169          * @type Number
5170          */
5171         minProgressWidth : 250,
5172         /**
5173          * An object containing the default button text strings that can be overriden for localized language support.
5174          * Supported properties are: ok, cancel, yes and no.
5175          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5176          * @type Object
5177          */
5178         buttonText : {
5179             ok : "OK",
5180             cancel : "Cancel",
5181             yes : "Yes",
5182             no : "No"
5183         }
5184     };
5185 }();
5186
5187 /**
5188  * Shorthand for {@link Roo.MessageBox}
5189  */
5190 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5191 Roo.Msg = Roo.Msg || Roo.MessageBox;
5192 /*
5193  * - LGPL
5194  *
5195  * navbar
5196  * 
5197  */
5198
5199 /**
5200  * @class Roo.bootstrap.Navbar
5201  * @extends Roo.bootstrap.Component
5202  * Bootstrap Navbar class
5203
5204  * @constructor
5205  * Create a new Navbar
5206  * @param {Object} config The config object
5207  */
5208
5209
5210 Roo.bootstrap.Navbar = function(config){
5211     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5212     this.addEvents({
5213         // raw events
5214         /**
5215          * @event beforetoggle
5216          * Fire before toggle the menu
5217          * @param {Roo.EventObject} e
5218          */
5219         "beforetoggle" : true
5220     });
5221 };
5222
5223 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5224     
5225     
5226    
5227     // private
5228     navItems : false,
5229     loadMask : false,
5230     
5231     
5232     getAutoCreate : function(){
5233         
5234         
5235         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5236         
5237     },
5238     
5239     initEvents :function ()
5240     {
5241         //Roo.log(this.el.select('.navbar-toggle',true));
5242         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5243         
5244         var mark = {
5245             tag: "div",
5246             cls:"x-dlg-mask"
5247         };
5248         
5249         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5250         
5251         var size = this.el.getSize();
5252         this.maskEl.setSize(size.width, size.height);
5253         this.maskEl.enableDisplayMode("block");
5254         this.maskEl.hide();
5255         
5256         if(this.loadMask){
5257             this.maskEl.show();
5258         }
5259     },
5260     
5261     
5262     getChildContainer : function()
5263     {
5264         if (this.el && this.el.select('.collapse').getCount()) {
5265             return this.el.select('.collapse',true).first();
5266         }
5267         
5268         return this.el;
5269     },
5270     
5271     mask : function()
5272     {
5273         this.maskEl.show();
5274     },
5275     
5276     unmask : function()
5277     {
5278         this.maskEl.hide();
5279     },
5280     onToggle : function()
5281     {
5282         
5283         if(this.fireEvent('beforetoggle', this) === false){
5284             return;
5285         }
5286         var ce = this.el.select('.navbar-collapse',true).first();
5287       
5288         if (!ce.hasClass('show')) {
5289            this.expand();
5290         } else {
5291             this.collapse();
5292         }
5293         
5294         
5295     
5296     },
5297     /**
5298      * Expand the navbar pulldown 
5299      */
5300     expand : function ()
5301     {
5302        
5303         var ce = this.el.select('.navbar-collapse',true).first();
5304         if (ce.hasClass('collapsing')) {
5305             return;
5306         }
5307         ce.dom.style.height = '';
5308                // show it...
5309         ce.addClass('in'); // old...
5310         ce.removeClass('collapse');
5311         ce.addClass('show');
5312         var h = ce.getHeight();
5313         Roo.log(h);
5314         ce.removeClass('show');
5315         // at this point we should be able to see it..
5316         ce.addClass('collapsing');
5317         
5318         ce.setHeight(0); // resize it ...
5319         ce.on('transitionend', function() {
5320             //Roo.log('done transition');
5321             ce.removeClass('collapsing');
5322             ce.addClass('show');
5323             ce.removeClass('collapse');
5324
5325             ce.dom.style.height = '';
5326         }, this, { single: true} );
5327         ce.setHeight(h);
5328         ce.dom.scrollTop = 0;
5329     },
5330     /**
5331      * Collapse the navbar pulldown 
5332      */
5333     collapse : function()
5334     {
5335          var ce = this.el.select('.navbar-collapse',true).first();
5336        
5337         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5338             // it's collapsed or collapsing..
5339             return;
5340         }
5341         ce.removeClass('in'); // old...
5342         ce.setHeight(ce.getHeight());
5343         ce.removeClass('show');
5344         ce.addClass('collapsing');
5345         
5346         ce.on('transitionend', function() {
5347             ce.dom.style.height = '';
5348             ce.removeClass('collapsing');
5349             ce.addClass('collapse');
5350         }, this, { single: true} );
5351         ce.setHeight(0);
5352     }
5353     
5354     
5355     
5356 });
5357
5358
5359
5360  
5361
5362  /*
5363  * - LGPL
5364  *
5365  * navbar
5366  * 
5367  */
5368
5369 /**
5370  * @class Roo.bootstrap.NavSimplebar
5371  * @extends Roo.bootstrap.Navbar
5372  * Bootstrap Sidebar class
5373  *
5374  * @cfg {Boolean} inverse is inverted color
5375  * 
5376  * @cfg {String} type (nav | pills | tabs)
5377  * @cfg {Boolean} arrangement stacked | justified
5378  * @cfg {String} align (left | right) alignment
5379  * 
5380  * @cfg {Boolean} main (true|false) main nav bar? default false
5381  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5382  * 
5383  * @cfg {String} tag (header|footer|nav|div) default is nav 
5384
5385  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5386  * 
5387  * 
5388  * @constructor
5389  * Create a new Sidebar
5390  * @param {Object} config The config object
5391  */
5392
5393
5394 Roo.bootstrap.NavSimplebar = function(config){
5395     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5396 };
5397
5398 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5399     
5400     inverse: false,
5401     
5402     type: false,
5403     arrangement: '',
5404     align : false,
5405     
5406     weight : 'light',
5407     
5408     main : false,
5409     
5410     
5411     tag : false,
5412     
5413     
5414     getAutoCreate : function(){
5415         
5416         
5417         var cfg = {
5418             tag : this.tag || 'div',
5419             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5420         };
5421         if (['light','white'].indexOf(this.weight) > -1) {
5422             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5423         }
5424         cfg.cls += ' bg-' + this.weight;
5425         
5426         if (this.inverse) {
5427             cfg.cls += ' navbar-inverse';
5428             
5429         }
5430         
5431         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5432         
5433         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5434             return cfg;
5435         }
5436         
5437         
5438     
5439         
5440         cfg.cn = [
5441             {
5442                 cls: 'nav nav-' + this.xtype,
5443                 tag : 'ul'
5444             }
5445         ];
5446         
5447          
5448         this.type = this.type || 'nav';
5449         if (['tabs','pills'].indexOf(this.type) != -1) {
5450             cfg.cn[0].cls += ' nav-' + this.type
5451         
5452         
5453         } else {
5454             if (this.type!=='nav') {
5455                 Roo.log('nav type must be nav/tabs/pills')
5456             }
5457             cfg.cn[0].cls += ' navbar-nav'
5458         }
5459         
5460         
5461         
5462         
5463         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5464             cfg.cn[0].cls += ' nav-' + this.arrangement;
5465         }
5466         
5467         
5468         if (this.align === 'right') {
5469             cfg.cn[0].cls += ' navbar-right';
5470         }
5471         
5472         
5473         
5474         
5475         return cfg;
5476     
5477         
5478     }
5479     
5480     
5481     
5482 });
5483
5484
5485
5486  
5487
5488  
5489        /*
5490  * - LGPL
5491  *
5492  * navbar
5493  * navbar-fixed-top
5494  * navbar-expand-md  fixed-top 
5495  */
5496
5497 /**
5498  * @class Roo.bootstrap.NavHeaderbar
5499  * @extends Roo.bootstrap.NavSimplebar
5500  * Bootstrap Sidebar class
5501  *
5502  * @cfg {String} brand what is brand
5503  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5504  * @cfg {String} brand_href href of the brand
5505  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5506  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5507  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5508  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5509  * 
5510  * @constructor
5511  * Create a new Sidebar
5512  * @param {Object} config The config object
5513  */
5514
5515
5516 Roo.bootstrap.NavHeaderbar = function(config){
5517     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5518       
5519 };
5520
5521 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5522     
5523     position: '',
5524     brand: '',
5525     brand_href: false,
5526     srButton : true,
5527     autohide : false,
5528     desktopCenter : false,
5529    
5530     
5531     getAutoCreate : function(){
5532         
5533         var   cfg = {
5534             tag: this.nav || 'nav',
5535             cls: 'navbar navbar-expand-md',
5536             role: 'navigation',
5537             cn: []
5538         };
5539         
5540         var cn = cfg.cn;
5541         if (this.desktopCenter) {
5542             cn.push({cls : 'container', cn : []});
5543             cn = cn[0].cn;
5544         }
5545         
5546         if(this.srButton){
5547             var btn = {
5548                 tag: 'button',
5549                 type: 'button',
5550                 cls: 'navbar-toggle navbar-toggler',
5551                 'data-toggle': 'collapse',
5552                 cn: [
5553                     {
5554                         tag: 'span',
5555                         cls: 'sr-only',
5556                         html: 'Toggle navigation'
5557                     },
5558                     {
5559                         tag: 'span',
5560                         cls: 'icon-bar navbar-toggler-icon'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     }
5570                 ]
5571             };
5572             
5573             cn.push( Roo.bootstrap.version == 4 ? btn : {
5574                 tag: 'div',
5575                 cls: 'navbar-header',
5576                 cn: [
5577                     btn
5578                 ]
5579             });
5580         }
5581         
5582         cn.push({
5583             tag: 'div',
5584             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5585             cn : []
5586         });
5587         
5588         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5589         
5590         if (['light','white'].indexOf(this.weight) > -1) {
5591             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5592         }
5593         cfg.cls += ' bg-' + this.weight;
5594         
5595         
5596         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5597             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5598             
5599             // tag can override this..
5600             
5601             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5602         }
5603         
5604         if (this.brand !== '') {
5605             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5606             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5607                 tag: 'a',
5608                 href: this.brand_href ? this.brand_href : '#',
5609                 cls: 'navbar-brand',
5610                 cn: [
5611                 this.brand
5612                 ]
5613             });
5614         }
5615         
5616         if(this.main){
5617             cfg.cls += ' main-nav';
5618         }
5619         
5620         
5621         return cfg;
5622
5623         
5624     },
5625     getHeaderChildContainer : function()
5626     {
5627         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5628             return this.el.select('.navbar-header',true).first();
5629         }
5630         
5631         return this.getChildContainer();
5632     },
5633     
5634     getChildContainer : function()
5635     {
5636          
5637         return this.el.select('.roo-navbar-collapse',true).first();
5638          
5639         
5640     },
5641     
5642     initEvents : function()
5643     {
5644         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5645         
5646         if (this.autohide) {
5647             
5648             var prevScroll = 0;
5649             var ft = this.el;
5650             
5651             Roo.get(document).on('scroll',function(e) {
5652                 var ns = Roo.get(document).getScroll().top;
5653                 var os = prevScroll;
5654                 prevScroll = ns;
5655                 
5656                 if(ns > os){
5657                     ft.removeClass('slideDown');
5658                     ft.addClass('slideUp');
5659                     return;
5660                 }
5661                 ft.removeClass('slideUp');
5662                 ft.addClass('slideDown');
5663                  
5664               
5665           },this);
5666         }
5667     }    
5668     
5669 });
5670
5671
5672
5673  
5674
5675  /*
5676  * - LGPL
5677  *
5678  * navbar
5679  * 
5680  */
5681
5682 /**
5683  * @class Roo.bootstrap.NavSidebar
5684  * @extends Roo.bootstrap.Navbar
5685  * Bootstrap Sidebar class
5686  * 
5687  * @constructor
5688  * Create a new Sidebar
5689  * @param {Object} config The config object
5690  */
5691
5692
5693 Roo.bootstrap.NavSidebar = function(config){
5694     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5695 };
5696
5697 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5698     
5699     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5700     
5701     getAutoCreate : function(){
5702         
5703         
5704         return  {
5705             tag: 'div',
5706             cls: 'sidebar sidebar-nav'
5707         };
5708     
5709         
5710     }
5711     
5712     
5713     
5714 });
5715
5716
5717
5718  
5719
5720  /*
5721  * - LGPL
5722  *
5723  * nav group
5724  * 
5725  */
5726
5727 /**
5728  * @class Roo.bootstrap.NavGroup
5729  * @extends Roo.bootstrap.Component
5730  * Bootstrap NavGroup class
5731  * @cfg {String} align (left|right)
5732  * @cfg {Boolean} inverse
5733  * @cfg {String} type (nav|pills|tab) default nav
5734  * @cfg {String} navId - reference Id for navbar.
5735
5736  * 
5737  * @constructor
5738  * Create a new nav group
5739  * @param {Object} config The config object
5740  */
5741
5742 Roo.bootstrap.NavGroup = function(config){
5743     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5744     this.navItems = [];
5745    
5746     Roo.bootstrap.NavGroup.register(this);
5747      this.addEvents({
5748         /**
5749              * @event changed
5750              * Fires when the active item changes
5751              * @param {Roo.bootstrap.NavGroup} this
5752              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5753              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5754          */
5755         'changed': true
5756      });
5757     
5758 };
5759
5760 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5761     
5762     align: '',
5763     inverse: false,
5764     form: false,
5765     type: 'nav',
5766     navId : '',
5767     // private
5768     
5769     navItems : false, 
5770     
5771     getAutoCreate : function()
5772     {
5773         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5774         
5775         cfg = {
5776             tag : 'ul',
5777             cls: 'nav' 
5778         };
5779         if (Roo.bootstrap.version == 4) {
5780             if (['tabs','pills'].indexOf(this.type) != -1) {
5781                 cfg.cls += ' nav-' + this.type; 
5782             } else {
5783                 // trying to remove so header bar can right align top?
5784                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5785                     // do not use on header bar... 
5786                     cfg.cls += ' navbar-nav';
5787                 }
5788             }
5789             
5790         } else {
5791             if (['tabs','pills'].indexOf(this.type) != -1) {
5792                 cfg.cls += ' nav-' + this.type
5793             } else {
5794                 if (this.type !== 'nav') {
5795                     Roo.log('nav type must be nav/tabs/pills')
5796                 }
5797                 cfg.cls += ' navbar-nav'
5798             }
5799         }
5800         
5801         if (this.parent() && this.parent().sidebar) {
5802             cfg = {
5803                 tag: 'ul',
5804                 cls: 'dashboard-menu sidebar-menu'
5805             };
5806             
5807             return cfg;
5808         }
5809         
5810         if (this.form === true) {
5811             cfg = {
5812                 tag: 'form',
5813                 cls: 'navbar-form form-inline'
5814             };
5815             //nav navbar-right ml-md-auto
5816             if (this.align === 'right') {
5817                 cfg.cls += ' navbar-right ml-md-auto';
5818             } else {
5819                 cfg.cls += ' navbar-left';
5820             }
5821         }
5822         
5823         if (this.align === 'right') {
5824             cfg.cls += ' navbar-right ml-md-auto';
5825         } else {
5826             cfg.cls += ' mr-auto';
5827         }
5828         
5829         if (this.inverse) {
5830             cfg.cls += ' navbar-inverse';
5831             
5832         }
5833         
5834         
5835         return cfg;
5836     },
5837     /**
5838     * sets the active Navigation item
5839     * @param {Roo.bootstrap.NavItem} the new current navitem
5840     */
5841     setActiveItem : function(item)
5842     {
5843         var prev = false;
5844         Roo.each(this.navItems, function(v){
5845             if (v == item) {
5846                 return ;
5847             }
5848             if (v.isActive()) {
5849                 v.setActive(false, true);
5850                 prev = v;
5851                 
5852             }
5853             
5854         });
5855
5856         item.setActive(true, true);
5857         this.fireEvent('changed', this, item, prev);
5858         
5859         
5860     },
5861     /**
5862     * gets the active Navigation item
5863     * @return {Roo.bootstrap.NavItem} the current navitem
5864     */
5865     getActive : function()
5866     {
5867         
5868         var prev = false;
5869         Roo.each(this.navItems, function(v){
5870             
5871             if (v.isActive()) {
5872                 prev = v;
5873                 
5874             }
5875             
5876         });
5877         return prev;
5878     },
5879     
5880     indexOfNav : function()
5881     {
5882         
5883         var prev = false;
5884         Roo.each(this.navItems, function(v,i){
5885             
5886             if (v.isActive()) {
5887                 prev = i;
5888                 
5889             }
5890             
5891         });
5892         return prev;
5893     },
5894     /**
5895     * adds a Navigation item
5896     * @param {Roo.bootstrap.NavItem} the navitem to add
5897     */
5898     addItem : function(cfg)
5899     {
5900         if (this.form && Roo.bootstrap.version == 4) {
5901             cfg.tag = 'div';
5902         }
5903         var cn = new Roo.bootstrap.NavItem(cfg);
5904         this.register(cn);
5905         cn.parentId = this.id;
5906         cn.onRender(this.el, null);
5907         return cn;
5908     },
5909     /**
5910     * register a Navigation item
5911     * @param {Roo.bootstrap.NavItem} the navitem to add
5912     */
5913     register : function(item)
5914     {
5915         this.navItems.push( item);
5916         item.navId = this.navId;
5917     
5918     },
5919     
5920     /**
5921     * clear all the Navigation item
5922     */
5923    
5924     clearAll : function()
5925     {
5926         this.navItems = [];
5927         this.el.dom.innerHTML = '';
5928     },
5929     
5930     getNavItem: function(tabId)
5931     {
5932         var ret = false;
5933         Roo.each(this.navItems, function(e) {
5934             if (e.tabId == tabId) {
5935                ret =  e;
5936                return false;
5937             }
5938             return true;
5939             
5940         });
5941         return ret;
5942     },
5943     
5944     setActiveNext : function()
5945     {
5946         var i = this.indexOfNav(this.getActive());
5947         if (i > this.navItems.length) {
5948             return;
5949         }
5950         this.setActiveItem(this.navItems[i+1]);
5951     },
5952     setActivePrev : function()
5953     {
5954         var i = this.indexOfNav(this.getActive());
5955         if (i  < 1) {
5956             return;
5957         }
5958         this.setActiveItem(this.navItems[i-1]);
5959     },
5960     clearWasActive : function(except) {
5961         Roo.each(this.navItems, function(e) {
5962             if (e.tabId != except.tabId && e.was_active) {
5963                e.was_active = false;
5964                return false;
5965             }
5966             return true;
5967             
5968         });
5969     },
5970     getWasActive : function ()
5971     {
5972         var r = false;
5973         Roo.each(this.navItems, function(e) {
5974             if (e.was_active) {
5975                r = e;
5976                return false;
5977             }
5978             return true;
5979             
5980         });
5981         return r;
5982     }
5983     
5984     
5985 });
5986
5987  
5988 Roo.apply(Roo.bootstrap.NavGroup, {
5989     
5990     groups: {},
5991      /**
5992     * register a Navigation Group
5993     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5994     */
5995     register : function(navgrp)
5996     {
5997         this.groups[navgrp.navId] = navgrp;
5998         
5999     },
6000     /**
6001     * fetch a Navigation Group based on the navigation ID
6002     * @param {string} the navgroup to add
6003     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6004     */
6005     get: function(navId) {
6006         if (typeof(this.groups[navId]) == 'undefined') {
6007             return false;
6008             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6009         }
6010         return this.groups[navId] ;
6011     }
6012     
6013     
6014     
6015 });
6016
6017  /*
6018  * - LGPL
6019  *
6020  * row
6021  * 
6022  */
6023
6024 /**
6025  * @class Roo.bootstrap.NavItem
6026  * @extends Roo.bootstrap.Component
6027  * Bootstrap Navbar.NavItem class
6028  * @cfg {String} href  link to
6029  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6030
6031  * @cfg {String} html content of button
6032  * @cfg {String} badge text inside badge
6033  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6034  * @cfg {String} glyphicon DEPRICATED - use fa
6035  * @cfg {String} icon DEPRICATED - use fa
6036  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6037  * @cfg {Boolean} active Is item active
6038  * @cfg {Boolean} disabled Is item disabled
6039  
6040  * @cfg {Boolean} preventDefault (true | false) default false
6041  * @cfg {String} tabId the tab that this item activates.
6042  * @cfg {String} tagtype (a|span) render as a href or span?
6043  * @cfg {Boolean} animateRef (true|false) link to element default false  
6044   
6045  * @constructor
6046  * Create a new Navbar Item
6047  * @param {Object} config The config object
6048  */
6049 Roo.bootstrap.NavItem = function(config){
6050     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6051     this.addEvents({
6052         // raw events
6053         /**
6054          * @event click
6055          * The raw click event for the entire grid.
6056          * @param {Roo.EventObject} e
6057          */
6058         "click" : true,
6059          /**
6060             * @event changed
6061             * Fires when the active item active state changes
6062             * @param {Roo.bootstrap.NavItem} this
6063             * @param {boolean} state the new state
6064              
6065          */
6066         'changed': true,
6067         /**
6068             * @event scrollto
6069             * Fires when scroll to element
6070             * @param {Roo.bootstrap.NavItem} this
6071             * @param {Object} options
6072             * @param {Roo.EventObject} e
6073              
6074          */
6075         'scrollto': true
6076     });
6077    
6078 };
6079
6080 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6081     
6082     href: false,
6083     html: '',
6084     badge: '',
6085     icon: false,
6086     fa : false,
6087     glyphicon: false,
6088     active: false,
6089     preventDefault : false,
6090     tabId : false,
6091     tagtype : 'a',
6092     tag: 'li',
6093     disabled : false,
6094     animateRef : false,
6095     was_active : false,
6096     button_weight : '',
6097     button_outline : false,
6098     
6099     navLink: false,
6100     
6101     getAutoCreate : function(){
6102          
6103         var cfg = {
6104             tag: this.tag,
6105             cls: 'nav-item'
6106         };
6107         
6108         if (this.active) {
6109             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6110         }
6111         if (this.disabled) {
6112             cfg.cls += ' disabled';
6113         }
6114         
6115         // BS4 only?
6116         if (this.button_weight.length) {
6117             cfg.tag = this.href ? 'a' : 'button';
6118             cfg.html = this.html || '';
6119             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6120             if (this.href) {
6121                 cfg.href = this.href;
6122             }
6123             if (this.fa) {
6124                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6125             }
6126             
6127             // menu .. should add dropdown-menu class - so no need for carat..
6128             
6129             if (this.badge !== '') {
6130                  
6131                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6132             }
6133             return cfg;
6134         }
6135         
6136         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6137             cfg.cn = [
6138                 {
6139                     tag: this.tagtype,
6140                     href : this.href || "#",
6141                     html: this.html || ''
6142                 }
6143             ];
6144             if (this.tagtype == 'a') {
6145                 cfg.cn[0].cls = 'nav-link';
6146             }
6147             if (this.icon) {
6148                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6149             }
6150             if (this.fa) {
6151                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6152             }
6153             if(this.glyphicon) {
6154                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6155             }
6156             
6157             if (this.menu) {
6158                 
6159                 cfg.cn[0].html += " <span class='caret'></span>";
6160              
6161             }
6162             
6163             if (this.badge !== '') {
6164                  
6165                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6166             }
6167         }
6168         
6169         
6170         
6171         return cfg;
6172     },
6173     onRender : function(ct, position)
6174     {
6175        // Roo.log("Call onRender: " + this.xtype);
6176         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6177             this.tag = 'div';
6178         }
6179         
6180         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6181         this.navLink = this.el.select('.nav-link',true).first();
6182         return ret;
6183     },
6184       
6185     
6186     initEvents: function() 
6187     {
6188         if (typeof (this.menu) != 'undefined') {
6189             this.menu.parentType = this.xtype;
6190             this.menu.triggerEl = this.el;
6191             this.menu = this.addxtype(Roo.apply({}, this.menu));
6192         }
6193         
6194         this.el.select('a',true).on('click', this.onClick, this);
6195         
6196         if(this.tagtype == 'span'){
6197             this.el.select('span',true).on('click', this.onClick, this);
6198         }
6199        
6200         // at this point parent should be available..
6201         this.parent().register(this);
6202     },
6203     
6204     onClick : function(e)
6205     {
6206         if (e.getTarget('.dropdown-menu-item')) {
6207             // did you click on a menu itemm.... - then don't trigger onclick..
6208             return;
6209         }
6210         
6211         if(
6212                 this.preventDefault || 
6213                 this.href == '#' 
6214         ){
6215             Roo.log("NavItem - prevent Default?");
6216             e.preventDefault();
6217         }
6218         
6219         if (this.disabled) {
6220             return;
6221         }
6222         
6223         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6224         if (tg && tg.transition) {
6225             Roo.log("waiting for the transitionend");
6226             return;
6227         }
6228         
6229         
6230         
6231         //Roo.log("fire event clicked");
6232         if(this.fireEvent('click', this, e) === false){
6233             return;
6234         };
6235         
6236         if(this.tagtype == 'span'){
6237             return;
6238         }
6239         
6240         //Roo.log(this.href);
6241         var ael = this.el.select('a',true).first();
6242         //Roo.log(ael);
6243         
6244         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6245             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6246             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6247                 return; // ignore... - it's a 'hash' to another page.
6248             }
6249             Roo.log("NavItem - prevent Default?");
6250             e.preventDefault();
6251             this.scrollToElement(e);
6252         }
6253         
6254         
6255         var p =  this.parent();
6256    
6257         if (['tabs','pills'].indexOf(p.type)!==-1) {
6258             if (typeof(p.setActiveItem) !== 'undefined') {
6259                 p.setActiveItem(this);
6260             }
6261         }
6262         
6263         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6264         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6265             // remove the collapsed menu expand...
6266             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6267         }
6268     },
6269     
6270     isActive: function () {
6271         return this.active
6272     },
6273     setActive : function(state, fire, is_was_active)
6274     {
6275         if (this.active && !state && this.navId) {
6276             this.was_active = true;
6277             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6278             if (nv) {
6279                 nv.clearWasActive(this);
6280             }
6281             
6282         }
6283         this.active = state;
6284         
6285         if (!state ) {
6286             this.el.removeClass('active');
6287             this.navLink ? this.navLink.removeClass('active') : false;
6288         } else if (!this.el.hasClass('active')) {
6289             
6290             this.el.addClass('active');
6291             if (Roo.bootstrap.version == 4 && this.navLink ) {
6292                 this.navLink.addClass('active');
6293             }
6294             
6295         }
6296         if (fire) {
6297             this.fireEvent('changed', this, state);
6298         }
6299         
6300         // show a panel if it's registered and related..
6301         
6302         if (!this.navId || !this.tabId || !state || is_was_active) {
6303             return;
6304         }
6305         
6306         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6307         if (!tg) {
6308             return;
6309         }
6310         var pan = tg.getPanelByName(this.tabId);
6311         if (!pan) {
6312             return;
6313         }
6314         // if we can not flip to new panel - go back to old nav highlight..
6315         if (false == tg.showPanel(pan)) {
6316             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6317             if (nv) {
6318                 var onav = nv.getWasActive();
6319                 if (onav) {
6320                     onav.setActive(true, false, true);
6321                 }
6322             }
6323             
6324         }
6325         
6326         
6327         
6328     },
6329      // this should not be here...
6330     setDisabled : function(state)
6331     {
6332         this.disabled = state;
6333         if (!state ) {
6334             this.el.removeClass('disabled');
6335         } else if (!this.el.hasClass('disabled')) {
6336             this.el.addClass('disabled');
6337         }
6338         
6339     },
6340     
6341     /**
6342      * Fetch the element to display the tooltip on.
6343      * @return {Roo.Element} defaults to this.el
6344      */
6345     tooltipEl : function()
6346     {
6347         return this.el.select('' + this.tagtype + '', true).first();
6348     },
6349     
6350     scrollToElement : function(e)
6351     {
6352         var c = document.body;
6353         
6354         /*
6355          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6356          */
6357         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6358             c = document.documentElement;
6359         }
6360         
6361         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6362         
6363         if(!target){
6364             return;
6365         }
6366
6367         var o = target.calcOffsetsTo(c);
6368         
6369         var options = {
6370             target : target,
6371             value : o[1]
6372         };
6373         
6374         this.fireEvent('scrollto', this, options, e);
6375         
6376         Roo.get(c).scrollTo('top', options.value, true);
6377         
6378         return;
6379     }
6380 });
6381  
6382
6383  /*
6384  * - LGPL
6385  *
6386  * sidebar item
6387  *
6388  *  li
6389  *    <span> icon </span>
6390  *    <span> text </span>
6391  *    <span>badge </span>
6392  */
6393
6394 /**
6395  * @class Roo.bootstrap.NavSidebarItem
6396  * @extends Roo.bootstrap.NavItem
6397  * Bootstrap Navbar.NavSidebarItem class
6398  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6399  * {Boolean} open is the menu open
6400  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6401  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6402  * {String} buttonSize (sm|md|lg)the extra classes for the button
6403  * {Boolean} showArrow show arrow next to the text (default true)
6404  * @constructor
6405  * Create a new Navbar Button
6406  * @param {Object} config The config object
6407  */
6408 Roo.bootstrap.NavSidebarItem = function(config){
6409     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6410     this.addEvents({
6411         // raw events
6412         /**
6413          * @event click
6414          * The raw click event for the entire grid.
6415          * @param {Roo.EventObject} e
6416          */
6417         "click" : true,
6418          /**
6419             * @event changed
6420             * Fires when the active item active state changes
6421             * @param {Roo.bootstrap.NavSidebarItem} this
6422             * @param {boolean} state the new state
6423              
6424          */
6425         'changed': true
6426     });
6427    
6428 };
6429
6430 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6431     
6432     badgeWeight : 'default',
6433     
6434     open: false,
6435     
6436     buttonView : false,
6437     
6438     buttonWeight : 'default',
6439     
6440     buttonSize : 'md',
6441     
6442     showArrow : true,
6443     
6444     getAutoCreate : function(){
6445         
6446         
6447         var a = {
6448                 tag: 'a',
6449                 href : this.href || '#',
6450                 cls: '',
6451                 html : '',
6452                 cn : []
6453         };
6454         
6455         if(this.buttonView){
6456             a = {
6457                 tag: 'button',
6458                 href : this.href || '#',
6459                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6460                 html : this.html,
6461                 cn : []
6462             };
6463         }
6464         
6465         var cfg = {
6466             tag: 'li',
6467             cls: '',
6468             cn: [ a ]
6469         };
6470         
6471         if (this.active) {
6472             cfg.cls += ' active';
6473         }
6474         
6475         if (this.disabled) {
6476             cfg.cls += ' disabled';
6477         }
6478         if (this.open) {
6479             cfg.cls += ' open x-open';
6480         }
6481         // left icon..
6482         if (this.glyphicon || this.icon) {
6483             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6484             a.cn.push({ tag : 'i', cls : c }) ;
6485         }
6486         
6487         if(!this.buttonView){
6488             var span = {
6489                 tag: 'span',
6490                 html : this.html || ''
6491             };
6492
6493             a.cn.push(span);
6494             
6495         }
6496         
6497         if (this.badge !== '') {
6498             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6499         }
6500         
6501         if (this.menu) {
6502             
6503             if(this.showArrow){
6504                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6505             }
6506             
6507             a.cls += ' dropdown-toggle treeview' ;
6508         }
6509         
6510         return cfg;
6511     },
6512     
6513     initEvents : function()
6514     { 
6515         if (typeof (this.menu) != 'undefined') {
6516             this.menu.parentType = this.xtype;
6517             this.menu.triggerEl = this.el;
6518             this.menu = this.addxtype(Roo.apply({}, this.menu));
6519         }
6520         
6521         this.el.on('click', this.onClick, this);
6522         
6523         if(this.badge !== ''){
6524             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6525         }
6526         
6527     },
6528     
6529     onClick : function(e)
6530     {
6531         if(this.disabled){
6532             e.preventDefault();
6533             return;
6534         }
6535         
6536         if(this.preventDefault){
6537             e.preventDefault();
6538         }
6539         
6540         this.fireEvent('click', this, e);
6541     },
6542     
6543     disable : function()
6544     {
6545         this.setDisabled(true);
6546     },
6547     
6548     enable : function()
6549     {
6550         this.setDisabled(false);
6551     },
6552     
6553     setDisabled : function(state)
6554     {
6555         if(this.disabled == state){
6556             return;
6557         }
6558         
6559         this.disabled = state;
6560         
6561         if (state) {
6562             this.el.addClass('disabled');
6563             return;
6564         }
6565         
6566         this.el.removeClass('disabled');
6567         
6568         return;
6569     },
6570     
6571     setActive : function(state)
6572     {
6573         if(this.active == state){
6574             return;
6575         }
6576         
6577         this.active = state;
6578         
6579         if (state) {
6580             this.el.addClass('active');
6581             return;
6582         }
6583         
6584         this.el.removeClass('active');
6585         
6586         return;
6587     },
6588     
6589     isActive: function () 
6590     {
6591         return this.active;
6592     },
6593     
6594     setBadge : function(str)
6595     {
6596         if(!this.badgeEl){
6597             return;
6598         }
6599         
6600         this.badgeEl.dom.innerHTML = str;
6601     }
6602     
6603    
6604      
6605  
6606 });
6607  
6608
6609  /*
6610  * - LGPL
6611  *
6612  *  Breadcrumb Nav
6613  * 
6614  */
6615 Roo.namespace('Roo.bootstrap.breadcrumb');
6616
6617
6618 /**
6619  * @class Roo.bootstrap.breadcrumb.Nav
6620  * @extends Roo.bootstrap.Component
6621  * Bootstrap Breadcrumb Nav Class
6622  *  
6623  * @children Roo.bootstrap.breadcrumb.Item
6624  * 
6625  * @constructor
6626  * Create a new breadcrumb.Nav
6627  * @param {Object} config The config object
6628  */
6629
6630
6631 Roo.bootstrap.breadcrumb.Nav = function(config){
6632     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6633     
6634     
6635 };
6636
6637 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6638     
6639     getAutoCreate : function()
6640     {
6641
6642         var cfg = {
6643             tag: 'nav',
6644             cn : [
6645                 {
6646                     tag : 'ol',
6647                     cls : 'breadcrumb'
6648                 }
6649             ]
6650             
6651         };
6652           
6653         return cfg;
6654     },
6655     
6656     initEvents: function()
6657     {
6658         this.olEl = this.el.select('ol',true).first();    
6659     },
6660     getChildContainer : function()
6661     {
6662         return this.olEl;  
6663     }
6664     
6665 });
6666
6667  /*
6668  * - LGPL
6669  *
6670  *  Breadcrumb Item
6671  * 
6672  */
6673
6674
6675 /**
6676  * @class Roo.bootstrap.breadcrumb.Nav
6677  * @extends Roo.bootstrap.Component
6678  * Bootstrap Breadcrumb Nav Class
6679  *  
6680  * @children Roo.bootstrap.breadcrumb.Component
6681  * @cfg {String} html the content of the link.
6682  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6683  * @cfg {Boolean} active is it active
6684
6685  * 
6686  * @constructor
6687  * Create a new breadcrumb.Nav
6688  * @param {Object} config The config object
6689  */
6690
6691 Roo.bootstrap.breadcrumb.Item = function(config){
6692     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6693     this.addEvents({
6694         // img events
6695         /**
6696          * @event click
6697          * The img click event for the img.
6698          * @param {Roo.EventObject} e
6699          */
6700         "click" : true
6701     });
6702     
6703 };
6704
6705 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6706     
6707     href: false,
6708     html : '',
6709     
6710     getAutoCreate : function()
6711     {
6712
6713         var cfg = {
6714             tag: 'li',
6715             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6716         };
6717         if (this.href !== false) {
6718             cfg.cn = [{
6719                 tag : 'a',
6720                 href : this.href,
6721                 html : this.html
6722             }];
6723         } else {
6724             cfg.html = this.html;
6725         }
6726         
6727         return cfg;
6728     },
6729     
6730     initEvents: function()
6731     {
6732         if (this.href) {
6733             this.el.select('a', true).first().on('click',this.onClick, this)
6734         }
6735         
6736     },
6737     onClick : function(e)
6738     {
6739         e.preventDefault();
6740         this.fireEvent('click',this,  e);
6741     }
6742     
6743 });
6744
6745  /*
6746  * - LGPL
6747  *
6748  * row
6749  * 
6750  */
6751
6752 /**
6753  * @class Roo.bootstrap.Row
6754  * @extends Roo.bootstrap.Component
6755  * Bootstrap Row class (contains columns...)
6756  * 
6757  * @constructor
6758  * Create a new Row
6759  * @param {Object} config The config object
6760  */
6761
6762 Roo.bootstrap.Row = function(config){
6763     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6764 };
6765
6766 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6767     
6768     getAutoCreate : function(){
6769        return {
6770             cls: 'row clearfix'
6771        };
6772     }
6773     
6774     
6775 });
6776
6777  
6778
6779  /*
6780  * - LGPL
6781  *
6782  * pagination
6783  * 
6784  */
6785
6786 /**
6787  * @class Roo.bootstrap.Pagination
6788  * @extends Roo.bootstrap.Component
6789  * Bootstrap Pagination class
6790  * @cfg {String} size xs | sm | md | lg
6791  * @cfg {Boolean} inverse false | true
6792  * 
6793  * @constructor
6794  * Create a new Pagination
6795  * @param {Object} config The config object
6796  */
6797
6798 Roo.bootstrap.Pagination = function(config){
6799     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6800 };
6801
6802 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6803     
6804     cls: false,
6805     size: false,
6806     inverse: false,
6807     
6808     getAutoCreate : function(){
6809         var cfg = {
6810             tag: 'ul',
6811                 cls: 'pagination'
6812         };
6813         if (this.inverse) {
6814             cfg.cls += ' inverse';
6815         }
6816         if (this.html) {
6817             cfg.html=this.html;
6818         }
6819         if (this.cls) {
6820             cfg.cls += " " + this.cls;
6821         }
6822         return cfg;
6823     }
6824    
6825 });
6826
6827  
6828
6829  /*
6830  * - LGPL
6831  *
6832  * Pagination item
6833  * 
6834  */
6835
6836
6837 /**
6838  * @class Roo.bootstrap.PaginationItem
6839  * @extends Roo.bootstrap.Component
6840  * Bootstrap PaginationItem class
6841  * @cfg {String} html text
6842  * @cfg {String} href the link
6843  * @cfg {Boolean} preventDefault (true | false) default true
6844  * @cfg {Boolean} active (true | false) default false
6845  * @cfg {Boolean} disabled default false
6846  * 
6847  * 
6848  * @constructor
6849  * Create a new PaginationItem
6850  * @param {Object} config The config object
6851  */
6852
6853
6854 Roo.bootstrap.PaginationItem = function(config){
6855     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6856     this.addEvents({
6857         // raw events
6858         /**
6859          * @event click
6860          * The raw click event for the entire grid.
6861          * @param {Roo.EventObject} e
6862          */
6863         "click" : true
6864     });
6865 };
6866
6867 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6868     
6869     href : false,
6870     html : false,
6871     preventDefault: true,
6872     active : false,
6873     cls : false,
6874     disabled: false,
6875     
6876     getAutoCreate : function(){
6877         var cfg= {
6878             tag: 'li',
6879             cn: [
6880                 {
6881                     tag : 'a',
6882                     href : this.href ? this.href : '#',
6883                     html : this.html ? this.html : ''
6884                 }
6885             ]
6886         };
6887         
6888         if(this.cls){
6889             cfg.cls = this.cls;
6890         }
6891         
6892         if(this.disabled){
6893             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6894         }
6895         
6896         if(this.active){
6897             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6898         }
6899         
6900         return cfg;
6901     },
6902     
6903     initEvents: function() {
6904         
6905         this.el.on('click', this.onClick, this);
6906         
6907     },
6908     onClick : function(e)
6909     {
6910         Roo.log('PaginationItem on click ');
6911         if(this.preventDefault){
6912             e.preventDefault();
6913         }
6914         
6915         if(this.disabled){
6916             return;
6917         }
6918         
6919         this.fireEvent('click', this, e);
6920     }
6921    
6922 });
6923
6924  
6925
6926  /*
6927  * - LGPL
6928  *
6929  * slider
6930  * 
6931  */
6932
6933
6934 /**
6935  * @class Roo.bootstrap.Slider
6936  * @extends Roo.bootstrap.Component
6937  * Bootstrap Slider class
6938  *    
6939  * @constructor
6940  * Create a new Slider
6941  * @param {Object} config The config object
6942  */
6943
6944 Roo.bootstrap.Slider = function(config){
6945     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6946 };
6947
6948 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6949     
6950     getAutoCreate : function(){
6951         
6952         var cfg = {
6953             tag: 'div',
6954             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6955             cn: [
6956                 {
6957                     tag: 'a',
6958                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6959                 }
6960             ]
6961         };
6962         
6963         return cfg;
6964     }
6965    
6966 });
6967
6968  /*
6969  * Based on:
6970  * Ext JS Library 1.1.1
6971  * Copyright(c) 2006-2007, Ext JS, LLC.
6972  *
6973  * Originally Released Under LGPL - original licence link has changed is not relivant.
6974  *
6975  * Fork - LGPL
6976  * <script type="text/javascript">
6977  */
6978  
6979
6980 /**
6981  * @class Roo.grid.ColumnModel
6982  * @extends Roo.util.Observable
6983  * This is the default implementation of a ColumnModel used by the Grid. It defines
6984  * the columns in the grid.
6985  * <br>Usage:<br>
6986  <pre><code>
6987  var colModel = new Roo.grid.ColumnModel([
6988         {header: "Ticker", width: 60, sortable: true, locked: true},
6989         {header: "Company Name", width: 150, sortable: true},
6990         {header: "Market Cap.", width: 100, sortable: true},
6991         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6992         {header: "Employees", width: 100, sortable: true, resizable: false}
6993  ]);
6994  </code></pre>
6995  * <p>
6996  
6997  * The config options listed for this class are options which may appear in each
6998  * individual column definition.
6999  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7000  * @constructor
7001  * @param {Object} config An Array of column config objects. See this class's
7002  * config objects for details.
7003 */
7004 Roo.grid.ColumnModel = function(config){
7005         /**
7006      * The config passed into the constructor
7007      */
7008     this.config = config;
7009     this.lookup = {};
7010
7011     // if no id, create one
7012     // if the column does not have a dataIndex mapping,
7013     // map it to the order it is in the config
7014     for(var i = 0, len = config.length; i < len; i++){
7015         var c = config[i];
7016         if(typeof c.dataIndex == "undefined"){
7017             c.dataIndex = i;
7018         }
7019         if(typeof c.renderer == "string"){
7020             c.renderer = Roo.util.Format[c.renderer];
7021         }
7022         if(typeof c.id == "undefined"){
7023             c.id = Roo.id();
7024         }
7025         if(c.editor && c.editor.xtype){
7026             c.editor  = Roo.factory(c.editor, Roo.grid);
7027         }
7028         if(c.editor && c.editor.isFormField){
7029             c.editor = new Roo.grid.GridEditor(c.editor);
7030         }
7031         this.lookup[c.id] = c;
7032     }
7033
7034     /**
7035      * The width of columns which have no width specified (defaults to 100)
7036      * @type Number
7037      */
7038     this.defaultWidth = 100;
7039
7040     /**
7041      * Default sortable of columns which have no sortable specified (defaults to false)
7042      * @type Boolean
7043      */
7044     this.defaultSortable = false;
7045
7046     this.addEvents({
7047         /**
7048              * @event widthchange
7049              * Fires when the width of a column changes.
7050              * @param {ColumnModel} this
7051              * @param {Number} columnIndex The column index
7052              * @param {Number} newWidth The new width
7053              */
7054             "widthchange": true,
7055         /**
7056              * @event headerchange
7057              * Fires when the text of a header changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newText The new header text
7061              */
7062             "headerchange": true,
7063         /**
7064              * @event hiddenchange
7065              * Fires when a column is hidden or "unhidden".
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Boolean} hidden true if hidden, false otherwise
7069              */
7070             "hiddenchange": true,
7071             /**
7072          * @event columnmoved
7073          * Fires when a column is moved.
7074          * @param {ColumnModel} this
7075          * @param {Number} oldIndex
7076          * @param {Number} newIndex
7077          */
7078         "columnmoved" : true,
7079         /**
7080          * @event columlockchange
7081          * Fires when a column's locked state is changed
7082          * @param {ColumnModel} this
7083          * @param {Number} colIndex
7084          * @param {Boolean} locked true if locked
7085          */
7086         "columnlockchange" : true
7087     });
7088     Roo.grid.ColumnModel.superclass.constructor.call(this);
7089 };
7090 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7091     /**
7092      * @cfg {String} header The header text to display in the Grid view.
7093      */
7094     /**
7095      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7096      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7097      * specified, the column's index is used as an index into the Record's data Array.
7098      */
7099     /**
7100      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7101      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7102      */
7103     /**
7104      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7105      * Defaults to the value of the {@link #defaultSortable} property.
7106      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7107      */
7108     /**
7109      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7110      */
7111     /**
7112      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7113      */
7114     /**
7115      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7116      */
7117     /**
7118      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7119      */
7120     /**
7121      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7122      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7123      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7124      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7125      */
7126        /**
7127      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7128      */
7129     /**
7130      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7131      */
7132     /**
7133      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7134      */
7135     /**
7136      * @cfg {String} cursor (Optional)
7137      */
7138     /**
7139      * @cfg {String} tooltip (Optional)
7140      */
7141     /**
7142      * @cfg {Number} xs (Optional)
7143      */
7144     /**
7145      * @cfg {Number} sm (Optional)
7146      */
7147     /**
7148      * @cfg {Number} md (Optional)
7149      */
7150     /**
7151      * @cfg {Number} lg (Optional)
7152      */
7153     /**
7154      * Returns the id of the column at the specified index.
7155      * @param {Number} index The column index
7156      * @return {String} the id
7157      */
7158     getColumnId : function(index){
7159         return this.config[index].id;
7160     },
7161
7162     /**
7163      * Returns the column for a specified id.
7164      * @param {String} id The column id
7165      * @return {Object} the column
7166      */
7167     getColumnById : function(id){
7168         return this.lookup[id];
7169     },
7170
7171     
7172     /**
7173      * Returns the column for a specified dataIndex.
7174      * @param {String} dataIndex The column dataIndex
7175      * @return {Object|Boolean} the column or false if not found
7176      */
7177     getColumnByDataIndex: function(dataIndex){
7178         var index = this.findColumnIndex(dataIndex);
7179         return index > -1 ? this.config[index] : false;
7180     },
7181     
7182     /**
7183      * Returns the index for a specified column id.
7184      * @param {String} id The column id
7185      * @return {Number} the index, or -1 if not found
7186      */
7187     getIndexById : function(id){
7188         for(var i = 0, len = this.config.length; i < len; i++){
7189             if(this.config[i].id == id){
7190                 return i;
7191             }
7192         }
7193         return -1;
7194     },
7195     
7196     /**
7197      * Returns the index for a specified column dataIndex.
7198      * @param {String} dataIndex The column dataIndex
7199      * @return {Number} the index, or -1 if not found
7200      */
7201     
7202     findColumnIndex : function(dataIndex){
7203         for(var i = 0, len = this.config.length; i < len; i++){
7204             if(this.config[i].dataIndex == dataIndex){
7205                 return i;
7206             }
7207         }
7208         return -1;
7209     },
7210     
7211     
7212     moveColumn : function(oldIndex, newIndex){
7213         var c = this.config[oldIndex];
7214         this.config.splice(oldIndex, 1);
7215         this.config.splice(newIndex, 0, c);
7216         this.dataMap = null;
7217         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7218     },
7219
7220     isLocked : function(colIndex){
7221         return this.config[colIndex].locked === true;
7222     },
7223
7224     setLocked : function(colIndex, value, suppressEvent){
7225         if(this.isLocked(colIndex) == value){
7226             return;
7227         }
7228         this.config[colIndex].locked = value;
7229         if(!suppressEvent){
7230             this.fireEvent("columnlockchange", this, colIndex, value);
7231         }
7232     },
7233
7234     getTotalLockedWidth : function(){
7235         var totalWidth = 0;
7236         for(var i = 0; i < this.config.length; i++){
7237             if(this.isLocked(i) && !this.isHidden(i)){
7238                 this.totalWidth += this.getColumnWidth(i);
7239             }
7240         }
7241         return totalWidth;
7242     },
7243
7244     getLockedCount : function(){
7245         for(var i = 0, len = this.config.length; i < len; i++){
7246             if(!this.isLocked(i)){
7247                 return i;
7248             }
7249         }
7250         
7251         return this.config.length;
7252     },
7253
7254     /**
7255      * Returns the number of columns.
7256      * @return {Number}
7257      */
7258     getColumnCount : function(visibleOnly){
7259         if(visibleOnly === true){
7260             var c = 0;
7261             for(var i = 0, len = this.config.length; i < len; i++){
7262                 if(!this.isHidden(i)){
7263                     c++;
7264                 }
7265             }
7266             return c;
7267         }
7268         return this.config.length;
7269     },
7270
7271     /**
7272      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7273      * @param {Function} fn
7274      * @param {Object} scope (optional)
7275      * @return {Array} result
7276      */
7277     getColumnsBy : function(fn, scope){
7278         var r = [];
7279         for(var i = 0, len = this.config.length; i < len; i++){
7280             var c = this.config[i];
7281             if(fn.call(scope||this, c, i) === true){
7282                 r[r.length] = c;
7283             }
7284         }
7285         return r;
7286     },
7287
7288     /**
7289      * Returns true if the specified column is sortable.
7290      * @param {Number} col The column index
7291      * @return {Boolean}
7292      */
7293     isSortable : function(col){
7294         if(typeof this.config[col].sortable == "undefined"){
7295             return this.defaultSortable;
7296         }
7297         return this.config[col].sortable;
7298     },
7299
7300     /**
7301      * Returns the rendering (formatting) function defined for the column.
7302      * @param {Number} col The column index.
7303      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7304      */
7305     getRenderer : function(col){
7306         if(!this.config[col].renderer){
7307             return Roo.grid.ColumnModel.defaultRenderer;
7308         }
7309         return this.config[col].renderer;
7310     },
7311
7312     /**
7313      * Sets the rendering (formatting) function for a column.
7314      * @param {Number} col The column index
7315      * @param {Function} fn The function to use to process the cell's raw data
7316      * to return HTML markup for the grid view. The render function is called with
7317      * the following parameters:<ul>
7318      * <li>Data value.</li>
7319      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7320      * <li>css A CSS style string to apply to the table cell.</li>
7321      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7322      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7323      * <li>Row index</li>
7324      * <li>Column index</li>
7325      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7326      */
7327     setRenderer : function(col, fn){
7328         this.config[col].renderer = fn;
7329     },
7330
7331     /**
7332      * Returns the width for the specified column.
7333      * @param {Number} col The column index
7334      * @return {Number}
7335      */
7336     getColumnWidth : function(col){
7337         return this.config[col].width * 1 || this.defaultWidth;
7338     },
7339
7340     /**
7341      * Sets the width for a column.
7342      * @param {Number} col The column index
7343      * @param {Number} width The new width
7344      */
7345     setColumnWidth : function(col, width, suppressEvent){
7346         this.config[col].width = width;
7347         this.totalWidth = null;
7348         if(!suppressEvent){
7349              this.fireEvent("widthchange", this, col, width);
7350         }
7351     },
7352
7353     /**
7354      * Returns the total width of all columns.
7355      * @param {Boolean} includeHidden True to include hidden column widths
7356      * @return {Number}
7357      */
7358     getTotalWidth : function(includeHidden){
7359         if(!this.totalWidth){
7360             this.totalWidth = 0;
7361             for(var i = 0, len = this.config.length; i < len; i++){
7362                 if(includeHidden || !this.isHidden(i)){
7363                     this.totalWidth += this.getColumnWidth(i);
7364                 }
7365             }
7366         }
7367         return this.totalWidth;
7368     },
7369
7370     /**
7371      * Returns the header for the specified column.
7372      * @param {Number} col The column index
7373      * @return {String}
7374      */
7375     getColumnHeader : function(col){
7376         return this.config[col].header;
7377     },
7378
7379     /**
7380      * Sets the header for a column.
7381      * @param {Number} col The column index
7382      * @param {String} header The new header
7383      */
7384     setColumnHeader : function(col, header){
7385         this.config[col].header = header;
7386         this.fireEvent("headerchange", this, col, header);
7387     },
7388
7389     /**
7390      * Returns the tooltip for the specified column.
7391      * @param {Number} col The column index
7392      * @return {String}
7393      */
7394     getColumnTooltip : function(col){
7395             return this.config[col].tooltip;
7396     },
7397     /**
7398      * Sets the tooltip for a column.
7399      * @param {Number} col The column index
7400      * @param {String} tooltip The new tooltip
7401      */
7402     setColumnTooltip : function(col, tooltip){
7403             this.config[col].tooltip = tooltip;
7404     },
7405
7406     /**
7407      * Returns the dataIndex for the specified column.
7408      * @param {Number} col The column index
7409      * @return {Number}
7410      */
7411     getDataIndex : function(col){
7412         return this.config[col].dataIndex;
7413     },
7414
7415     /**
7416      * Sets the dataIndex for a column.
7417      * @param {Number} col The column index
7418      * @param {Number} dataIndex The new dataIndex
7419      */
7420     setDataIndex : function(col, dataIndex){
7421         this.config[col].dataIndex = dataIndex;
7422     },
7423
7424     
7425     
7426     /**
7427      * Returns true if the cell is editable.
7428      * @param {Number} colIndex The column index
7429      * @param {Number} rowIndex The row index - this is nto actually used..?
7430      * @return {Boolean}
7431      */
7432     isCellEditable : function(colIndex, rowIndex){
7433         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7434     },
7435
7436     /**
7437      * Returns the editor defined for the cell/column.
7438      * return false or null to disable editing.
7439      * @param {Number} colIndex The column index
7440      * @param {Number} rowIndex The row index
7441      * @return {Object}
7442      */
7443     getCellEditor : function(colIndex, rowIndex){
7444         return this.config[colIndex].editor;
7445     },
7446
7447     /**
7448      * Sets if a column is editable.
7449      * @param {Number} col The column index
7450      * @param {Boolean} editable True if the column is editable
7451      */
7452     setEditable : function(col, editable){
7453         this.config[col].editable = editable;
7454     },
7455
7456
7457     /**
7458      * Returns true if the column is hidden.
7459      * @param {Number} colIndex The column index
7460      * @return {Boolean}
7461      */
7462     isHidden : function(colIndex){
7463         return this.config[colIndex].hidden;
7464     },
7465
7466
7467     /**
7468      * Returns true if the column width cannot be changed
7469      */
7470     isFixed : function(colIndex){
7471         return this.config[colIndex].fixed;
7472     },
7473
7474     /**
7475      * Returns true if the column can be resized
7476      * @return {Boolean}
7477      */
7478     isResizable : function(colIndex){
7479         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7480     },
7481     /**
7482      * Sets if a column is hidden.
7483      * @param {Number} colIndex The column index
7484      * @param {Boolean} hidden True if the column is hidden
7485      */
7486     setHidden : function(colIndex, hidden){
7487         this.config[colIndex].hidden = hidden;
7488         this.totalWidth = null;
7489         this.fireEvent("hiddenchange", this, colIndex, hidden);
7490     },
7491
7492     /**
7493      * Sets the editor for a column.
7494      * @param {Number} col The column index
7495      * @param {Object} editor The editor object
7496      */
7497     setEditor : function(col, editor){
7498         this.config[col].editor = editor;
7499     }
7500 });
7501
7502 Roo.grid.ColumnModel.defaultRenderer = function(value)
7503 {
7504     if(typeof value == "object") {
7505         return value;
7506     }
7507         if(typeof value == "string" && value.length < 1){
7508             return "&#160;";
7509         }
7510     
7511         return String.format("{0}", value);
7512 };
7513
7514 // Alias for backwards compatibility
7515 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7516 /*
7517  * Based on:
7518  * Ext JS Library 1.1.1
7519  * Copyright(c) 2006-2007, Ext JS, LLC.
7520  *
7521  * Originally Released Under LGPL - original licence link has changed is not relivant.
7522  *
7523  * Fork - LGPL
7524  * <script type="text/javascript">
7525  */
7526  
7527 /**
7528  * @class Roo.LoadMask
7529  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7530  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7531  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7532  * element's UpdateManager load indicator and will be destroyed after the initial load.
7533  * @constructor
7534  * Create a new LoadMask
7535  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7536  * @param {Object} config The config object
7537  */
7538 Roo.LoadMask = function(el, config){
7539     this.el = Roo.get(el);
7540     Roo.apply(this, config);
7541     if(this.store){
7542         this.store.on('beforeload', this.onBeforeLoad, this);
7543         this.store.on('load', this.onLoad, this);
7544         this.store.on('loadexception', this.onLoadException, this);
7545         this.removeMask = false;
7546     }else{
7547         var um = this.el.getUpdateManager();
7548         um.showLoadIndicator = false; // disable the default indicator
7549         um.on('beforeupdate', this.onBeforeLoad, this);
7550         um.on('update', this.onLoad, this);
7551         um.on('failure', this.onLoad, this);
7552         this.removeMask = true;
7553     }
7554 };
7555
7556 Roo.LoadMask.prototype = {
7557     /**
7558      * @cfg {Boolean} removeMask
7559      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7560      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7561      */
7562     /**
7563      * @cfg {String} msg
7564      * The text to display in a centered loading message box (defaults to 'Loading...')
7565      */
7566     msg : 'Loading...',
7567     /**
7568      * @cfg {String} msgCls
7569      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7570      */
7571     msgCls : 'x-mask-loading',
7572
7573     /**
7574      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7575      * @type Boolean
7576      */
7577     disabled: false,
7578
7579     /**
7580      * Disables the mask to prevent it from being displayed
7581      */
7582     disable : function(){
7583        this.disabled = true;
7584     },
7585
7586     /**
7587      * Enables the mask so that it can be displayed
7588      */
7589     enable : function(){
7590         this.disabled = false;
7591     },
7592     
7593     onLoadException : function()
7594     {
7595         Roo.log(arguments);
7596         
7597         if (typeof(arguments[3]) != 'undefined') {
7598             Roo.MessageBox.alert("Error loading",arguments[3]);
7599         } 
7600         /*
7601         try {
7602             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7603                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7604             }   
7605         } catch(e) {
7606             
7607         }
7608         */
7609     
7610         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7611     },
7612     // private
7613     onLoad : function()
7614     {
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617
7618     // private
7619     onBeforeLoad : function(){
7620         if(!this.disabled){
7621             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7622         }
7623     },
7624
7625     // private
7626     destroy : function(){
7627         if(this.store){
7628             this.store.un('beforeload', this.onBeforeLoad, this);
7629             this.store.un('load', this.onLoad, this);
7630             this.store.un('loadexception', this.onLoadException, this);
7631         }else{
7632             var um = this.el.getUpdateManager();
7633             um.un('beforeupdate', this.onBeforeLoad, this);
7634             um.un('update', this.onLoad, this);
7635             um.un('failure', this.onLoad, this);
7636         }
7637     }
7638 };/*
7639  * - LGPL
7640  *
7641  * table
7642  * 
7643  */
7644
7645 /**
7646  * @class Roo.bootstrap.Table
7647  * @extends Roo.bootstrap.Component
7648  * Bootstrap Table class
7649  * @cfg {String} cls table class
7650  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7651  * @cfg {String} bgcolor Specifies the background color for a table
7652  * @cfg {Number} border Specifies whether the table cells should have borders or not
7653  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7654  * @cfg {Number} cellspacing Specifies the space between cells
7655  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7656  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7657  * @cfg {String} sortable Specifies that the table should be sortable
7658  * @cfg {String} summary Specifies a summary of the content of a table
7659  * @cfg {Number} width Specifies the width of a table
7660  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7661  * 
7662  * @cfg {boolean} striped Should the rows be alternative striped
7663  * @cfg {boolean} bordered Add borders to the table
7664  * @cfg {boolean} hover Add hover highlighting
7665  * @cfg {boolean} condensed Format condensed
7666  * @cfg {boolean} responsive Format condensed
7667  * @cfg {Boolean} loadMask (true|false) default false
7668  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7669  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7670  * @cfg {Boolean} rowSelection (true|false) default false
7671  * @cfg {Boolean} cellSelection (true|false) default false
7672  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7673  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7674  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7675  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7676  
7677  * 
7678  * @constructor
7679  * Create a new Table
7680  * @param {Object} config The config object
7681  */
7682
7683 Roo.bootstrap.Table = function(config){
7684     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7685     
7686   
7687     
7688     // BC...
7689     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7690     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7691     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7692     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7693     
7694     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7695     if (this.sm) {
7696         this.sm.grid = this;
7697         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7698         this.sm = this.selModel;
7699         this.sm.xmodule = this.xmodule || false;
7700     }
7701     
7702     if (this.cm && typeof(this.cm.config) == 'undefined') {
7703         this.colModel = new Roo.grid.ColumnModel(this.cm);
7704         this.cm = this.colModel;
7705         this.cm.xmodule = this.xmodule || false;
7706     }
7707     if (this.store) {
7708         this.store= Roo.factory(this.store, Roo.data);
7709         this.ds = this.store;
7710         this.ds.xmodule = this.xmodule || false;
7711          
7712     }
7713     if (this.footer && this.store) {
7714         this.footer.dataSource = this.ds;
7715         this.footer = Roo.factory(this.footer);
7716     }
7717     
7718     /** @private */
7719     this.addEvents({
7720         /**
7721          * @event cellclick
7722          * Fires when a cell is clicked
7723          * @param {Roo.bootstrap.Table} this
7724          * @param {Roo.Element} el
7725          * @param {Number} rowIndex
7726          * @param {Number} columnIndex
7727          * @param {Roo.EventObject} e
7728          */
7729         "cellclick" : true,
7730         /**
7731          * @event celldblclick
7732          * Fires when a cell is double clicked
7733          * @param {Roo.bootstrap.Table} this
7734          * @param {Roo.Element} el
7735          * @param {Number} rowIndex
7736          * @param {Number} columnIndex
7737          * @param {Roo.EventObject} e
7738          */
7739         "celldblclick" : true,
7740         /**
7741          * @event rowclick
7742          * Fires when a row is clicked
7743          * @param {Roo.bootstrap.Table} this
7744          * @param {Roo.Element} el
7745          * @param {Number} rowIndex
7746          * @param {Roo.EventObject} e
7747          */
7748         "rowclick" : true,
7749         /**
7750          * @event rowdblclick
7751          * Fires when a row is double clicked
7752          * @param {Roo.bootstrap.Table} this
7753          * @param {Roo.Element} el
7754          * @param {Number} rowIndex
7755          * @param {Roo.EventObject} e
7756          */
7757         "rowdblclick" : true,
7758         /**
7759          * @event mouseover
7760          * Fires when a mouseover occur
7761          * @param {Roo.bootstrap.Table} this
7762          * @param {Roo.Element} el
7763          * @param {Number} rowIndex
7764          * @param {Number} columnIndex
7765          * @param {Roo.EventObject} e
7766          */
7767         "mouseover" : true,
7768         /**
7769          * @event mouseout
7770          * Fires when a mouseout occur
7771          * @param {Roo.bootstrap.Table} this
7772          * @param {Roo.Element} el
7773          * @param {Number} rowIndex
7774          * @param {Number} columnIndex
7775          * @param {Roo.EventObject} e
7776          */
7777         "mouseout" : true,
7778         /**
7779          * @event rowclass
7780          * Fires when a row is rendered, so you can change add a style to it.
7781          * @param {Roo.bootstrap.Table} this
7782          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7783          */
7784         'rowclass' : true,
7785           /**
7786          * @event rowsrendered
7787          * Fires when all the  rows have been rendered
7788          * @param {Roo.bootstrap.Table} this
7789          */
7790         'rowsrendered' : true,
7791         /**
7792          * @event contextmenu
7793          * The raw contextmenu event for the entire grid.
7794          * @param {Roo.EventObject} e
7795          */
7796         "contextmenu" : true,
7797         /**
7798          * @event rowcontextmenu
7799          * Fires when a row is right clicked
7800          * @param {Roo.bootstrap.Table} this
7801          * @param {Number} rowIndex
7802          * @param {Roo.EventObject} e
7803          */
7804         "rowcontextmenu" : true,
7805         /**
7806          * @event cellcontextmenu
7807          * Fires when a cell is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Number} cellIndex
7811          * @param {Roo.EventObject} e
7812          */
7813          "cellcontextmenu" : true,
7814          /**
7815          * @event headercontextmenu
7816          * Fires when a header is right clicked
7817          * @param {Roo.bootstrap.Table} this
7818          * @param {Number} columnIndex
7819          * @param {Roo.EventObject} e
7820          */
7821         "headercontextmenu" : true
7822     });
7823 };
7824
7825 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7826     
7827     cls: false,
7828     align: false,
7829     bgcolor: false,
7830     border: false,
7831     cellpadding: false,
7832     cellspacing: false,
7833     frame: false,
7834     rules: false,
7835     sortable: false,
7836     summary: false,
7837     width: false,
7838     striped : false,
7839     scrollBody : false,
7840     bordered: false,
7841     hover:  false,
7842     condensed : false,
7843     responsive : false,
7844     sm : false,
7845     cm : false,
7846     store : false,
7847     loadMask : false,
7848     footerShow : true,
7849     headerShow : true,
7850   
7851     rowSelection : false,
7852     cellSelection : false,
7853     layout : false,
7854     
7855     // Roo.Element - the tbody
7856     mainBody: false,
7857     // Roo.Element - thead element
7858     mainHead: false,
7859     
7860     container: false, // used by gridpanel...
7861     
7862     lazyLoad : false,
7863     
7864     CSS : Roo.util.CSS,
7865     
7866     auto_hide_footer : false,
7867     
7868     getAutoCreate : function()
7869     {
7870         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7871         
7872         cfg = {
7873             tag: 'table',
7874             cls : 'table',
7875             cn : []
7876         };
7877         if (this.scrollBody) {
7878             cfg.cls += ' table-body-fixed';
7879         }    
7880         if (this.striped) {
7881             cfg.cls += ' table-striped';
7882         }
7883         
7884         if (this.hover) {
7885             cfg.cls += ' table-hover';
7886         }
7887         if (this.bordered) {
7888             cfg.cls += ' table-bordered';
7889         }
7890         if (this.condensed) {
7891             cfg.cls += ' table-condensed';
7892         }
7893         if (this.responsive) {
7894             cfg.cls += ' table-responsive';
7895         }
7896         
7897         if (this.cls) {
7898             cfg.cls+=  ' ' +this.cls;
7899         }
7900         
7901         // this lot should be simplifed...
7902         var _t = this;
7903         var cp = [
7904             'align',
7905             'bgcolor',
7906             'border',
7907             'cellpadding',
7908             'cellspacing',
7909             'frame',
7910             'rules',
7911             'sortable',
7912             'summary',
7913             'width'
7914         ].forEach(function(k) {
7915             if (_t[k]) {
7916                 cfg[k] = _t[k];
7917             }
7918         });
7919         
7920         
7921         if (this.layout) {
7922             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7923         }
7924         
7925         if(this.store || this.cm){
7926             if(this.headerShow){
7927                 cfg.cn.push(this.renderHeader());
7928             }
7929             
7930             cfg.cn.push(this.renderBody());
7931             
7932             if(this.footerShow){
7933                 cfg.cn.push(this.renderFooter());
7934             }
7935             // where does this come from?
7936             //cfg.cls+=  ' TableGrid';
7937         }
7938         
7939         return { cn : [ cfg ] };
7940     },
7941     
7942     initEvents : function()
7943     {   
7944         if(!this.store || !this.cm){
7945             return;
7946         }
7947         if (this.selModel) {
7948             this.selModel.initEvents();
7949         }
7950         
7951         
7952         //Roo.log('initEvents with ds!!!!');
7953         
7954         this.mainBody = this.el.select('tbody', true).first();
7955         this.mainHead = this.el.select('thead', true).first();
7956         this.mainFoot = this.el.select('tfoot', true).first();
7957         
7958         
7959         
7960         var _this = this;
7961         
7962         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7963             e.on('click', _this.sort, _this);
7964         });
7965         
7966         this.mainBody.on("click", this.onClick, this);
7967         this.mainBody.on("dblclick", this.onDblClick, this);
7968         
7969         // why is this done????? = it breaks dialogs??
7970         //this.parent().el.setStyle('position', 'relative');
7971         
7972         
7973         if (this.footer) {
7974             this.footer.parentId = this.id;
7975             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7976             
7977             if(this.lazyLoad){
7978                 this.el.select('tfoot tr td').first().addClass('hide');
7979             }
7980         } 
7981         
7982         if(this.loadMask) {
7983             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7984         }
7985         
7986         this.store.on('load', this.onLoad, this);
7987         this.store.on('beforeload', this.onBeforeLoad, this);
7988         this.store.on('update', this.onUpdate, this);
7989         this.store.on('add', this.onAdd, this);
7990         this.store.on("clear", this.clear, this);
7991         
7992         this.el.on("contextmenu", this.onContextMenu, this);
7993         
7994         this.mainBody.on('scroll', this.onBodyScroll, this);
7995         
7996         this.cm.on("headerchange", this.onHeaderChange, this);
7997         
7998         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7999         
8000     },
8001     
8002     onContextMenu : function(e, t)
8003     {
8004         this.processEvent("contextmenu", e);
8005     },
8006     
8007     processEvent : function(name, e)
8008     {
8009         if (name != 'touchstart' ) {
8010             this.fireEvent(name, e);    
8011         }
8012         
8013         var t = e.getTarget();
8014         
8015         var cell = Roo.get(t);
8016         
8017         if(!cell){
8018             return;
8019         }
8020         
8021         if(cell.findParent('tfoot', false, true)){
8022             return;
8023         }
8024         
8025         if(cell.findParent('thead', false, true)){
8026             
8027             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8028                 cell = Roo.get(t).findParent('th', false, true);
8029                 if (!cell) {
8030                     Roo.log("failed to find th in thead?");
8031                     Roo.log(e.getTarget());
8032                     return;
8033                 }
8034             }
8035             
8036             var cellIndex = cell.dom.cellIndex;
8037             
8038             var ename = name == 'touchstart' ? 'click' : name;
8039             this.fireEvent("header" + ename, this, cellIndex, e);
8040             
8041             return;
8042         }
8043         
8044         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8045             cell = Roo.get(t).findParent('td', false, true);
8046             if (!cell) {
8047                 Roo.log("failed to find th in tbody?");
8048                 Roo.log(e.getTarget());
8049                 return;
8050             }
8051         }
8052         
8053         var row = cell.findParent('tr', false, true);
8054         var cellIndex = cell.dom.cellIndex;
8055         var rowIndex = row.dom.rowIndex - 1;
8056         
8057         if(row !== false){
8058             
8059             this.fireEvent("row" + name, this, rowIndex, e);
8060             
8061             if(cell !== false){
8062             
8063                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8064             }
8065         }
8066         
8067     },
8068     
8069     onMouseover : function(e, el)
8070     {
8071         var cell = Roo.get(el);
8072         
8073         if(!cell){
8074             return;
8075         }
8076         
8077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8078             cell = cell.findParent('td', false, true);
8079         }
8080         
8081         var row = cell.findParent('tr', false, true);
8082         var cellIndex = cell.dom.cellIndex;
8083         var rowIndex = row.dom.rowIndex - 1; // start from 0
8084         
8085         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8086         
8087     },
8088     
8089     onMouseout : function(e, el)
8090     {
8091         var cell = Roo.get(el);
8092         
8093         if(!cell){
8094             return;
8095         }
8096         
8097         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8098             cell = cell.findParent('td', false, true);
8099         }
8100         
8101         var row = cell.findParent('tr', false, true);
8102         var cellIndex = cell.dom.cellIndex;
8103         var rowIndex = row.dom.rowIndex - 1; // start from 0
8104         
8105         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8106         
8107     },
8108     
8109     onClick : function(e, el)
8110     {
8111         var cell = Roo.get(el);
8112         
8113         if(!cell || (!this.cellSelection && !this.rowSelection)){
8114             return;
8115         }
8116         
8117         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8118             cell = cell.findParent('td', false, true);
8119         }
8120         
8121         if(!cell || typeof(cell) == 'undefined'){
8122             return;
8123         }
8124         
8125         var row = cell.findParent('tr', false, true);
8126         
8127         if(!row || typeof(row) == 'undefined'){
8128             return;
8129         }
8130         
8131         var cellIndex = cell.dom.cellIndex;
8132         var rowIndex = this.getRowIndex(row);
8133         
8134         // why??? - should these not be based on SelectionModel?
8135         if(this.cellSelection){
8136             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8137         }
8138         
8139         if(this.rowSelection){
8140             this.fireEvent('rowclick', this, row, rowIndex, e);
8141         }
8142         
8143         
8144     },
8145         
8146     onDblClick : function(e,el)
8147     {
8148         var cell = Roo.get(el);
8149         
8150         if(!cell || (!this.cellSelection && !this.rowSelection)){
8151             return;
8152         }
8153         
8154         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8155             cell = cell.findParent('td', false, true);
8156         }
8157         
8158         if(!cell || typeof(cell) == 'undefined'){
8159             return;
8160         }
8161         
8162         var row = cell.findParent('tr', false, true);
8163         
8164         if(!row || typeof(row) == 'undefined'){
8165             return;
8166         }
8167         
8168         var cellIndex = cell.dom.cellIndex;
8169         var rowIndex = this.getRowIndex(row);
8170         
8171         if(this.cellSelection){
8172             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8173         }
8174         
8175         if(this.rowSelection){
8176             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8177         }
8178     },
8179     
8180     sort : function(e,el)
8181     {
8182         var col = Roo.get(el);
8183         
8184         if(!col.hasClass('sortable')){
8185             return;
8186         }
8187         
8188         var sort = col.attr('sort');
8189         var dir = 'ASC';
8190         
8191         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8192             dir = 'DESC';
8193         }
8194         
8195         this.store.sortInfo = {field : sort, direction : dir};
8196         
8197         if (this.footer) {
8198             Roo.log("calling footer first");
8199             this.footer.onClick('first');
8200         } else {
8201         
8202             this.store.load({ params : { start : 0 } });
8203         }
8204     },
8205     
8206     renderHeader : function()
8207     {
8208         var header = {
8209             tag: 'thead',
8210             cn : []
8211         };
8212         
8213         var cm = this.cm;
8214         this.totalWidth = 0;
8215         
8216         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8217             
8218             var config = cm.config[i];
8219             
8220             var c = {
8221                 tag: 'th',
8222                 cls : 'x-hcol-' + i,
8223                 style : '',
8224                 html: cm.getColumnHeader(i)
8225             };
8226             
8227             var hh = '';
8228             
8229             if(typeof(config.sortable) != 'undefined' && config.sortable){
8230                 c.cls = 'sortable';
8231                 c.html = '<i class="glyphicon"></i>' + c.html;
8232             }
8233             
8234             // could use BS4 hidden-..-down 
8235             
8236             if(typeof(config.lgHeader) != 'undefined'){
8237                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8238             }
8239             
8240             if(typeof(config.mdHeader) != 'undefined'){
8241                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8242             }
8243             
8244             if(typeof(config.smHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.xsHeader) != 'undefined'){
8249                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8250             }
8251             
8252             if(hh.length){
8253                 c.html = hh;
8254             }
8255             
8256             if(typeof(config.tooltip) != 'undefined'){
8257                 c.tooltip = config.tooltip;
8258             }
8259             
8260             if(typeof(config.colspan) != 'undefined'){
8261                 c.colspan = config.colspan;
8262             }
8263             
8264             if(typeof(config.hidden) != 'undefined' && config.hidden){
8265                 c.style += ' display:none;';
8266             }
8267             
8268             if(typeof(config.dataIndex) != 'undefined'){
8269                 c.sort = config.dataIndex;
8270             }
8271             
8272            
8273             
8274             if(typeof(config.align) != 'undefined' && config.align.length){
8275                 c.style += ' text-align:' + config.align + ';';
8276             }
8277             
8278             if(typeof(config.width) != 'undefined'){
8279                 c.style += ' width:' + config.width + 'px;';
8280                 this.totalWidth += config.width;
8281             } else {
8282                 this.totalWidth += 100; // assume minimum of 100 per column?
8283             }
8284             
8285             if(typeof(config.cls) != 'undefined'){
8286                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8287             }
8288             
8289             ['xs','sm','md','lg'].map(function(size){
8290                 
8291                 if(typeof(config[size]) == 'undefined'){
8292                     return;
8293                 }
8294                  
8295                 if (!config[size]) { // 0 = hidden
8296                     // BS 4 '0' is treated as hide that column and below.
8297                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8298                     return;
8299                 }
8300                 
8301                 c.cls += ' col-' + size + '-' + config[size] + (
8302                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8303                 );
8304                 
8305                 
8306             });
8307             
8308             header.cn.push(c)
8309         }
8310         
8311         return header;
8312     },
8313     
8314     renderBody : function()
8315     {
8316         var body = {
8317             tag: 'tbody',
8318             cn : [
8319                 {
8320                     tag: 'tr',
8321                     cn : [
8322                         {
8323                             tag : 'td',
8324                             colspan :  this.cm.getColumnCount()
8325                         }
8326                     ]
8327                 }
8328             ]
8329         };
8330         
8331         return body;
8332     },
8333     
8334     renderFooter : function()
8335     {
8336         var footer = {
8337             tag: 'tfoot',
8338             cn : [
8339                 {
8340                     tag: 'tr',
8341                     cn : [
8342                         {
8343                             tag : 'td',
8344                             colspan :  this.cm.getColumnCount()
8345                         }
8346                     ]
8347                 }
8348             ]
8349         };
8350         
8351         return footer;
8352     },
8353     
8354     
8355     
8356     onLoad : function()
8357     {
8358 //        Roo.log('ds onload');
8359         this.clear();
8360         
8361         var _this = this;
8362         var cm = this.cm;
8363         var ds = this.store;
8364         
8365         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8366             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8367             if (_this.store.sortInfo) {
8368                     
8369                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8370                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8371                 }
8372                 
8373                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8374                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8375                 }
8376             }
8377         });
8378         
8379         var tbody =  this.mainBody;
8380               
8381         if(ds.getCount() > 0){
8382             ds.data.each(function(d,rowIndex){
8383                 var row =  this.renderRow(cm, ds, rowIndex);
8384                 
8385                 tbody.createChild(row);
8386                 
8387                 var _this = this;
8388                 
8389                 if(row.cellObjects.length){
8390                     Roo.each(row.cellObjects, function(r){
8391                         _this.renderCellObject(r);
8392                     })
8393                 }
8394                 
8395             }, this);
8396         }
8397         
8398         var tfoot = this.el.select('tfoot', true).first();
8399         
8400         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8401             
8402             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8403             
8404             var total = this.ds.getTotalCount();
8405             
8406             if(this.footer.pageSize < total){
8407                 this.mainFoot.show();
8408             }
8409         }
8410         
8411         Roo.each(this.el.select('tbody td', true).elements, function(e){
8412             e.on('mouseover', _this.onMouseover, _this);
8413         });
8414         
8415         Roo.each(this.el.select('tbody td', true).elements, function(e){
8416             e.on('mouseout', _this.onMouseout, _this);
8417         });
8418         this.fireEvent('rowsrendered', this);
8419         
8420         this.autoSize();
8421     },
8422     
8423     
8424     onUpdate : function(ds,record)
8425     {
8426         this.refreshRow(record);
8427         this.autoSize();
8428     },
8429     
8430     onRemove : function(ds, record, index, isUpdate){
8431         if(isUpdate !== true){
8432             this.fireEvent("beforerowremoved", this, index, record);
8433         }
8434         var bt = this.mainBody.dom;
8435         
8436         var rows = this.el.select('tbody > tr', true).elements;
8437         
8438         if(typeof(rows[index]) != 'undefined'){
8439             bt.removeChild(rows[index].dom);
8440         }
8441         
8442 //        if(bt.rows[index]){
8443 //            bt.removeChild(bt.rows[index]);
8444 //        }
8445         
8446         if(isUpdate !== true){
8447             //this.stripeRows(index);
8448             //this.syncRowHeights(index, index);
8449             //this.layout();
8450             this.fireEvent("rowremoved", this, index, record);
8451         }
8452     },
8453     
8454     onAdd : function(ds, records, rowIndex)
8455     {
8456         //Roo.log('on Add called');
8457         // - note this does not handle multiple adding very well..
8458         var bt = this.mainBody.dom;
8459         for (var i =0 ; i < records.length;i++) {
8460             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8461             //Roo.log(records[i]);
8462             //Roo.log(this.store.getAt(rowIndex+i));
8463             this.insertRow(this.store, rowIndex + i, false);
8464             return;
8465         }
8466         
8467     },
8468     
8469     
8470     refreshRow : function(record){
8471         var ds = this.store, index;
8472         if(typeof record == 'number'){
8473             index = record;
8474             record = ds.getAt(index);
8475         }else{
8476             index = ds.indexOf(record);
8477             if (index < 0) {
8478                 return; // should not happen - but seems to 
8479             }
8480         }
8481         this.insertRow(ds, index, true);
8482         this.autoSize();
8483         this.onRemove(ds, record, index+1, true);
8484         this.autoSize();
8485         //this.syncRowHeights(index, index);
8486         //this.layout();
8487         this.fireEvent("rowupdated", this, index, record);
8488     },
8489     
8490     insertRow : function(dm, rowIndex, isUpdate){
8491         
8492         if(!isUpdate){
8493             this.fireEvent("beforerowsinserted", this, rowIndex);
8494         }
8495             //var s = this.getScrollState();
8496         var row = this.renderRow(this.cm, this.store, rowIndex);
8497         // insert before rowIndex..
8498         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8499         
8500         var _this = this;
8501                 
8502         if(row.cellObjects.length){
8503             Roo.each(row.cellObjects, function(r){
8504                 _this.renderCellObject(r);
8505             })
8506         }
8507             
8508         if(!isUpdate){
8509             this.fireEvent("rowsinserted", this, rowIndex);
8510             //this.syncRowHeights(firstRow, lastRow);
8511             //this.stripeRows(firstRow);
8512             //this.layout();
8513         }
8514         
8515     },
8516     
8517     
8518     getRowDom : function(rowIndex)
8519     {
8520         var rows = this.el.select('tbody > tr', true).elements;
8521         
8522         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8523         
8524     },
8525     // returns the object tree for a tr..
8526   
8527     
8528     renderRow : function(cm, ds, rowIndex) 
8529     {
8530         var d = ds.getAt(rowIndex);
8531         
8532         var row = {
8533             tag : 'tr',
8534             cls : 'x-row-' + rowIndex,
8535             cn : []
8536         };
8537             
8538         var cellObjects = [];
8539         
8540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8541             var config = cm.config[i];
8542             
8543             var renderer = cm.getRenderer(i);
8544             var value = '';
8545             var id = false;
8546             
8547             if(typeof(renderer) !== 'undefined'){
8548                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8549             }
8550             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8551             // and are rendered into the cells after the row is rendered - using the id for the element.
8552             
8553             if(typeof(value) === 'object'){
8554                 id = Roo.id();
8555                 cellObjects.push({
8556                     container : id,
8557                     cfg : value 
8558                 })
8559             }
8560             
8561             var rowcfg = {
8562                 record: d,
8563                 rowIndex : rowIndex,
8564                 colIndex : i,
8565                 rowClass : ''
8566             };
8567
8568             this.fireEvent('rowclass', this, rowcfg);
8569             
8570             var td = {
8571                 tag: 'td',
8572                 cls : rowcfg.rowClass + ' x-col-' + i,
8573                 style: '',
8574                 html: (typeof(value) === 'object') ? '' : value
8575             };
8576             
8577             if (id) {
8578                 td.id = id;
8579             }
8580             
8581             if(typeof(config.colspan) != 'undefined'){
8582                 td.colspan = config.colspan;
8583             }
8584             
8585             if(typeof(config.hidden) != 'undefined' && config.hidden){
8586                 td.style += ' display:none;';
8587             }
8588             
8589             if(typeof(config.align) != 'undefined' && config.align.length){
8590                 td.style += ' text-align:' + config.align + ';';
8591             }
8592             if(typeof(config.valign) != 'undefined' && config.valign.length){
8593                 td.style += ' vertical-align:' + config.valign + ';';
8594             }
8595             
8596             if(typeof(config.width) != 'undefined'){
8597                 td.style += ' width:' +  config.width + 'px;';
8598             }
8599             
8600             if(typeof(config.cursor) != 'undefined'){
8601                 td.style += ' cursor:' +  config.cursor + ';';
8602             }
8603             
8604             if(typeof(config.cls) != 'undefined'){
8605                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8606             }
8607             
8608             ['xs','sm','md','lg'].map(function(size){
8609                 
8610                 if(typeof(config[size]) == 'undefined'){
8611                     return;
8612                 }
8613                 
8614                 
8615                   
8616                 if (!config[size]) { // 0 = hidden
8617                     // BS 4 '0' is treated as hide that column and below.
8618                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8619                     return;
8620                 }
8621                 
8622                 td.cls += ' col-' + size + '-' + config[size] + (
8623                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8624                 );
8625                  
8626
8627             });
8628             
8629             row.cn.push(td);
8630            
8631         }
8632         
8633         row.cellObjects = cellObjects;
8634         
8635         return row;
8636           
8637     },
8638     
8639     
8640     
8641     onBeforeLoad : function()
8642     {
8643         
8644     },
8645      /**
8646      * Remove all rows
8647      */
8648     clear : function()
8649     {
8650         this.el.select('tbody', true).first().dom.innerHTML = '';
8651     },
8652     /**
8653      * Show or hide a row.
8654      * @param {Number} rowIndex to show or hide
8655      * @param {Boolean} state hide
8656      */
8657     setRowVisibility : function(rowIndex, state)
8658     {
8659         var bt = this.mainBody.dom;
8660         
8661         var rows = this.el.select('tbody > tr', true).elements;
8662         
8663         if(typeof(rows[rowIndex]) == 'undefined'){
8664             return;
8665         }
8666         rows[rowIndex].dom.style.display = state ? '' : 'none';
8667     },
8668     
8669     
8670     getSelectionModel : function(){
8671         if(!this.selModel){
8672             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8673         }
8674         return this.selModel;
8675     },
8676     /*
8677      * Render the Roo.bootstrap object from renderder
8678      */
8679     renderCellObject : function(r)
8680     {
8681         var _this = this;
8682         
8683         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8684         
8685         var t = r.cfg.render(r.container);
8686         
8687         if(r.cfg.cn){
8688             Roo.each(r.cfg.cn, function(c){
8689                 var child = {
8690                     container: t.getChildContainer(),
8691                     cfg: c
8692                 };
8693                 _this.renderCellObject(child);
8694             })
8695         }
8696     },
8697     
8698     getRowIndex : function(row)
8699     {
8700         var rowIndex = -1;
8701         
8702         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8703             if(el != row){
8704                 return;
8705             }
8706             
8707             rowIndex = index;
8708         });
8709         
8710         return rowIndex;
8711     },
8712      /**
8713      * Returns the grid's underlying element = used by panel.Grid
8714      * @return {Element} The element
8715      */
8716     getGridEl : function(){
8717         return this.el;
8718     },
8719      /**
8720      * Forces a resize - used by panel.Grid
8721      * @return {Element} The element
8722      */
8723     autoSize : function()
8724     {
8725         //var ctr = Roo.get(this.container.dom.parentElement);
8726         var ctr = Roo.get(this.el.dom);
8727         
8728         var thd = this.getGridEl().select('thead',true).first();
8729         var tbd = this.getGridEl().select('tbody', true).first();
8730         var tfd = this.getGridEl().select('tfoot', true).first();
8731         
8732         var cw = ctr.getWidth();
8733         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8734         
8735         if (tbd) {
8736             
8737             tbd.setWidth(ctr.getWidth());
8738             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8739             // this needs fixing for various usage - currently only hydra job advers I think..
8740             //tdb.setHeight(
8741             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8742             //); 
8743             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8744             cw -= barsize;
8745         }
8746         cw = Math.max(cw, this.totalWidth);
8747         this.getGridEl().select('tbody tr',true).setWidth(cw);
8748         
8749         // resize 'expandable coloumn?
8750         
8751         return; // we doe not have a view in this design..
8752         
8753     },
8754     onBodyScroll: function()
8755     {
8756         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8757         if(this.mainHead){
8758             this.mainHead.setStyle({
8759                 'position' : 'relative',
8760                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8761             });
8762         }
8763         
8764         if(this.lazyLoad){
8765             
8766             var scrollHeight = this.mainBody.dom.scrollHeight;
8767             
8768             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8769             
8770             var height = this.mainBody.getHeight();
8771             
8772             if(scrollHeight - height == scrollTop) {
8773                 
8774                 var total = this.ds.getTotalCount();
8775                 
8776                 if(this.footer.cursor + this.footer.pageSize < total){
8777                     
8778                     this.footer.ds.load({
8779                         params : {
8780                             start : this.footer.cursor + this.footer.pageSize,
8781                             limit : this.footer.pageSize
8782                         },
8783                         add : true
8784                     });
8785                 }
8786             }
8787             
8788         }
8789     },
8790     
8791     onHeaderChange : function()
8792     {
8793         var header = this.renderHeader();
8794         var table = this.el.select('table', true).first();
8795         
8796         this.mainHead.remove();
8797         this.mainHead = table.createChild(header, this.mainBody, false);
8798     },
8799     
8800     onHiddenChange : function(colModel, colIndex, hidden)
8801     {
8802         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8803         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8804         
8805         this.CSS.updateRule(thSelector, "display", "");
8806         this.CSS.updateRule(tdSelector, "display", "");
8807         
8808         if(hidden){
8809             this.CSS.updateRule(thSelector, "display", "none");
8810             this.CSS.updateRule(tdSelector, "display", "none");
8811         }
8812         
8813         this.onHeaderChange();
8814         this.onLoad();
8815     },
8816     
8817     setColumnWidth: function(col_index, width)
8818     {
8819         // width = "md-2 xs-2..."
8820         if(!this.colModel.config[col_index]) {
8821             return;
8822         }
8823         
8824         var w = width.split(" ");
8825         
8826         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8827         
8828         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8829         
8830         
8831         for(var j = 0; j < w.length; j++) {
8832             
8833             if(!w[j]) {
8834                 continue;
8835             }
8836             
8837             var size_cls = w[j].split("-");
8838             
8839             if(!Number.isInteger(size_cls[1] * 1)) {
8840                 continue;
8841             }
8842             
8843             if(!this.colModel.config[col_index][size_cls[0]]) {
8844                 continue;
8845             }
8846             
8847             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8848                 continue;
8849             }
8850             
8851             h_row[0].classList.replace(
8852                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8853                 "col-"+size_cls[0]+"-"+size_cls[1]
8854             );
8855             
8856             for(var i = 0; i < rows.length; i++) {
8857                 
8858                 var size_cls = w[j].split("-");
8859                 
8860                 if(!Number.isInteger(size_cls[1] * 1)) {
8861                     continue;
8862                 }
8863                 
8864                 if(!this.colModel.config[col_index][size_cls[0]]) {
8865                     continue;
8866                 }
8867                 
8868                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8869                     continue;
8870                 }
8871                 
8872                 rows[i].classList.replace(
8873                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8874                     "col-"+size_cls[0]+"-"+size_cls[1]
8875                 );
8876             }
8877             
8878             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8879         }
8880     }
8881 });
8882
8883  
8884
8885  /*
8886  * - LGPL
8887  *
8888  * table cell
8889  * 
8890  */
8891
8892 /**
8893  * @class Roo.bootstrap.TableCell
8894  * @extends Roo.bootstrap.Component
8895  * Bootstrap TableCell class
8896  * @cfg {String} html cell contain text
8897  * @cfg {String} cls cell class
8898  * @cfg {String} tag cell tag (td|th) default td
8899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8900  * @cfg {String} align Aligns the content in a cell
8901  * @cfg {String} axis Categorizes cells
8902  * @cfg {String} bgcolor Specifies the background color of a cell
8903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8904  * @cfg {Number} colspan Specifies the number of columns a cell should span
8905  * @cfg {String} headers Specifies one or more header cells a cell is related to
8906  * @cfg {Number} height Sets the height of a cell
8907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8908  * @cfg {Number} rowspan Sets the number of rows a cell should span
8909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8910  * @cfg {String} valign Vertical aligns the content in a cell
8911  * @cfg {Number} width Specifies the width of a cell
8912  * 
8913  * @constructor
8914  * Create a new TableCell
8915  * @param {Object} config The config object
8916  */
8917
8918 Roo.bootstrap.TableCell = function(config){
8919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8920 };
8921
8922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8923     
8924     html: false,
8925     cls: false,
8926     tag: false,
8927     abbr: false,
8928     align: false,
8929     axis: false,
8930     bgcolor: false,
8931     charoff: false,
8932     colspan: false,
8933     headers: false,
8934     height: false,
8935     nowrap: false,
8936     rowspan: false,
8937     scope: false,
8938     valign: false,
8939     width: false,
8940     
8941     
8942     getAutoCreate : function(){
8943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8944         
8945         cfg = {
8946             tag: 'td'
8947         };
8948         
8949         if(this.tag){
8950             cfg.tag = this.tag;
8951         }
8952         
8953         if (this.html) {
8954             cfg.html=this.html
8955         }
8956         if (this.cls) {
8957             cfg.cls=this.cls
8958         }
8959         if (this.abbr) {
8960             cfg.abbr=this.abbr
8961         }
8962         if (this.align) {
8963             cfg.align=this.align
8964         }
8965         if (this.axis) {
8966             cfg.axis=this.axis
8967         }
8968         if (this.bgcolor) {
8969             cfg.bgcolor=this.bgcolor
8970         }
8971         if (this.charoff) {
8972             cfg.charoff=this.charoff
8973         }
8974         if (this.colspan) {
8975             cfg.colspan=this.colspan
8976         }
8977         if (this.headers) {
8978             cfg.headers=this.headers
8979         }
8980         if (this.height) {
8981             cfg.height=this.height
8982         }
8983         if (this.nowrap) {
8984             cfg.nowrap=this.nowrap
8985         }
8986         if (this.rowspan) {
8987             cfg.rowspan=this.rowspan
8988         }
8989         if (this.scope) {
8990             cfg.scope=this.scope
8991         }
8992         if (this.valign) {
8993             cfg.valign=this.valign
8994         }
8995         if (this.width) {
8996             cfg.width=this.width
8997         }
8998         
8999         
9000         return cfg;
9001     }
9002    
9003 });
9004
9005  
9006
9007  /*
9008  * - LGPL
9009  *
9010  * table row
9011  * 
9012  */
9013
9014 /**
9015  * @class Roo.bootstrap.TableRow
9016  * @extends Roo.bootstrap.Component
9017  * Bootstrap TableRow class
9018  * @cfg {String} cls row class
9019  * @cfg {String} align Aligns the content in a table row
9020  * @cfg {String} bgcolor Specifies a background color for a table row
9021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9022  * @cfg {String} valign Vertical aligns the content in a table row
9023  * 
9024  * @constructor
9025  * Create a new TableRow
9026  * @param {Object} config The config object
9027  */
9028
9029 Roo.bootstrap.TableRow = function(config){
9030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9031 };
9032
9033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9034     
9035     cls: false,
9036     align: false,
9037     bgcolor: false,
9038     charoff: false,
9039     valign: false,
9040     
9041     getAutoCreate : function(){
9042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9043         
9044         cfg = {
9045             tag: 'tr'
9046         };
9047             
9048         if(this.cls){
9049             cfg.cls = this.cls;
9050         }
9051         if(this.align){
9052             cfg.align = this.align;
9053         }
9054         if(this.bgcolor){
9055             cfg.bgcolor = this.bgcolor;
9056         }
9057         if(this.charoff){
9058             cfg.charoff = this.charoff;
9059         }
9060         if(this.valign){
9061             cfg.valign = this.valign;
9062         }
9063         
9064         return cfg;
9065     }
9066    
9067 });
9068
9069  
9070
9071  /*
9072  * - LGPL
9073  *
9074  * table body
9075  * 
9076  */
9077
9078 /**
9079  * @class Roo.bootstrap.TableBody
9080  * @extends Roo.bootstrap.Component
9081  * Bootstrap TableBody class
9082  * @cfg {String} cls element class
9083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9084  * @cfg {String} align Aligns the content inside the element
9085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9087  * 
9088  * @constructor
9089  * Create a new TableBody
9090  * @param {Object} config The config object
9091  */
9092
9093 Roo.bootstrap.TableBody = function(config){
9094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9095 };
9096
9097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9098     
9099     cls: false,
9100     tag: false,
9101     align: false,
9102     charoff: false,
9103     valign: false,
9104     
9105     getAutoCreate : function(){
9106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9107         
9108         cfg = {
9109             tag: 'tbody'
9110         };
9111             
9112         if (this.cls) {
9113             cfg.cls=this.cls
9114         }
9115         if(this.tag){
9116             cfg.tag = this.tag;
9117         }
9118         
9119         if(this.align){
9120             cfg.align = this.align;
9121         }
9122         if(this.charoff){
9123             cfg.charoff = this.charoff;
9124         }
9125         if(this.valign){
9126             cfg.valign = this.valign;
9127         }
9128         
9129         return cfg;
9130     }
9131     
9132     
9133 //    initEvents : function()
9134 //    {
9135 //        
9136 //        if(!this.store){
9137 //            return;
9138 //        }
9139 //        
9140 //        this.store = Roo.factory(this.store, Roo.data);
9141 //        this.store.on('load', this.onLoad, this);
9142 //        
9143 //        this.store.load();
9144 //        
9145 //    },
9146 //    
9147 //    onLoad: function () 
9148 //    {   
9149 //        this.fireEvent('load', this);
9150 //    }
9151 //    
9152 //   
9153 });
9154
9155  
9156
9157  /*
9158  * Based on:
9159  * Ext JS Library 1.1.1
9160  * Copyright(c) 2006-2007, Ext JS, LLC.
9161  *
9162  * Originally Released Under LGPL - original licence link has changed is not relivant.
9163  *
9164  * Fork - LGPL
9165  * <script type="text/javascript">
9166  */
9167
9168 // as we use this in bootstrap.
9169 Roo.namespace('Roo.form');
9170  /**
9171  * @class Roo.form.Action
9172  * Internal Class used to handle form actions
9173  * @constructor
9174  * @param {Roo.form.BasicForm} el The form element or its id
9175  * @param {Object} config Configuration options
9176  */
9177
9178  
9179  
9180 // define the action interface
9181 Roo.form.Action = function(form, options){
9182     this.form = form;
9183     this.options = options || {};
9184 };
9185 /**
9186  * Client Validation Failed
9187  * @const 
9188  */
9189 Roo.form.Action.CLIENT_INVALID = 'client';
9190 /**
9191  * Server Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.SERVER_INVALID = 'server';
9195  /**
9196  * Connect to Server Failed
9197  * @const 
9198  */
9199 Roo.form.Action.CONNECT_FAILURE = 'connect';
9200 /**
9201  * Reading Data from Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.LOAD_FAILURE = 'load';
9205
9206 Roo.form.Action.prototype = {
9207     type : 'default',
9208     failureType : undefined,
9209     response : undefined,
9210     result : undefined,
9211
9212     // interface method
9213     run : function(options){
9214
9215     },
9216
9217     // interface method
9218     success : function(response){
9219
9220     },
9221
9222     // interface method
9223     handleResponse : function(response){
9224
9225     },
9226
9227     // default connection failure
9228     failure : function(response){
9229         
9230         this.response = response;
9231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9232         this.form.afterAction(this, false);
9233     },
9234
9235     processResponse : function(response){
9236         this.response = response;
9237         if(!response.responseText){
9238             return true;
9239         }
9240         this.result = this.handleResponse(response);
9241         return this.result;
9242     },
9243
9244     // utility functions used internally
9245     getUrl : function(appendParams){
9246         var url = this.options.url || this.form.url || this.form.el.dom.action;
9247         if(appendParams){
9248             var p = this.getParams();
9249             if(p){
9250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9251             }
9252         }
9253         return url;
9254     },
9255
9256     getMethod : function(){
9257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9258     },
9259
9260     getParams : function(){
9261         var bp = this.form.baseParams;
9262         var p = this.options.params;
9263         if(p){
9264             if(typeof p == "object"){
9265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9266             }else if(typeof p == 'string' && bp){
9267                 p += '&' + Roo.urlEncode(bp);
9268             }
9269         }else if(bp){
9270             p = Roo.urlEncode(bp);
9271         }
9272         return p;
9273     },
9274
9275     createCallback : function(){
9276         return {
9277             success: this.success,
9278             failure: this.failure,
9279             scope: this,
9280             timeout: (this.form.timeout*1000),
9281             upload: this.form.fileUpload ? this.success : undefined
9282         };
9283     }
9284 };
9285
9286 Roo.form.Action.Submit = function(form, options){
9287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9288 };
9289
9290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9291     type : 'submit',
9292
9293     haveProgress : false,
9294     uploadComplete : false,
9295     
9296     // uploadProgress indicator.
9297     uploadProgress : function()
9298     {
9299         if (!this.form.progressUrl) {
9300             return;
9301         }
9302         
9303         if (!this.haveProgress) {
9304             Roo.MessageBox.progress("Uploading", "Uploading");
9305         }
9306         if (this.uploadComplete) {
9307            Roo.MessageBox.hide();
9308            return;
9309         }
9310         
9311         this.haveProgress = true;
9312    
9313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9314         
9315         var c = new Roo.data.Connection();
9316         c.request({
9317             url : this.form.progressUrl,
9318             params: {
9319                 id : uid
9320             },
9321             method: 'GET',
9322             success : function(req){
9323                //console.log(data);
9324                 var rdata = false;
9325                 var edata;
9326                 try  {
9327                    rdata = Roo.decode(req.responseText)
9328                 } catch (e) {
9329                     Roo.log("Invalid data from server..");
9330                     Roo.log(edata);
9331                     return;
9332                 }
9333                 if (!rdata || !rdata.success) {
9334                     Roo.log(rdata);
9335                     Roo.MessageBox.alert(Roo.encode(rdata));
9336                     return;
9337                 }
9338                 var data = rdata.data;
9339                 
9340                 if (this.uploadComplete) {
9341                    Roo.MessageBox.hide();
9342                    return;
9343                 }
9344                    
9345                 if (data){
9346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9348                     );
9349                 }
9350                 this.uploadProgress.defer(2000,this);
9351             },
9352        
9353             failure: function(data) {
9354                 Roo.log('progress url failed ');
9355                 Roo.log(data);
9356             },
9357             scope : this
9358         });
9359            
9360     },
9361     
9362     
9363     run : function()
9364     {
9365         // run get Values on the form, so it syncs any secondary forms.
9366         this.form.getValues();
9367         
9368         var o = this.options;
9369         var method = this.getMethod();
9370         var isPost = method == 'POST';
9371         if(o.clientValidation === false || this.form.isValid()){
9372             
9373             if (this.form.progressUrl) {
9374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9375                     (new Date() * 1) + '' + Math.random());
9376                     
9377             } 
9378             
9379             
9380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9381                 form:this.form.el.dom,
9382                 url:this.getUrl(!isPost),
9383                 method: method,
9384                 params:isPost ? this.getParams() : null,
9385                 isUpload: this.form.fileUpload,
9386                 formData : this.form.formData
9387             }));
9388             
9389             this.uploadProgress();
9390
9391         }else if (o.clientValidation !== false){ // client validation failed
9392             this.failureType = Roo.form.Action.CLIENT_INVALID;
9393             this.form.afterAction(this, false);
9394         }
9395     },
9396
9397     success : function(response)
9398     {
9399         this.uploadComplete= true;
9400         if (this.haveProgress) {
9401             Roo.MessageBox.hide();
9402         }
9403         
9404         
9405         var result = this.processResponse(response);
9406         if(result === true || result.success){
9407             this.form.afterAction(this, true);
9408             return;
9409         }
9410         if(result.errors){
9411             this.form.markInvalid(result.errors);
9412             this.failureType = Roo.form.Action.SERVER_INVALID;
9413         }
9414         this.form.afterAction(this, false);
9415     },
9416     failure : function(response)
9417     {
9418         this.uploadComplete= true;
9419         if (this.haveProgress) {
9420             Roo.MessageBox.hide();
9421         }
9422         
9423         this.response = response;
9424         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9425         this.form.afterAction(this, false);
9426     },
9427     
9428     handleResponse : function(response){
9429         if(this.form.errorReader){
9430             var rs = this.form.errorReader.read(response);
9431             var errors = [];
9432             if(rs.records){
9433                 for(var i = 0, len = rs.records.length; i < len; i++) {
9434                     var r = rs.records[i];
9435                     errors[i] = r.data;
9436                 }
9437             }
9438             if(errors.length < 1){
9439                 errors = null;
9440             }
9441             return {
9442                 success : rs.success,
9443                 errors : errors
9444             };
9445         }
9446         var ret = false;
9447         try {
9448             ret = Roo.decode(response.responseText);
9449         } catch (e) {
9450             ret = {
9451                 success: false,
9452                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9453                 errors : []
9454             };
9455         }
9456         return ret;
9457         
9458     }
9459 });
9460
9461
9462 Roo.form.Action.Load = function(form, options){
9463     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9464     this.reader = this.form.reader;
9465 };
9466
9467 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9468     type : 'load',
9469
9470     run : function(){
9471         
9472         Roo.Ajax.request(Roo.apply(
9473                 this.createCallback(), {
9474                     method:this.getMethod(),
9475                     url:this.getUrl(false),
9476                     params:this.getParams()
9477         }));
9478     },
9479
9480     success : function(response){
9481         
9482         var result = this.processResponse(response);
9483         if(result === true || !result.success || !result.data){
9484             this.failureType = Roo.form.Action.LOAD_FAILURE;
9485             this.form.afterAction(this, false);
9486             return;
9487         }
9488         this.form.clearInvalid();
9489         this.form.setValues(result.data);
9490         this.form.afterAction(this, true);
9491     },
9492
9493     handleResponse : function(response){
9494         if(this.form.reader){
9495             var rs = this.form.reader.read(response);
9496             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9497             return {
9498                 success : rs.success,
9499                 data : data
9500             };
9501         }
9502         return Roo.decode(response.responseText);
9503     }
9504 });
9505
9506 Roo.form.Action.ACTION_TYPES = {
9507     'load' : Roo.form.Action.Load,
9508     'submit' : Roo.form.Action.Submit
9509 };/*
9510  * - LGPL
9511  *
9512  * form
9513  *
9514  */
9515
9516 /**
9517  * @class Roo.bootstrap.Form
9518  * @extends Roo.bootstrap.Component
9519  * Bootstrap Form class
9520  * @cfg {String} method  GET | POST (default POST)
9521  * @cfg {String} labelAlign top | left (default top)
9522  * @cfg {String} align left  | right - for navbars
9523  * @cfg {Boolean} loadMask load mask when submit (default true)
9524
9525  *
9526  * @constructor
9527  * Create a new Form
9528  * @param {Object} config The config object
9529  */
9530
9531
9532 Roo.bootstrap.Form = function(config){
9533     
9534     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9535     
9536     Roo.bootstrap.Form.popover.apply();
9537     
9538     this.addEvents({
9539         /**
9540          * @event clientvalidation
9541          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9542          * @param {Form} this
9543          * @param {Boolean} valid true if the form has passed client-side validation
9544          */
9545         clientvalidation: true,
9546         /**
9547          * @event beforeaction
9548          * Fires before any action is performed. Return false to cancel the action.
9549          * @param {Form} this
9550          * @param {Action} action The action to be performed
9551          */
9552         beforeaction: true,
9553         /**
9554          * @event actionfailed
9555          * Fires when an action fails.
9556          * @param {Form} this
9557          * @param {Action} action The action that failed
9558          */
9559         actionfailed : true,
9560         /**
9561          * @event actioncomplete
9562          * Fires when an action is completed.
9563          * @param {Form} this
9564          * @param {Action} action The action that completed
9565          */
9566         actioncomplete : true
9567     });
9568 };
9569
9570 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9571
9572      /**
9573      * @cfg {String} method
9574      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9575      */
9576     method : 'POST',
9577     /**
9578      * @cfg {String} url
9579      * The URL to use for form actions if one isn't supplied in the action options.
9580      */
9581     /**
9582      * @cfg {Boolean} fileUpload
9583      * Set to true if this form is a file upload.
9584      */
9585
9586     /**
9587      * @cfg {Object} baseParams
9588      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9589      */
9590
9591     /**
9592      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9593      */
9594     timeout: 30,
9595     /**
9596      * @cfg {Sting} align (left|right) for navbar forms
9597      */
9598     align : 'left',
9599
9600     // private
9601     activeAction : null,
9602
9603     /**
9604      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9605      * element by passing it or its id or mask the form itself by passing in true.
9606      * @type Mixed
9607      */
9608     waitMsgTarget : false,
9609
9610     loadMask : true,
9611     
9612     /**
9613      * @cfg {Boolean} errorMask (true|false) default false
9614      */
9615     errorMask : false,
9616     
9617     /**
9618      * @cfg {Number} maskOffset Default 100
9619      */
9620     maskOffset : 100,
9621     
9622     /**
9623      * @cfg {Boolean} maskBody
9624      */
9625     maskBody : false,
9626
9627     getAutoCreate : function(){
9628
9629         var cfg = {
9630             tag: 'form',
9631             method : this.method || 'POST',
9632             id : this.id || Roo.id(),
9633             cls : ''
9634         };
9635         if (this.parent().xtype.match(/^Nav/)) {
9636             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9637
9638         }
9639
9640         if (this.labelAlign == 'left' ) {
9641             cfg.cls += ' form-horizontal';
9642         }
9643
9644
9645         return cfg;
9646     },
9647     initEvents : function()
9648     {
9649         this.el.on('submit', this.onSubmit, this);
9650         // this was added as random key presses on the form where triggering form submit.
9651         this.el.on('keypress', function(e) {
9652             if (e.getCharCode() != 13) {
9653                 return true;
9654             }
9655             // we might need to allow it for textareas.. and some other items.
9656             // check e.getTarget().
9657
9658             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9659                 return true;
9660             }
9661
9662             Roo.log("keypress blocked");
9663
9664             e.preventDefault();
9665             return false;
9666         });
9667         
9668     },
9669     // private
9670     onSubmit : function(e){
9671         e.stopEvent();
9672     },
9673
9674      /**
9675      * Returns true if client-side validation on the form is successful.
9676      * @return Boolean
9677      */
9678     isValid : function(){
9679         var items = this.getItems();
9680         var valid = true;
9681         var target = false;
9682         
9683         items.each(function(f){
9684             
9685             if(f.validate()){
9686                 return;
9687             }
9688             
9689             Roo.log('invalid field: ' + f.name);
9690             
9691             valid = false;
9692
9693             if(!target && f.el.isVisible(true)){
9694                 target = f;
9695             }
9696            
9697         });
9698         
9699         if(this.errorMask && !valid){
9700             Roo.bootstrap.Form.popover.mask(this, target);
9701         }
9702         
9703         return valid;
9704     },
9705     
9706     /**
9707      * Returns true if any fields in this form have changed since their original load.
9708      * @return Boolean
9709      */
9710     isDirty : function(){
9711         var dirty = false;
9712         var items = this.getItems();
9713         items.each(function(f){
9714            if(f.isDirty()){
9715                dirty = true;
9716                return false;
9717            }
9718            return true;
9719         });
9720         return dirty;
9721     },
9722      /**
9723      * Performs a predefined action (submit or load) or custom actions you define on this form.
9724      * @param {String} actionName The name of the action type
9725      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9726      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9727      * accept other config options):
9728      * <pre>
9729 Property          Type             Description
9730 ----------------  ---------------  ----------------------------------------------------------------------------------
9731 url               String           The url for the action (defaults to the form's url)
9732 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9733 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9734 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9735                                    validate the form on the client (defaults to false)
9736      * </pre>
9737      * @return {BasicForm} this
9738      */
9739     doAction : function(action, options){
9740         if(typeof action == 'string'){
9741             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9742         }
9743         if(this.fireEvent('beforeaction', this, action) !== false){
9744             this.beforeAction(action);
9745             action.run.defer(100, action);
9746         }
9747         return this;
9748     },
9749
9750     // private
9751     beforeAction : function(action){
9752         var o = action.options;
9753         
9754         if(this.loadMask){
9755             
9756             if(this.maskBody){
9757                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9758             } else {
9759                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9760             }
9761         }
9762         // not really supported yet.. ??
9763
9764         //if(this.waitMsgTarget === true){
9765         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9766         //}else if(this.waitMsgTarget){
9767         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9768         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9769         //}else {
9770         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9771        // }
9772
9773     },
9774
9775     // private
9776     afterAction : function(action, success){
9777         this.activeAction = null;
9778         var o = action.options;
9779
9780         if(this.loadMask){
9781             
9782             if(this.maskBody){
9783                 Roo.get(document.body).unmask();
9784             } else {
9785                 this.el.unmask();
9786             }
9787         }
9788         
9789         //if(this.waitMsgTarget === true){
9790 //            this.el.unmask();
9791         //}else if(this.waitMsgTarget){
9792         //    this.waitMsgTarget.unmask();
9793         //}else{
9794         //    Roo.MessageBox.updateProgress(1);
9795         //    Roo.MessageBox.hide();
9796        // }
9797         //
9798         if(success){
9799             if(o.reset){
9800                 this.reset();
9801             }
9802             Roo.callback(o.success, o.scope, [this, action]);
9803             this.fireEvent('actioncomplete', this, action);
9804
9805         }else{
9806
9807             // failure condition..
9808             // we have a scenario where updates need confirming.
9809             // eg. if a locking scenario exists..
9810             // we look for { errors : { needs_confirm : true }} in the response.
9811             if (
9812                 (typeof(action.result) != 'undefined')  &&
9813                 (typeof(action.result.errors) != 'undefined')  &&
9814                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9815            ){
9816                 var _t = this;
9817                 Roo.log("not supported yet");
9818                  /*
9819
9820                 Roo.MessageBox.confirm(
9821                     "Change requires confirmation",
9822                     action.result.errorMsg,
9823                     function(r) {
9824                         if (r != 'yes') {
9825                             return;
9826                         }
9827                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9828                     }
9829
9830                 );
9831                 */
9832
9833
9834                 return;
9835             }
9836
9837             Roo.callback(o.failure, o.scope, [this, action]);
9838             // show an error message if no failed handler is set..
9839             if (!this.hasListener('actionfailed')) {
9840                 Roo.log("need to add dialog support");
9841                 /*
9842                 Roo.MessageBox.alert("Error",
9843                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9844                         action.result.errorMsg :
9845                         "Saving Failed, please check your entries or try again"
9846                 );
9847                 */
9848             }
9849
9850             this.fireEvent('actionfailed', this, action);
9851         }
9852
9853     },
9854     /**
9855      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9856      * @param {String} id The value to search for
9857      * @return Field
9858      */
9859     findField : function(id){
9860         var items = this.getItems();
9861         var field = items.get(id);
9862         if(!field){
9863              items.each(function(f){
9864                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9865                     field = f;
9866                     return false;
9867                 }
9868                 return true;
9869             });
9870         }
9871         return field || null;
9872     },
9873      /**
9874      * Mark fields in this form invalid in bulk.
9875      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9876      * @return {BasicForm} this
9877      */
9878     markInvalid : function(errors){
9879         if(errors instanceof Array){
9880             for(var i = 0, len = errors.length; i < len; i++){
9881                 var fieldError = errors[i];
9882                 var f = this.findField(fieldError.id);
9883                 if(f){
9884                     f.markInvalid(fieldError.msg);
9885                 }
9886             }
9887         }else{
9888             var field, id;
9889             for(id in errors){
9890                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9891                     field.markInvalid(errors[id]);
9892                 }
9893             }
9894         }
9895         //Roo.each(this.childForms || [], function (f) {
9896         //    f.markInvalid(errors);
9897         //});
9898
9899         return this;
9900     },
9901
9902     /**
9903      * Set values for fields in this form in bulk.
9904      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9905      * @return {BasicForm} this
9906      */
9907     setValues : function(values){
9908         if(values instanceof Array){ // array of objects
9909             for(var i = 0, len = values.length; i < len; i++){
9910                 var v = values[i];
9911                 var f = this.findField(v.id);
9912                 if(f){
9913                     f.setValue(v.value);
9914                     if(this.trackResetOnLoad){
9915                         f.originalValue = f.getValue();
9916                     }
9917                 }
9918             }
9919         }else{ // object hash
9920             var field, id;
9921             for(id in values){
9922                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9923
9924                     if (field.setFromData &&
9925                         field.valueField &&
9926                         field.displayField &&
9927                         // combos' with local stores can
9928                         // be queried via setValue()
9929                         // to set their value..
9930                         (field.store && !field.store.isLocal)
9931                         ) {
9932                         // it's a combo
9933                         var sd = { };
9934                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9935                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9936                         field.setFromData(sd);
9937
9938                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9939                         
9940                         field.setFromData(values);
9941                         
9942                     } else {
9943                         field.setValue(values[id]);
9944                     }
9945
9946
9947                     if(this.trackResetOnLoad){
9948                         field.originalValue = field.getValue();
9949                     }
9950                 }
9951             }
9952         }
9953
9954         //Roo.each(this.childForms || [], function (f) {
9955         //    f.setValues(values);
9956         //});
9957
9958         return this;
9959     },
9960
9961     /**
9962      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9963      * they are returned as an array.
9964      * @param {Boolean} asString
9965      * @return {Object}
9966      */
9967     getValues : function(asString){
9968         //if (this.childForms) {
9969             // copy values from the child forms
9970         //    Roo.each(this.childForms, function (f) {
9971         //        this.setValues(f.getValues());
9972         //    }, this);
9973         //}
9974
9975
9976
9977         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9978         if(asString === true){
9979             return fs;
9980         }
9981         return Roo.urlDecode(fs);
9982     },
9983
9984     /**
9985      * Returns the fields in this form as an object with key/value pairs.
9986      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9987      * @return {Object}
9988      */
9989     getFieldValues : function(with_hidden)
9990     {
9991         var items = this.getItems();
9992         var ret = {};
9993         items.each(function(f){
9994             
9995             if (!f.getName()) {
9996                 return;
9997             }
9998             
9999             var v = f.getValue();
10000             
10001             if (f.inputType =='radio') {
10002                 if (typeof(ret[f.getName()]) == 'undefined') {
10003                     ret[f.getName()] = ''; // empty..
10004                 }
10005
10006                 if (!f.el.dom.checked) {
10007                     return;
10008
10009                 }
10010                 v = f.el.dom.value;
10011
10012             }
10013             
10014             if(f.xtype == 'MoneyField'){
10015                 ret[f.currencyName] = f.getCurrency();
10016             }
10017
10018             // not sure if this supported any more..
10019             if ((typeof(v) == 'object') && f.getRawValue) {
10020                 v = f.getRawValue() ; // dates..
10021             }
10022             // combo boxes where name != hiddenName...
10023             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10024                 ret[f.name] = f.getRawValue();
10025             }
10026             ret[f.getName()] = v;
10027         });
10028
10029         return ret;
10030     },
10031
10032     /**
10033      * Clears all invalid messages in this form.
10034      * @return {BasicForm} this
10035      */
10036     clearInvalid : function(){
10037         var items = this.getItems();
10038
10039         items.each(function(f){
10040            f.clearInvalid();
10041         });
10042
10043         return this;
10044     },
10045
10046     /**
10047      * Resets this form.
10048      * @return {BasicForm} this
10049      */
10050     reset : function(){
10051         var items = this.getItems();
10052         items.each(function(f){
10053             f.reset();
10054         });
10055
10056         Roo.each(this.childForms || [], function (f) {
10057             f.reset();
10058         });
10059
10060
10061         return this;
10062     },
10063     
10064     getItems : function()
10065     {
10066         var r=new Roo.util.MixedCollection(false, function(o){
10067             return o.id || (o.id = Roo.id());
10068         });
10069         var iter = function(el) {
10070             if (el.inputEl) {
10071                 r.add(el);
10072             }
10073             if (!el.items) {
10074                 return;
10075             }
10076             Roo.each(el.items,function(e) {
10077                 iter(e);
10078             });
10079         };
10080
10081         iter(this);
10082         return r;
10083     },
10084     
10085     hideFields : function(items)
10086     {
10087         Roo.each(items, function(i){
10088             
10089             var f = this.findField(i);
10090             
10091             if(!f){
10092                 return;
10093             }
10094             
10095             f.hide();
10096             
10097         }, this);
10098     },
10099     
10100     showFields : function(items)
10101     {
10102         Roo.each(items, function(i){
10103             
10104             var f = this.findField(i);
10105             
10106             if(!f){
10107                 return;
10108             }
10109             
10110             f.show();
10111             
10112         }, this);
10113     }
10114
10115 });
10116
10117 Roo.apply(Roo.bootstrap.Form, {
10118     
10119     popover : {
10120         
10121         padding : 5,
10122         
10123         isApplied : false,
10124         
10125         isMasked : false,
10126         
10127         form : false,
10128         
10129         target : false,
10130         
10131         toolTip : false,
10132         
10133         intervalID : false,
10134         
10135         maskEl : false,
10136         
10137         apply : function()
10138         {
10139             if(this.isApplied){
10140                 return;
10141             }
10142             
10143             this.maskEl = {
10144                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10145                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10146                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10147                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10148             };
10149             
10150             this.maskEl.top.enableDisplayMode("block");
10151             this.maskEl.left.enableDisplayMode("block");
10152             this.maskEl.bottom.enableDisplayMode("block");
10153             this.maskEl.right.enableDisplayMode("block");
10154             
10155             this.toolTip = new Roo.bootstrap.Tooltip({
10156                 cls : 'roo-form-error-popover',
10157                 alignment : {
10158                     'left' : ['r-l', [-2,0], 'right'],
10159                     'right' : ['l-r', [2,0], 'left'],
10160                     'bottom' : ['tl-bl', [0,2], 'top'],
10161                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10162                 }
10163             });
10164             
10165             this.toolTip.render(Roo.get(document.body));
10166
10167             this.toolTip.el.enableDisplayMode("block");
10168             
10169             Roo.get(document.body).on('click', function(){
10170                 this.unmask();
10171             }, this);
10172             
10173             Roo.get(document.body).on('touchstart', function(){
10174                 this.unmask();
10175             }, this);
10176             
10177             this.isApplied = true
10178         },
10179         
10180         mask : function(form, target)
10181         {
10182             this.form = form;
10183             
10184             this.target = target;
10185             
10186             if(!this.form.errorMask || !target.el){
10187                 return;
10188             }
10189             
10190             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10191             
10192             Roo.log(scrollable);
10193             
10194             var ot = this.target.el.calcOffsetsTo(scrollable);
10195             
10196             var scrollTo = ot[1] - this.form.maskOffset;
10197             
10198             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10199             
10200             scrollable.scrollTo('top', scrollTo);
10201             
10202             var box = this.target.el.getBox();
10203             Roo.log(box);
10204             var zIndex = Roo.bootstrap.Modal.zIndex++;
10205
10206             
10207             this.maskEl.top.setStyle('position', 'absolute');
10208             this.maskEl.top.setStyle('z-index', zIndex);
10209             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10210             this.maskEl.top.setLeft(0);
10211             this.maskEl.top.setTop(0);
10212             this.maskEl.top.show();
10213             
10214             this.maskEl.left.setStyle('position', 'absolute');
10215             this.maskEl.left.setStyle('z-index', zIndex);
10216             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10217             this.maskEl.left.setLeft(0);
10218             this.maskEl.left.setTop(box.y - this.padding);
10219             this.maskEl.left.show();
10220
10221             this.maskEl.bottom.setStyle('position', 'absolute');
10222             this.maskEl.bottom.setStyle('z-index', zIndex);
10223             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10224             this.maskEl.bottom.setLeft(0);
10225             this.maskEl.bottom.setTop(box.bottom + this.padding);
10226             this.maskEl.bottom.show();
10227
10228             this.maskEl.right.setStyle('position', 'absolute');
10229             this.maskEl.right.setStyle('z-index', zIndex);
10230             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10231             this.maskEl.right.setLeft(box.right + this.padding);
10232             this.maskEl.right.setTop(box.y - this.padding);
10233             this.maskEl.right.show();
10234
10235             this.toolTip.bindEl = this.target.el;
10236
10237             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10238
10239             var tip = this.target.blankText;
10240
10241             if(this.target.getValue() !== '' ) {
10242                 
10243                 if (this.target.invalidText.length) {
10244                     tip = this.target.invalidText;
10245                 } else if (this.target.regexText.length){
10246                     tip = this.target.regexText;
10247                 }
10248             }
10249
10250             this.toolTip.show(tip);
10251
10252             this.intervalID = window.setInterval(function() {
10253                 Roo.bootstrap.Form.popover.unmask();
10254             }, 10000);
10255
10256             window.onwheel = function(){ return false;};
10257             
10258             (function(){ this.isMasked = true; }).defer(500, this);
10259             
10260         },
10261         
10262         unmask : function()
10263         {
10264             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10265                 return;
10266             }
10267             
10268             this.maskEl.top.setStyle('position', 'absolute');
10269             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10270             this.maskEl.top.hide();
10271
10272             this.maskEl.left.setStyle('position', 'absolute');
10273             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10274             this.maskEl.left.hide();
10275
10276             this.maskEl.bottom.setStyle('position', 'absolute');
10277             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.bottom.hide();
10279
10280             this.maskEl.right.setStyle('position', 'absolute');
10281             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.right.hide();
10283             
10284             this.toolTip.hide();
10285             
10286             this.toolTip.el.hide();
10287             
10288             window.onwheel = function(){ return true;};
10289             
10290             if(this.intervalID){
10291                 window.clearInterval(this.intervalID);
10292                 this.intervalID = false;
10293             }
10294             
10295             this.isMasked = false;
10296             
10297         }
10298         
10299     }
10300     
10301 });
10302
10303 /*
10304  * Based on:
10305  * Ext JS Library 1.1.1
10306  * Copyright(c) 2006-2007, Ext JS, LLC.
10307  *
10308  * Originally Released Under LGPL - original licence link has changed is not relivant.
10309  *
10310  * Fork - LGPL
10311  * <script type="text/javascript">
10312  */
10313 /**
10314  * @class Roo.form.VTypes
10315  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10316  * @singleton
10317  */
10318 Roo.form.VTypes = function(){
10319     // closure these in so they are only created once.
10320     var alpha = /^[a-zA-Z_]+$/;
10321     var alphanum = /^[a-zA-Z0-9_]+$/;
10322     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10323     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10324
10325     // All these messages and functions are configurable
10326     return {
10327         /**
10328          * The function used to validate email addresses
10329          * @param {String} value The email address
10330          */
10331         'email' : function(v){
10332             return email.test(v);
10333         },
10334         /**
10335          * The error text to display when the email validation function returns false
10336          * @type String
10337          */
10338         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10339         /**
10340          * The keystroke filter mask to be applied on email input
10341          * @type RegExp
10342          */
10343         'emailMask' : /[a-z0-9_\.\-@]/i,
10344
10345         /**
10346          * The function used to validate URLs
10347          * @param {String} value The URL
10348          */
10349         'url' : function(v){
10350             return url.test(v);
10351         },
10352         /**
10353          * The error text to display when the url validation function returns false
10354          * @type String
10355          */
10356         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10357         
10358         /**
10359          * The function used to validate alpha values
10360          * @param {String} value The value
10361          */
10362         'alpha' : function(v){
10363             return alpha.test(v);
10364         },
10365         /**
10366          * The error text to display when the alpha validation function returns false
10367          * @type String
10368          */
10369         'alphaText' : 'This field should only contain letters and _',
10370         /**
10371          * The keystroke filter mask to be applied on alpha input
10372          * @type RegExp
10373          */
10374         'alphaMask' : /[a-z_]/i,
10375
10376         /**
10377          * The function used to validate alphanumeric values
10378          * @param {String} value The value
10379          */
10380         'alphanum' : function(v){
10381             return alphanum.test(v);
10382         },
10383         /**
10384          * The error text to display when the alphanumeric validation function returns false
10385          * @type String
10386          */
10387         'alphanumText' : 'This field should only contain letters, numbers and _',
10388         /**
10389          * The keystroke filter mask to be applied on alphanumeric input
10390          * @type RegExp
10391          */
10392         'alphanumMask' : /[a-z0-9_]/i
10393     };
10394 }();/*
10395  * - LGPL
10396  *
10397  * Input
10398  * 
10399  */
10400
10401 /**
10402  * @class Roo.bootstrap.Input
10403  * @extends Roo.bootstrap.Component
10404  * Bootstrap Input class
10405  * @cfg {Boolean} disabled is it disabled
10406  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10407  * @cfg {String} name name of the input
10408  * @cfg {string} fieldLabel - the label associated
10409  * @cfg {string} placeholder - placeholder to put in text.
10410  * @cfg {string}  before - input group add on before
10411  * @cfg {string} after - input group add on after
10412  * @cfg {string} size - (lg|sm) or leave empty..
10413  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10414  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10415  * @cfg {Number} md colspan out of 12 for computer-sized screens
10416  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10417  * @cfg {string} value default value of the input
10418  * @cfg {Number} labelWidth set the width of label 
10419  * @cfg {Number} labellg set the width of label (1-12)
10420  * @cfg {Number} labelmd set the width of label (1-12)
10421  * @cfg {Number} labelsm set the width of label (1-12)
10422  * @cfg {Number} labelxs set the width of label (1-12)
10423  * @cfg {String} labelAlign (top|left)
10424  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10425  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10426  * @cfg {String} indicatorpos (left|right) default left
10427  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10428  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10429  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10430
10431  * @cfg {String} align (left|center|right) Default left
10432  * @cfg {Boolean} forceFeedback (true|false) Default false
10433  * 
10434  * @constructor
10435  * Create a new Input
10436  * @param {Object} config The config object
10437  */
10438
10439 Roo.bootstrap.Input = function(config){
10440     
10441     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10442     
10443     this.addEvents({
10444         /**
10445          * @event focus
10446          * Fires when this field receives input focus.
10447          * @param {Roo.form.Field} this
10448          */
10449         focus : true,
10450         /**
10451          * @event blur
10452          * Fires when this field loses input focus.
10453          * @param {Roo.form.Field} this
10454          */
10455         blur : true,
10456         /**
10457          * @event specialkey
10458          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10459          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10460          * @param {Roo.form.Field} this
10461          * @param {Roo.EventObject} e The event object
10462          */
10463         specialkey : true,
10464         /**
10465          * @event change
10466          * Fires just before the field blurs if the field value has changed.
10467          * @param {Roo.form.Field} this
10468          * @param {Mixed} newValue The new value
10469          * @param {Mixed} oldValue The original value
10470          */
10471         change : true,
10472         /**
10473          * @event invalid
10474          * Fires after the field has been marked as invalid.
10475          * @param {Roo.form.Field} this
10476          * @param {String} msg The validation message
10477          */
10478         invalid : true,
10479         /**
10480          * @event valid
10481          * Fires after the field has been validated with no errors.
10482          * @param {Roo.form.Field} this
10483          */
10484         valid : true,
10485          /**
10486          * @event keyup
10487          * Fires after the key up
10488          * @param {Roo.form.Field} this
10489          * @param {Roo.EventObject}  e The event Object
10490          */
10491         keyup : true
10492     });
10493 };
10494
10495 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10496      /**
10497      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10498       automatic validation (defaults to "keyup").
10499      */
10500     validationEvent : "keyup",
10501      /**
10502      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10503      */
10504     validateOnBlur : true,
10505     /**
10506      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10507      */
10508     validationDelay : 250,
10509      /**
10510      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10511      */
10512     focusClass : "x-form-focus",  // not needed???
10513     
10514        
10515     /**
10516      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10517      */
10518     invalidClass : "has-warning",
10519     
10520     /**
10521      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     validClass : "has-success",
10524     
10525     /**
10526      * @cfg {Boolean} hasFeedback (true|false) default true
10527      */
10528     hasFeedback : true,
10529     
10530     /**
10531      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10532      */
10533     invalidFeedbackClass : "glyphicon-warning-sign",
10534     
10535     /**
10536      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     validFeedbackClass : "glyphicon-ok",
10539     
10540     /**
10541      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10542      */
10543     selectOnFocus : false,
10544     
10545      /**
10546      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10547      */
10548     maskRe : null,
10549        /**
10550      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10551      */
10552     vtype : null,
10553     
10554       /**
10555      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10556      */
10557     disableKeyFilter : false,
10558     
10559        /**
10560      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10561      */
10562     disabled : false,
10563      /**
10564      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10565      */
10566     allowBlank : true,
10567     /**
10568      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10569      */
10570     blankText : "Please complete this mandatory field",
10571     
10572      /**
10573      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10574      */
10575     minLength : 0,
10576     /**
10577      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10578      */
10579     maxLength : Number.MAX_VALUE,
10580     /**
10581      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10582      */
10583     minLengthText : "The minimum length for this field is {0}",
10584     /**
10585      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10586      */
10587     maxLengthText : "The maximum length for this field is {0}",
10588   
10589     
10590     /**
10591      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10592      * If available, this function will be called only after the basic validators all return true, and will be passed the
10593      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10594      */
10595     validator : null,
10596     /**
10597      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10598      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10599      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10600      */
10601     regex : null,
10602     /**
10603      * @cfg {String} regexText -- Depricated - use Invalid Text
10604      */
10605     regexText : "",
10606     
10607     /**
10608      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10609      */
10610     invalidText : "",
10611     
10612     
10613     
10614     autocomplete: false,
10615     
10616     
10617     fieldLabel : '',
10618     inputType : 'text',
10619     
10620     name : false,
10621     placeholder: false,
10622     before : false,
10623     after : false,
10624     size : false,
10625     hasFocus : false,
10626     preventMark: false,
10627     isFormField : true,
10628     value : '',
10629     labelWidth : 2,
10630     labelAlign : false,
10631     readOnly : false,
10632     align : false,
10633     formatedValue : false,
10634     forceFeedback : false,
10635     
10636     indicatorpos : 'left',
10637     
10638     labellg : 0,
10639     labelmd : 0,
10640     labelsm : 0,
10641     labelxs : 0,
10642     
10643     capture : '',
10644     accept : '',
10645     
10646     parentLabelAlign : function()
10647     {
10648         var parent = this;
10649         while (parent.parent()) {
10650             parent = parent.parent();
10651             if (typeof(parent.labelAlign) !='undefined') {
10652                 return parent.labelAlign;
10653             }
10654         }
10655         return 'left';
10656         
10657     },
10658     
10659     getAutoCreate : function()
10660     {
10661         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10662         
10663         var id = Roo.id();
10664         
10665         var cfg = {};
10666         
10667         if(this.inputType != 'hidden'){
10668             cfg.cls = 'form-group' //input-group
10669         }
10670         
10671         var input =  {
10672             tag: 'input',
10673             id : id,
10674             type : this.inputType,
10675             value : this.value,
10676             cls : 'form-control',
10677             placeholder : this.placeholder || '',
10678             autocomplete : this.autocomplete || 'new-password'
10679         };
10680         if (this.inputType == 'file') {
10681             input.style = 'overflow:hidden'; // why not in CSS?
10682         }
10683         
10684         if(this.capture.length){
10685             input.capture = this.capture;
10686         }
10687         
10688         if(this.accept.length){
10689             input.accept = this.accept + "/*";
10690         }
10691         
10692         if(this.align){
10693             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10694         }
10695         
10696         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10697             input.maxLength = this.maxLength;
10698         }
10699         
10700         if (this.disabled) {
10701             input.disabled=true;
10702         }
10703         
10704         if (this.readOnly) {
10705             input.readonly=true;
10706         }
10707         
10708         if (this.name) {
10709             input.name = this.name;
10710         }
10711         
10712         if (this.size) {
10713             input.cls += ' input-' + this.size;
10714         }
10715         
10716         var settings=this;
10717         ['xs','sm','md','lg'].map(function(size){
10718             if (settings[size]) {
10719                 cfg.cls += ' col-' + size + '-' + settings[size];
10720             }
10721         });
10722         
10723         var inputblock = input;
10724         
10725         var feedback = {
10726             tag: 'span',
10727             cls: 'glyphicon form-control-feedback'
10728         };
10729             
10730         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10731             
10732             inputblock = {
10733                 cls : 'has-feedback',
10734                 cn :  [
10735                     input,
10736                     feedback
10737                 ] 
10738             };  
10739         }
10740         
10741         if (this.before || this.after) {
10742             
10743             inputblock = {
10744                 cls : 'input-group',
10745                 cn :  [] 
10746             };
10747             
10748             if (this.before && typeof(this.before) == 'string') {
10749                 
10750                 inputblock.cn.push({
10751                     tag :'span',
10752                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10753                     html : this.before
10754                 });
10755             }
10756             if (this.before && typeof(this.before) == 'object') {
10757                 this.before = Roo.factory(this.before);
10758                 
10759                 inputblock.cn.push({
10760                     tag :'span',
10761                     cls : 'roo-input-before input-group-prepend   input-group-' +
10762                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10763                 });
10764             }
10765             
10766             inputblock.cn.push(input);
10767             
10768             if (this.after && typeof(this.after) == 'string') {
10769                 inputblock.cn.push({
10770                     tag :'span',
10771                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10772                     html : this.after
10773                 });
10774             }
10775             if (this.after && typeof(this.after) == 'object') {
10776                 this.after = Roo.factory(this.after);
10777                 
10778                 inputblock.cn.push({
10779                     tag :'span',
10780                     cls : 'roo-input-after input-group-append  input-group-' +
10781                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10782                 });
10783             }
10784             
10785             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10786                 inputblock.cls += ' has-feedback';
10787                 inputblock.cn.push(feedback);
10788             }
10789         };
10790         var indicator = {
10791             tag : 'i',
10792             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10793             tooltip : 'This field is required'
10794         };
10795         if (this.allowBlank ) {
10796             indicator.style = this.allowBlank ? ' display:none' : '';
10797         }
10798         if (align ==='left' && this.fieldLabel.length) {
10799             
10800             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10801             
10802             cfg.cn = [
10803                 indicator,
10804                 {
10805                     tag: 'label',
10806                     'for' :  id,
10807                     cls : 'control-label col-form-label',
10808                     html : this.fieldLabel
10809
10810                 },
10811                 {
10812                     cls : "", 
10813                     cn: [
10814                         inputblock
10815                     ]
10816                 }
10817             ];
10818             
10819             var labelCfg = cfg.cn[1];
10820             var contentCfg = cfg.cn[2];
10821             
10822             if(this.indicatorpos == 'right'){
10823                 cfg.cn = [
10824                     {
10825                         tag: 'label',
10826                         'for' :  id,
10827                         cls : 'control-label col-form-label',
10828                         cn : [
10829                             {
10830                                 tag : 'span',
10831                                 html : this.fieldLabel
10832                             },
10833                             indicator
10834                         ]
10835                     },
10836                     {
10837                         cls : "",
10838                         cn: [
10839                             inputblock
10840                         ]
10841                     }
10842
10843                 ];
10844                 
10845                 labelCfg = cfg.cn[0];
10846                 contentCfg = cfg.cn[1];
10847             
10848             }
10849             
10850             if(this.labelWidth > 12){
10851                 labelCfg.style = "width: " + this.labelWidth + 'px';
10852             }
10853             
10854             if(this.labelWidth < 13 && this.labelmd == 0){
10855                 this.labelmd = this.labelWidth;
10856             }
10857             
10858             if(this.labellg > 0){
10859                 labelCfg.cls += ' col-lg-' + this.labellg;
10860                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10861             }
10862             
10863             if(this.labelmd > 0){
10864                 labelCfg.cls += ' col-md-' + this.labelmd;
10865                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10866             }
10867             
10868             if(this.labelsm > 0){
10869                 labelCfg.cls += ' col-sm-' + this.labelsm;
10870                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10871             }
10872             
10873             if(this.labelxs > 0){
10874                 labelCfg.cls += ' col-xs-' + this.labelxs;
10875                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10876             }
10877             
10878             
10879         } else if ( this.fieldLabel.length) {
10880                 
10881             
10882             
10883             cfg.cn = [
10884                 {
10885                     tag : 'i',
10886                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10887                     tooltip : 'This field is required',
10888                     style : this.allowBlank ? ' display:none' : '' 
10889                 },
10890                 {
10891                     tag: 'label',
10892                    //cls : 'input-group-addon',
10893                     html : this.fieldLabel
10894
10895                 },
10896
10897                inputblock
10898
10899            ];
10900            
10901            if(this.indicatorpos == 'right'){
10902        
10903                 cfg.cn = [
10904                     {
10905                         tag: 'label',
10906                        //cls : 'input-group-addon',
10907                         html : this.fieldLabel
10908
10909                     },
10910                     {
10911                         tag : 'i',
10912                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10913                         tooltip : 'This field is required',
10914                         style : this.allowBlank ? ' display:none' : '' 
10915                     },
10916
10917                    inputblock
10918
10919                ];
10920
10921             }
10922
10923         } else {
10924             
10925             cfg.cn = [
10926
10927                     inputblock
10928
10929             ];
10930                 
10931                 
10932         };
10933         
10934         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10935            cfg.cls += ' navbar-form';
10936         }
10937         
10938         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10939             // on BS4 we do this only if not form 
10940             cfg.cls += ' navbar-form';
10941             cfg.tag = 'li';
10942         }
10943         
10944         return cfg;
10945         
10946     },
10947     /**
10948      * return the real input element.
10949      */
10950     inputEl: function ()
10951     {
10952         return this.el.select('input.form-control',true).first();
10953     },
10954     
10955     tooltipEl : function()
10956     {
10957         return this.inputEl();
10958     },
10959     
10960     indicatorEl : function()
10961     {
10962         if (Roo.bootstrap.version == 4) {
10963             return false; // not enabled in v4 yet.
10964         }
10965         
10966         var indicator = this.el.select('i.roo-required-indicator',true).first();
10967         
10968         if(!indicator){
10969             return false;
10970         }
10971         
10972         return indicator;
10973         
10974     },
10975     
10976     setDisabled : function(v)
10977     {
10978         var i  = this.inputEl().dom;
10979         if (!v) {
10980             i.removeAttribute('disabled');
10981             return;
10982             
10983         }
10984         i.setAttribute('disabled','true');
10985     },
10986     initEvents : function()
10987     {
10988           
10989         this.inputEl().on("keydown" , this.fireKey,  this);
10990         this.inputEl().on("focus", this.onFocus,  this);
10991         this.inputEl().on("blur", this.onBlur,  this);
10992         
10993         this.inputEl().relayEvent('keyup', this);
10994         
10995         this.indicator = this.indicatorEl();
10996         
10997         if(this.indicator){
10998             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10999         }
11000  
11001         // reference to original value for reset
11002         this.originalValue = this.getValue();
11003         //Roo.form.TextField.superclass.initEvents.call(this);
11004         if(this.validationEvent == 'keyup'){
11005             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11006             this.inputEl().on('keyup', this.filterValidation, this);
11007         }
11008         else if(this.validationEvent !== false){
11009             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11010         }
11011         
11012         if(this.selectOnFocus){
11013             this.on("focus", this.preFocus, this);
11014             
11015         }
11016         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11017             this.inputEl().on("keypress", this.filterKeys, this);
11018         } else {
11019             this.inputEl().relayEvent('keypress', this);
11020         }
11021        /* if(this.grow){
11022             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11023             this.el.on("click", this.autoSize,  this);
11024         }
11025         */
11026         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11027             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11028         }
11029         
11030         if (typeof(this.before) == 'object') {
11031             this.before.render(this.el.select('.roo-input-before',true).first());
11032         }
11033         if (typeof(this.after) == 'object') {
11034             this.after.render(this.el.select('.roo-input-after',true).first());
11035         }
11036         
11037         this.inputEl().on('change', this.onChange, this);
11038         
11039     },
11040     filterValidation : function(e){
11041         if(!e.isNavKeyPress()){
11042             this.validationTask.delay(this.validationDelay);
11043         }
11044     },
11045      /**
11046      * Validates the field value
11047      * @return {Boolean} True if the value is valid, else false
11048      */
11049     validate : function(){
11050         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11051         if(this.disabled || this.validateValue(this.getRawValue())){
11052             this.markValid();
11053             return true;
11054         }
11055         
11056         this.markInvalid();
11057         return false;
11058     },
11059     
11060     
11061     /**
11062      * Validates a value according to the field's validation rules and marks the field as invalid
11063      * if the validation fails
11064      * @param {Mixed} value The value to validate
11065      * @return {Boolean} True if the value is valid, else false
11066      */
11067     validateValue : function(value)
11068     {
11069         if(this.getVisibilityEl().hasClass('hidden')){
11070             return true;
11071         }
11072         
11073         if(value.length < 1)  { // if it's blank
11074             if(this.allowBlank){
11075                 return true;
11076             }
11077             return false;
11078         }
11079         
11080         if(value.length < this.minLength){
11081             return false;
11082         }
11083         if(value.length > this.maxLength){
11084             return false;
11085         }
11086         if(this.vtype){
11087             var vt = Roo.form.VTypes;
11088             if(!vt[this.vtype](value, this)){
11089                 return false;
11090             }
11091         }
11092         if(typeof this.validator == "function"){
11093             var msg = this.validator(value);
11094             if(msg !== true){
11095                 return false;
11096             }
11097             if (typeof(msg) == 'string') {
11098                 this.invalidText = msg;
11099             }
11100         }
11101         
11102         if(this.regex && !this.regex.test(value)){
11103             return false;
11104         }
11105         
11106         return true;
11107     },
11108     
11109      // private
11110     fireKey : function(e){
11111         //Roo.log('field ' + e.getKey());
11112         if(e.isNavKeyPress()){
11113             this.fireEvent("specialkey", this, e);
11114         }
11115     },
11116     focus : function (selectText){
11117         if(this.rendered){
11118             this.inputEl().focus();
11119             if(selectText === true){
11120                 this.inputEl().dom.select();
11121             }
11122         }
11123         return this;
11124     } ,
11125     
11126     onFocus : function(){
11127         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11128            // this.el.addClass(this.focusClass);
11129         }
11130         if(!this.hasFocus){
11131             this.hasFocus = true;
11132             this.startValue = this.getValue();
11133             this.fireEvent("focus", this);
11134         }
11135     },
11136     
11137     beforeBlur : Roo.emptyFn,
11138
11139     
11140     // private
11141     onBlur : function(){
11142         this.beforeBlur();
11143         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11144             //this.el.removeClass(this.focusClass);
11145         }
11146         this.hasFocus = false;
11147         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11148             this.validate();
11149         }
11150         var v = this.getValue();
11151         if(String(v) !== String(this.startValue)){
11152             this.fireEvent('change', this, v, this.startValue);
11153         }
11154         this.fireEvent("blur", this);
11155     },
11156     
11157     onChange : function(e)
11158     {
11159         var v = this.getValue();
11160         if(String(v) !== String(this.startValue)){
11161             this.fireEvent('change', this, v, this.startValue);
11162         }
11163         
11164     },
11165     
11166     /**
11167      * Resets the current field value to the originally loaded value and clears any validation messages
11168      */
11169     reset : function(){
11170         this.setValue(this.originalValue);
11171         this.validate();
11172     },
11173      /**
11174      * Returns the name of the field
11175      * @return {Mixed} name The name field
11176      */
11177     getName: function(){
11178         return this.name;
11179     },
11180      /**
11181      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11182      * @return {Mixed} value The field value
11183      */
11184     getValue : function(){
11185         
11186         var v = this.inputEl().getValue();
11187         
11188         return v;
11189     },
11190     /**
11191      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11192      * @return {Mixed} value The field value
11193      */
11194     getRawValue : function(){
11195         var v = this.inputEl().getValue();
11196         
11197         return v;
11198     },
11199     
11200     /**
11201      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11202      * @param {Mixed} value The value to set
11203      */
11204     setRawValue : function(v){
11205         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11206     },
11207     
11208     selectText : function(start, end){
11209         var v = this.getRawValue();
11210         if(v.length > 0){
11211             start = start === undefined ? 0 : start;
11212             end = end === undefined ? v.length : end;
11213             var d = this.inputEl().dom;
11214             if(d.setSelectionRange){
11215                 d.setSelectionRange(start, end);
11216             }else if(d.createTextRange){
11217                 var range = d.createTextRange();
11218                 range.moveStart("character", start);
11219                 range.moveEnd("character", v.length-end);
11220                 range.select();
11221             }
11222         }
11223     },
11224     
11225     /**
11226      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11227      * @param {Mixed} value The value to set
11228      */
11229     setValue : function(v){
11230         this.value = v;
11231         if(this.rendered){
11232             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11233             this.validate();
11234         }
11235     },
11236     
11237     /*
11238     processValue : function(value){
11239         if(this.stripCharsRe){
11240             var newValue = value.replace(this.stripCharsRe, '');
11241             if(newValue !== value){
11242                 this.setRawValue(newValue);
11243                 return newValue;
11244             }
11245         }
11246         return value;
11247     },
11248   */
11249     preFocus : function(){
11250         
11251         if(this.selectOnFocus){
11252             this.inputEl().dom.select();
11253         }
11254     },
11255     filterKeys : function(e){
11256         var k = e.getKey();
11257         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11258             return;
11259         }
11260         var c = e.getCharCode(), cc = String.fromCharCode(c);
11261         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11262             return;
11263         }
11264         if(!this.maskRe.test(cc)){
11265             e.stopEvent();
11266         }
11267     },
11268      /**
11269      * Clear any invalid styles/messages for this field
11270      */
11271     clearInvalid : function(){
11272         
11273         if(!this.el || this.preventMark){ // not rendered
11274             return;
11275         }
11276         
11277         
11278         this.el.removeClass([this.invalidClass, 'is-invalid']);
11279         
11280         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11281             
11282             var feedback = this.el.select('.form-control-feedback', true).first();
11283             
11284             if(feedback){
11285                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11286             }
11287             
11288         }
11289         
11290         if(this.indicator){
11291             this.indicator.removeClass('visible');
11292             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11293         }
11294         
11295         this.fireEvent('valid', this);
11296     },
11297     
11298      /**
11299      * Mark this field as valid
11300      */
11301     markValid : function()
11302     {
11303         if(!this.el  || this.preventMark){ // not rendered...
11304             return;
11305         }
11306         
11307         this.el.removeClass([this.invalidClass, this.validClass]);
11308         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11309
11310         var feedback = this.el.select('.form-control-feedback', true).first();
11311             
11312         if(feedback){
11313             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11314         }
11315         
11316         if(this.indicator){
11317             this.indicator.removeClass('visible');
11318             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11319         }
11320         
11321         if(this.disabled){
11322             return;
11323         }
11324         
11325            
11326         if(this.allowBlank && !this.getRawValue().length){
11327             return;
11328         }
11329         if (Roo.bootstrap.version == 3) {
11330             this.el.addClass(this.validClass);
11331         } else {
11332             this.inputEl().addClass('is-valid');
11333         }
11334
11335         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11336             
11337             var feedback = this.el.select('.form-control-feedback', true).first();
11338             
11339             if(feedback){
11340                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11341                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11342             }
11343             
11344         }
11345         
11346         this.fireEvent('valid', this);
11347     },
11348     
11349      /**
11350      * Mark this field as invalid
11351      * @param {String} msg The validation message
11352      */
11353     markInvalid : function(msg)
11354     {
11355         if(!this.el  || this.preventMark){ // not rendered
11356             return;
11357         }
11358         
11359         this.el.removeClass([this.invalidClass, this.validClass]);
11360         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11361         
11362         var feedback = this.el.select('.form-control-feedback', true).first();
11363             
11364         if(feedback){
11365             this.el.select('.form-control-feedback', true).first().removeClass(
11366                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11367         }
11368
11369         if(this.disabled){
11370             return;
11371         }
11372         
11373         if(this.allowBlank && !this.getRawValue().length){
11374             return;
11375         }
11376         
11377         if(this.indicator){
11378             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11379             this.indicator.addClass('visible');
11380         }
11381         if (Roo.bootstrap.version == 3) {
11382             this.el.addClass(this.invalidClass);
11383         } else {
11384             this.inputEl().addClass('is-invalid');
11385         }
11386         
11387         
11388         
11389         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11390             
11391             var feedback = this.el.select('.form-control-feedback', true).first();
11392             
11393             if(feedback){
11394                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11395                 
11396                 if(this.getValue().length || this.forceFeedback){
11397                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11398                 }
11399                 
11400             }
11401             
11402         }
11403         
11404         this.fireEvent('invalid', this, msg);
11405     },
11406     // private
11407     SafariOnKeyDown : function(event)
11408     {
11409         // this is a workaround for a password hang bug on chrome/ webkit.
11410         if (this.inputEl().dom.type != 'password') {
11411             return;
11412         }
11413         
11414         var isSelectAll = false;
11415         
11416         if(this.inputEl().dom.selectionEnd > 0){
11417             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11418         }
11419         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11420             event.preventDefault();
11421             this.setValue('');
11422             return;
11423         }
11424         
11425         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11426             
11427             event.preventDefault();
11428             // this is very hacky as keydown always get's upper case.
11429             //
11430             var cc = String.fromCharCode(event.getCharCode());
11431             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11432             
11433         }
11434     },
11435     adjustWidth : function(tag, w){
11436         tag = tag.toLowerCase();
11437         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11438             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11439                 if(tag == 'input'){
11440                     return w + 2;
11441                 }
11442                 if(tag == 'textarea'){
11443                     return w-2;
11444                 }
11445             }else if(Roo.isOpera){
11446                 if(tag == 'input'){
11447                     return w + 2;
11448                 }
11449                 if(tag == 'textarea'){
11450                     return w-2;
11451                 }
11452             }
11453         }
11454         return w;
11455     },
11456     
11457     setFieldLabel : function(v)
11458     {
11459         if(!this.rendered){
11460             return;
11461         }
11462         
11463         if(this.indicatorEl()){
11464             var ar = this.el.select('label > span',true);
11465             
11466             if (ar.elements.length) {
11467                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11468                 this.fieldLabel = v;
11469                 return;
11470             }
11471             
11472             var br = this.el.select('label',true);
11473             
11474             if(br.elements.length) {
11475                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476                 this.fieldLabel = v;
11477                 return;
11478             }
11479             
11480             Roo.log('Cannot Found any of label > span || label in input');
11481             return;
11482         }
11483         
11484         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11485         this.fieldLabel = v;
11486         
11487         
11488     }
11489 });
11490
11491  
11492 /*
11493  * - LGPL
11494  *
11495  * Input
11496  * 
11497  */
11498
11499 /**
11500  * @class Roo.bootstrap.TextArea
11501  * @extends Roo.bootstrap.Input
11502  * Bootstrap TextArea class
11503  * @cfg {Number} cols Specifies the visible width of a text area
11504  * @cfg {Number} rows Specifies the visible number of lines in a text area
11505  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11506  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11507  * @cfg {string} html text
11508  * 
11509  * @constructor
11510  * Create a new TextArea
11511  * @param {Object} config The config object
11512  */
11513
11514 Roo.bootstrap.TextArea = function(config){
11515     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11516    
11517 };
11518
11519 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11520      
11521     cols : false,
11522     rows : 5,
11523     readOnly : false,
11524     warp : 'soft',
11525     resize : false,
11526     value: false,
11527     html: false,
11528     
11529     getAutoCreate : function(){
11530         
11531         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11532         
11533         var id = Roo.id();
11534         
11535         var cfg = {};
11536         
11537         if(this.inputType != 'hidden'){
11538             cfg.cls = 'form-group' //input-group
11539         }
11540         
11541         var input =  {
11542             tag: 'textarea',
11543             id : id,
11544             warp : this.warp,
11545             rows : this.rows,
11546             value : this.value || '',
11547             html: this.html || '',
11548             cls : 'form-control',
11549             placeholder : this.placeholder || '' 
11550             
11551         };
11552         
11553         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11554             input.maxLength = this.maxLength;
11555         }
11556         
11557         if(this.resize){
11558             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11559         }
11560         
11561         if(this.cols){
11562             input.cols = this.cols;
11563         }
11564         
11565         if (this.readOnly) {
11566             input.readonly = true;
11567         }
11568         
11569         if (this.name) {
11570             input.name = this.name;
11571         }
11572         
11573         if (this.size) {
11574             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11575         }
11576         
11577         var settings=this;
11578         ['xs','sm','md','lg'].map(function(size){
11579             if (settings[size]) {
11580                 cfg.cls += ' col-' + size + '-' + settings[size];
11581             }
11582         });
11583         
11584         var inputblock = input;
11585         
11586         if(this.hasFeedback && !this.allowBlank){
11587             
11588             var feedback = {
11589                 tag: 'span',
11590                 cls: 'glyphicon form-control-feedback'
11591             };
11592
11593             inputblock = {
11594                 cls : 'has-feedback',
11595                 cn :  [
11596                     input,
11597                     feedback
11598                 ] 
11599             };  
11600         }
11601         
11602         
11603         if (this.before || this.after) {
11604             
11605             inputblock = {
11606                 cls : 'input-group',
11607                 cn :  [] 
11608             };
11609             if (this.before) {
11610                 inputblock.cn.push({
11611                     tag :'span',
11612                     cls : 'input-group-addon',
11613                     html : this.before
11614                 });
11615             }
11616             
11617             inputblock.cn.push(input);
11618             
11619             if(this.hasFeedback && !this.allowBlank){
11620                 inputblock.cls += ' has-feedback';
11621                 inputblock.cn.push(feedback);
11622             }
11623             
11624             if (this.after) {
11625                 inputblock.cn.push({
11626                     tag :'span',
11627                     cls : 'input-group-addon',
11628                     html : this.after
11629                 });
11630             }
11631             
11632         }
11633         
11634         if (align ==='left' && this.fieldLabel.length) {
11635             cfg.cn = [
11636                 {
11637                     tag: 'label',
11638                     'for' :  id,
11639                     cls : 'control-label',
11640                     html : this.fieldLabel
11641                 },
11642                 {
11643                     cls : "",
11644                     cn: [
11645                         inputblock
11646                     ]
11647                 }
11648
11649             ];
11650             
11651             if(this.labelWidth > 12){
11652                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11653             }
11654
11655             if(this.labelWidth < 13 && this.labelmd == 0){
11656                 this.labelmd = this.labelWidth;
11657             }
11658
11659             if(this.labellg > 0){
11660                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11661                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11662             }
11663
11664             if(this.labelmd > 0){
11665                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11666                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11667             }
11668
11669             if(this.labelsm > 0){
11670                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11671                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11672             }
11673
11674             if(this.labelxs > 0){
11675                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11676                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11677             }
11678             
11679         } else if ( this.fieldLabel.length) {
11680             cfg.cn = [
11681
11682                {
11683                    tag: 'label',
11684                    //cls : 'input-group-addon',
11685                    html : this.fieldLabel
11686
11687                },
11688
11689                inputblock
11690
11691            ];
11692
11693         } else {
11694
11695             cfg.cn = [
11696
11697                 inputblock
11698
11699             ];
11700                 
11701         }
11702         
11703         if (this.disabled) {
11704             input.disabled=true;
11705         }
11706         
11707         return cfg;
11708         
11709     },
11710     /**
11711      * return the real textarea element.
11712      */
11713     inputEl: function ()
11714     {
11715         return this.el.select('textarea.form-control',true).first();
11716     },
11717     
11718     /**
11719      * Clear any invalid styles/messages for this field
11720      */
11721     clearInvalid : function()
11722     {
11723         
11724         if(!this.el || this.preventMark){ // not rendered
11725             return;
11726         }
11727         
11728         var label = this.el.select('label', true).first();
11729         var icon = this.el.select('i.fa-star', true).first();
11730         
11731         if(label && icon){
11732             icon.remove();
11733         }
11734         this.el.removeClass( this.validClass);
11735         this.inputEl().removeClass('is-invalid');
11736          
11737         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11738             
11739             var feedback = this.el.select('.form-control-feedback', true).first();
11740             
11741             if(feedback){
11742                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11743             }
11744             
11745         }
11746         
11747         this.fireEvent('valid', this);
11748     },
11749     
11750      /**
11751      * Mark this field as valid
11752      */
11753     markValid : function()
11754     {
11755         if(!this.el  || this.preventMark){ // not rendered
11756             return;
11757         }
11758         
11759         this.el.removeClass([this.invalidClass, this.validClass]);
11760         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11761         
11762         var feedback = this.el.select('.form-control-feedback', true).first();
11763             
11764         if(feedback){
11765             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11766         }
11767
11768         if(this.disabled || this.allowBlank){
11769             return;
11770         }
11771         
11772         var label = this.el.select('label', true).first();
11773         var icon = this.el.select('i.fa-star', true).first();
11774         
11775         if(label && icon){
11776             icon.remove();
11777         }
11778         if (Roo.bootstrap.version == 3) {
11779             this.el.addClass(this.validClass);
11780         } else {
11781             this.inputEl().addClass('is-valid');
11782         }
11783         
11784         
11785         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11786             
11787             var feedback = this.el.select('.form-control-feedback', true).first();
11788             
11789             if(feedback){
11790                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11791                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11792             }
11793             
11794         }
11795         
11796         this.fireEvent('valid', this);
11797     },
11798     
11799      /**
11800      * Mark this field as invalid
11801      * @param {String} msg The validation message
11802      */
11803     markInvalid : function(msg)
11804     {
11805         if(!this.el  || this.preventMark){ // not rendered
11806             return;
11807         }
11808         
11809         this.el.removeClass([this.invalidClass, this.validClass]);
11810         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11811         
11812         var feedback = this.el.select('.form-control-feedback', true).first();
11813             
11814         if(feedback){
11815             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11816         }
11817
11818         if(this.disabled || this.allowBlank){
11819             return;
11820         }
11821         
11822         var label = this.el.select('label', true).first();
11823         var icon = this.el.select('i.fa-star', true).first();
11824         
11825         if(!this.getValue().length && label && !icon){
11826             this.el.createChild({
11827                 tag : 'i',
11828                 cls : 'text-danger fa fa-lg fa-star',
11829                 tooltip : 'This field is required',
11830                 style : 'margin-right:5px;'
11831             }, label, true);
11832         }
11833         
11834         if (Roo.bootstrap.version == 3) {
11835             this.el.addClass(this.invalidClass);
11836         } else {
11837             this.inputEl().addClass('is-invalid');
11838         }
11839         
11840         // fixme ... this may be depricated need to test..
11841         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11842             
11843             var feedback = this.el.select('.form-control-feedback', true).first();
11844             
11845             if(feedback){
11846                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11847                 
11848                 if(this.getValue().length || this.forceFeedback){
11849                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11850                 }
11851                 
11852             }
11853             
11854         }
11855         
11856         this.fireEvent('invalid', this, msg);
11857     }
11858 });
11859
11860  
11861 /*
11862  * - LGPL
11863  *
11864  * trigger field - base class for combo..
11865  * 
11866  */
11867  
11868 /**
11869  * @class Roo.bootstrap.TriggerField
11870  * @extends Roo.bootstrap.Input
11871  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11872  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11873  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11874  * for which you can provide a custom implementation.  For example:
11875  * <pre><code>
11876 var trigger = new Roo.bootstrap.TriggerField();
11877 trigger.onTriggerClick = myTriggerFn;
11878 trigger.applyTo('my-field');
11879 </code></pre>
11880  *
11881  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11882  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11883  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11884  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11885  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11886
11887  * @constructor
11888  * Create a new TriggerField.
11889  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11890  * to the base TextField)
11891  */
11892 Roo.bootstrap.TriggerField = function(config){
11893     this.mimicing = false;
11894     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11895 };
11896
11897 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11898     /**
11899      * @cfg {String} triggerClass A CSS class to apply to the trigger
11900      */
11901      /**
11902      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11903      */
11904     hideTrigger:false,
11905
11906     /**
11907      * @cfg {Boolean} removable (true|false) special filter default false
11908      */
11909     removable : false,
11910     
11911     /** @cfg {Boolean} grow @hide */
11912     /** @cfg {Number} growMin @hide */
11913     /** @cfg {Number} growMax @hide */
11914
11915     /**
11916      * @hide 
11917      * @method
11918      */
11919     autoSize: Roo.emptyFn,
11920     // private
11921     monitorTab : true,
11922     // private
11923     deferHeight : true,
11924
11925     
11926     actionMode : 'wrap',
11927     
11928     caret : false,
11929     
11930     
11931     getAutoCreate : function(){
11932        
11933         var align = this.labelAlign || this.parentLabelAlign();
11934         
11935         var id = Roo.id();
11936         
11937         var cfg = {
11938             cls: 'form-group' //input-group
11939         };
11940         
11941         
11942         var input =  {
11943             tag: 'input',
11944             id : id,
11945             type : this.inputType,
11946             cls : 'form-control',
11947             autocomplete: 'new-password',
11948             placeholder : this.placeholder || '' 
11949             
11950         };
11951         if (this.name) {
11952             input.name = this.name;
11953         }
11954         if (this.size) {
11955             input.cls += ' input-' + this.size;
11956         }
11957         
11958         if (this.disabled) {
11959             input.disabled=true;
11960         }
11961         
11962         var inputblock = input;
11963         
11964         if(this.hasFeedback && !this.allowBlank){
11965             
11966             var feedback = {
11967                 tag: 'span',
11968                 cls: 'glyphicon form-control-feedback'
11969             };
11970             
11971             if(this.removable && !this.editable  ){
11972                 inputblock = {
11973                     cls : 'has-feedback',
11974                     cn :  [
11975                         inputblock,
11976                         {
11977                             tag: 'button',
11978                             html : 'x',
11979                             cls : 'roo-combo-removable-btn close'
11980                         },
11981                         feedback
11982                     ] 
11983                 };
11984             } else {
11985                 inputblock = {
11986                     cls : 'has-feedback',
11987                     cn :  [
11988                         inputblock,
11989                         feedback
11990                     ] 
11991                 };
11992             }
11993
11994         } else {
11995             if(this.removable && !this.editable ){
11996                 inputblock = {
11997                     cls : 'roo-removable',
11998                     cn :  [
11999                         inputblock,
12000                         {
12001                             tag: 'button',
12002                             html : 'x',
12003                             cls : 'roo-combo-removable-btn close'
12004                         }
12005                     ] 
12006                 };
12007             }
12008         }
12009         
12010         if (this.before || this.after) {
12011             
12012             inputblock = {
12013                 cls : 'input-group',
12014                 cn :  [] 
12015             };
12016             if (this.before) {
12017                 inputblock.cn.push({
12018                     tag :'span',
12019                     cls : 'input-group-addon input-group-prepend input-group-text',
12020                     html : this.before
12021                 });
12022             }
12023             
12024             inputblock.cn.push(input);
12025             
12026             if(this.hasFeedback && !this.allowBlank){
12027                 inputblock.cls += ' has-feedback';
12028                 inputblock.cn.push(feedback);
12029             }
12030             
12031             if (this.after) {
12032                 inputblock.cn.push({
12033                     tag :'span',
12034                     cls : 'input-group-addon input-group-append input-group-text',
12035                     html : this.after
12036                 });
12037             }
12038             
12039         };
12040         
12041       
12042         
12043         var ibwrap = inputblock;
12044         
12045         if(this.multiple){
12046             ibwrap = {
12047                 tag: 'ul',
12048                 cls: 'roo-select2-choices',
12049                 cn:[
12050                     {
12051                         tag: 'li',
12052                         cls: 'roo-select2-search-field',
12053                         cn: [
12054
12055                             inputblock
12056                         ]
12057                     }
12058                 ]
12059             };
12060                 
12061         }
12062         
12063         var combobox = {
12064             cls: 'roo-select2-container input-group',
12065             cn: [
12066                  {
12067                     tag: 'input',
12068                     type : 'hidden',
12069                     cls: 'form-hidden-field'
12070                 },
12071                 ibwrap
12072             ]
12073         };
12074         
12075         if(!this.multiple && this.showToggleBtn){
12076             
12077             var caret = {
12078                         tag: 'span',
12079                         cls: 'caret'
12080              };
12081             if (this.caret != false) {
12082                 caret = {
12083                      tag: 'i',
12084                      cls: 'fa fa-' + this.caret
12085                 };
12086                 
12087             }
12088             
12089             combobox.cn.push({
12090                 tag :'span',
12091                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12092                 cn : [
12093                     Roo.bootstrap.version == 3 ? caret : '',
12094                     {
12095                         tag: 'span',
12096                         cls: 'combobox-clear',
12097                         cn  : [
12098                             {
12099                                 tag : 'i',
12100                                 cls: 'icon-remove'
12101                             }
12102                         ]
12103                     }
12104                 ]
12105
12106             })
12107         }
12108         
12109         if(this.multiple){
12110             combobox.cls += ' roo-select2-container-multi';
12111         }
12112          var indicator = {
12113             tag : 'i',
12114             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12115             tooltip : 'This field is required'
12116         };
12117         if (Roo.bootstrap.version == 4) {
12118             indicator = {
12119                 tag : 'i',
12120                 style : 'display:none'
12121             };
12122         }
12123         
12124         
12125         if (align ==='left' && this.fieldLabel.length) {
12126             
12127             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12128
12129             cfg.cn = [
12130                 indicator,
12131                 {
12132                     tag: 'label',
12133                     'for' :  id,
12134                     cls : 'control-label',
12135                     html : this.fieldLabel
12136
12137                 },
12138                 {
12139                     cls : "", 
12140                     cn: [
12141                         combobox
12142                     ]
12143                 }
12144
12145             ];
12146             
12147             var labelCfg = cfg.cn[1];
12148             var contentCfg = cfg.cn[2];
12149             
12150             if(this.indicatorpos == 'right'){
12151                 cfg.cn = [
12152                     {
12153                         tag: 'label',
12154                         'for' :  id,
12155                         cls : 'control-label',
12156                         cn : [
12157                             {
12158                                 tag : 'span',
12159                                 html : this.fieldLabel
12160                             },
12161                             indicator
12162                         ]
12163                     },
12164                     {
12165                         cls : "", 
12166                         cn: [
12167                             combobox
12168                         ]
12169                     }
12170
12171                 ];
12172                 
12173                 labelCfg = cfg.cn[0];
12174                 contentCfg = cfg.cn[1];
12175             }
12176             
12177             if(this.labelWidth > 12){
12178                 labelCfg.style = "width: " + this.labelWidth + 'px';
12179             }
12180             
12181             if(this.labelWidth < 13 && this.labelmd == 0){
12182                 this.labelmd = this.labelWidth;
12183             }
12184             
12185             if(this.labellg > 0){
12186                 labelCfg.cls += ' col-lg-' + this.labellg;
12187                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12188             }
12189             
12190             if(this.labelmd > 0){
12191                 labelCfg.cls += ' col-md-' + this.labelmd;
12192                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12193             }
12194             
12195             if(this.labelsm > 0){
12196                 labelCfg.cls += ' col-sm-' + this.labelsm;
12197                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12198             }
12199             
12200             if(this.labelxs > 0){
12201                 labelCfg.cls += ' col-xs-' + this.labelxs;
12202                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12203             }
12204             
12205         } else if ( this.fieldLabel.length) {
12206 //                Roo.log(" label");
12207             cfg.cn = [
12208                 indicator,
12209                {
12210                    tag: 'label',
12211                    //cls : 'input-group-addon',
12212                    html : this.fieldLabel
12213
12214                },
12215
12216                combobox
12217
12218             ];
12219             
12220             if(this.indicatorpos == 'right'){
12221                 
12222                 cfg.cn = [
12223                     {
12224                        tag: 'label',
12225                        cn : [
12226                            {
12227                                tag : 'span',
12228                                html : this.fieldLabel
12229                            },
12230                            indicator
12231                        ]
12232
12233                     },
12234                     combobox
12235
12236                 ];
12237
12238             }
12239
12240         } else {
12241             
12242 //                Roo.log(" no label && no align");
12243                 cfg = combobox
12244                      
12245                 
12246         }
12247         
12248         var settings=this;
12249         ['xs','sm','md','lg'].map(function(size){
12250             if (settings[size]) {
12251                 cfg.cls += ' col-' + size + '-' + settings[size];
12252             }
12253         });
12254         
12255         return cfg;
12256         
12257     },
12258     
12259     
12260     
12261     // private
12262     onResize : function(w, h){
12263 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12264 //        if(typeof w == 'number'){
12265 //            var x = w - this.trigger.getWidth();
12266 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12267 //            this.trigger.setStyle('left', x+'px');
12268 //        }
12269     },
12270
12271     // private
12272     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12273
12274     // private
12275     getResizeEl : function(){
12276         return this.inputEl();
12277     },
12278
12279     // private
12280     getPositionEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     alignErrorIcon : function(){
12286         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12287     },
12288
12289     // private
12290     initEvents : function(){
12291         
12292         this.createList();
12293         
12294         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12295         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12296         if(!this.multiple && this.showToggleBtn){
12297             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12298             if(this.hideTrigger){
12299                 this.trigger.setDisplayed(false);
12300             }
12301             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12302         }
12303         
12304         if(this.multiple){
12305             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12306         }
12307         
12308         if(this.removable && !this.editable && !this.tickable){
12309             var close = this.closeTriggerEl();
12310             
12311             if(close){
12312                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12313                 close.on('click', this.removeBtnClick, this, close);
12314             }
12315         }
12316         
12317         //this.trigger.addClassOnOver('x-form-trigger-over');
12318         //this.trigger.addClassOnClick('x-form-trigger-click');
12319         
12320         //if(!this.width){
12321         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12322         //}
12323     },
12324     
12325     closeTriggerEl : function()
12326     {
12327         var close = this.el.select('.roo-combo-removable-btn', true).first();
12328         return close ? close : false;
12329     },
12330     
12331     removeBtnClick : function(e, h, el)
12332     {
12333         e.preventDefault();
12334         
12335         if(this.fireEvent("remove", this) !== false){
12336             this.reset();
12337             this.fireEvent("afterremove", this)
12338         }
12339     },
12340     
12341     createList : function()
12342     {
12343         this.list = Roo.get(document.body).createChild({
12344             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12345             cls: 'typeahead typeahead-long dropdown-menu shadow',
12346             style: 'display:none'
12347         });
12348         
12349         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12350         
12351     },
12352
12353     // private
12354     initTrigger : function(){
12355        
12356     },
12357
12358     // private
12359     onDestroy : function(){
12360         if(this.trigger){
12361             this.trigger.removeAllListeners();
12362           //  this.trigger.remove();
12363         }
12364         //if(this.wrap){
12365         //    this.wrap.remove();
12366         //}
12367         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12368     },
12369
12370     // private
12371     onFocus : function(){
12372         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12373         /*
12374         if(!this.mimicing){
12375             this.wrap.addClass('x-trigger-wrap-focus');
12376             this.mimicing = true;
12377             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12378             if(this.monitorTab){
12379                 this.el.on("keydown", this.checkTab, this);
12380             }
12381         }
12382         */
12383     },
12384
12385     // private
12386     checkTab : function(e){
12387         if(e.getKey() == e.TAB){
12388             this.triggerBlur();
12389         }
12390     },
12391
12392     // private
12393     onBlur : function(){
12394         // do nothing
12395     },
12396
12397     // private
12398     mimicBlur : function(e, t){
12399         /*
12400         if(!this.wrap.contains(t) && this.validateBlur()){
12401             this.triggerBlur();
12402         }
12403         */
12404     },
12405
12406     // private
12407     triggerBlur : function(){
12408         this.mimicing = false;
12409         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12410         if(this.monitorTab){
12411             this.el.un("keydown", this.checkTab, this);
12412         }
12413         //this.wrap.removeClass('x-trigger-wrap-focus');
12414         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12415     },
12416
12417     // private
12418     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12419     validateBlur : function(e, t){
12420         return true;
12421     },
12422
12423     // private
12424     onDisable : function(){
12425         this.inputEl().dom.disabled = true;
12426         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12427         //if(this.wrap){
12428         //    this.wrap.addClass('x-item-disabled');
12429         //}
12430     },
12431
12432     // private
12433     onEnable : function(){
12434         this.inputEl().dom.disabled = false;
12435         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12436         //if(this.wrap){
12437         //    this.el.removeClass('x-item-disabled');
12438         //}
12439     },
12440
12441     // private
12442     onShow : function(){
12443         var ae = this.getActionEl();
12444         
12445         if(ae){
12446             ae.dom.style.display = '';
12447             ae.dom.style.visibility = 'visible';
12448         }
12449     },
12450
12451     // private
12452     
12453     onHide : function(){
12454         var ae = this.getActionEl();
12455         ae.dom.style.display = 'none';
12456     },
12457
12458     /**
12459      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12460      * by an implementing function.
12461      * @method
12462      * @param {EventObject} e
12463      */
12464     onTriggerClick : Roo.emptyFn
12465 });
12466  
12467 /*
12468 * Licence: LGPL
12469 */
12470
12471 /**
12472  * @class Roo.bootstrap.CardUploader
12473  * @extends Roo.bootstrap.Button
12474  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12475  * @cfg {Number} errorTimeout default 3000
12476  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12477  * @cfg {Array}  html The button text.
12478
12479  *
12480  * @constructor
12481  * Create a new CardUploader
12482  * @param {Object} config The config object
12483  */
12484
12485 Roo.bootstrap.CardUploader = function(config){
12486     
12487  
12488     
12489     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12490     
12491     
12492     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12493         return r.data.id
12494         });
12495     
12496     
12497 };
12498
12499 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12500     
12501      
12502     errorTimeout : 3000,
12503      
12504     images : false,
12505    
12506     fileCollection : false,
12507     allowBlank : true,
12508     
12509     getAutoCreate : function()
12510     {
12511         
12512         var cfg =  {
12513             cls :'form-group' ,
12514             cn : [
12515                
12516                 {
12517                     tag: 'label',
12518                    //cls : 'input-group-addon',
12519                     html : this.fieldLabel
12520
12521                 },
12522
12523                 {
12524                     tag: 'input',
12525                     type : 'hidden',
12526                     value : this.value,
12527                     cls : 'd-none  form-control'
12528                 },
12529                 
12530                 {
12531                     tag: 'input',
12532                     multiple : 'multiple',
12533                     type : 'file',
12534                     cls : 'd-none  roo-card-upload-selector'
12535                 },
12536                 
12537                 {
12538                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12539                 },
12540                 {
12541                     cls : 'card-columns roo-card-uploader-container'
12542                 }
12543
12544             ]
12545         };
12546            
12547          
12548         return cfg;
12549     },
12550     
12551     getChildContainer : function() /// what children are added to.
12552     {
12553         return this.containerEl;
12554     },
12555    
12556     getButtonContainer : function() /// what children are added to.
12557     {
12558         return this.el.select(".roo-card-uploader-button-container").first();
12559     },
12560    
12561     initEvents : function()
12562     {
12563         
12564         Roo.bootstrap.Input.prototype.initEvents.call(this);
12565         
12566         var t = this;
12567         this.addxtype({
12568             xns: Roo.bootstrap,
12569
12570             xtype : 'Button',
12571             container_method : 'getButtonContainer' ,            
12572             html :  this.html, // fix changable?
12573             cls : 'w-100 ',
12574             listeners : {
12575                 'click' : function(btn, e) {
12576                     t.onClick(e);
12577                 }
12578             }
12579         });
12580         
12581         
12582         
12583         
12584         this.urlAPI = (window.createObjectURL && window) || 
12585                                 (window.URL && URL.revokeObjectURL && URL) || 
12586                                 (window.webkitURL && webkitURL);
12587                         
12588          
12589          
12590          
12591         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12592         
12593         this.selectorEl.on('change', this.onFileSelected, this);
12594         if (this.images) {
12595             var t = this;
12596             this.images.forEach(function(img) {
12597                 t.addCard(img)
12598             });
12599             this.images = false;
12600         }
12601         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12602          
12603        
12604     },
12605     
12606    
12607     onClick : function(e)
12608     {
12609         e.preventDefault();
12610          
12611         this.selectorEl.dom.click();
12612          
12613     },
12614     
12615     onFileSelected : function(e)
12616     {
12617         e.preventDefault();
12618         
12619         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12620             return;
12621         }
12622         
12623         Roo.each(this.selectorEl.dom.files, function(file){    
12624             this.addFile(file);
12625         }, this);
12626          
12627     },
12628     
12629       
12630     
12631       
12632     
12633     addFile : function(file)
12634     {
12635            
12636         if(typeof(file) === 'string'){
12637             throw "Add file by name?"; // should not happen
12638             return;
12639         }
12640         
12641         if(!file || !this.urlAPI){
12642             return;
12643         }
12644         
12645         // file;
12646         // file.type;
12647         
12648         var _this = this;
12649         
12650         
12651         var url = _this.urlAPI.createObjectURL( file);
12652            
12653         this.addCard({
12654             id : Roo.bootstrap.CardUploader.ID--,
12655             is_uploaded : false,
12656             src : url,
12657             title : file.name,
12658             mimetype : file.type,
12659             preview : false,
12660             is_deleted : 0
12661         })
12662         
12663     },
12664     
12665     addCard : function (data)
12666     {
12667         // hidden input element?
12668         // if the file is not an image...
12669         //then we need to use something other that and header_image
12670         var t = this;
12671         //   remove.....
12672         var footer = [
12673             {
12674                 xns : Roo.bootstrap,
12675                 xtype : 'CardFooter',
12676                 items: [
12677                     {
12678                         xns : Roo.bootstrap,
12679                         xtype : 'Element',
12680                         cls : 'd-flex',
12681                         items : [
12682                             
12683                             {
12684                                 xns : Roo.bootstrap,
12685                                 xtype : 'Button',
12686                                 html : String.format("<small>{0}</small>", data.title),
12687                                 cls : 'col-11 text-left',
12688                                 size: 'sm',
12689                                 weight: 'link',
12690                                 fa : 'download',
12691                                 listeners : {
12692                                     click : function() {
12693                                         this.downloadCard(data.id)
12694                                     }
12695                                 }
12696                             },
12697                           
12698                             {
12699                                 xns : Roo.bootstrap,
12700                                 xtype : 'Button',
12701                                 
12702                                 size : 'sm',
12703                                 weight: 'danger',
12704                                 cls : 'col-1',
12705                                 fa : 'times',
12706                                 listeners : {
12707                                     click : function() {
12708                                         t.removeCard(data.id)
12709                                     }
12710                                 }
12711                             }
12712                         ]
12713                     }
12714                     
12715                 ] 
12716             }
12717             
12718         ];
12719
12720         var cn = this.addxtype(
12721             {
12722                  
12723                 xns : Roo.bootstrap,
12724                 xtype : 'Card',
12725                 closeable : true,
12726                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12727                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12728                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12729                 data : data,
12730                 html : false,
12731                  
12732                 items : footer,
12733                 initEvents : function() {
12734                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12735                     this.imgEl = this.el.select('.card-img-top').first();
12736                     if (this.imgEl) {
12737                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12738                         this.imgEl.set({ 'pointer' : 'cursor' });
12739                                   
12740                     }
12741                     
12742                   
12743                 }
12744                 
12745             }
12746         );
12747         // dont' really need ot update items.
12748         // this.items.push(cn);
12749         this.fileCollection.add(cn);
12750         this.updateInput();
12751         
12752     },
12753     removeCard : function(id)
12754     {
12755         
12756         var card  = this.fileCollection.get(id);
12757         card.data.is_deleted = 1;
12758         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12759         this.fileCollection.remove(card);
12760         //this.items = this.items.filter(function(e) { return e != card });
12761         // dont' really need ot update items.
12762         card.el.dom.parentNode.removeChild(card.el.dom);
12763         
12764     },
12765     reset: function()
12766     {
12767         this.fileCollection.each(function(card) {
12768             card.el.dom.parentNode.removeChild(card.el.dom);    
12769         });
12770         this.fileCollection.clear();
12771         this.updateInput();
12772     },
12773     
12774     updateInput : function()
12775     {
12776         var data = [];
12777         this.fileCollection.each(function(e) {
12778             data.push(e.data);
12779         });
12780         
12781         this.inputEl().dom.value = JSON.stringify(data);
12782     }
12783     
12784     
12785 });
12786
12787
12788 Roo.bootstrap.CardUploader.ID = -1;/*
12789  * Based on:
12790  * Ext JS Library 1.1.1
12791  * Copyright(c) 2006-2007, Ext JS, LLC.
12792  *
12793  * Originally Released Under LGPL - original licence link has changed is not relivant.
12794  *
12795  * Fork - LGPL
12796  * <script type="text/javascript">
12797  */
12798
12799
12800 /**
12801  * @class Roo.data.SortTypes
12802  * @singleton
12803  * Defines the default sorting (casting?) comparison functions used when sorting data.
12804  */
12805 Roo.data.SortTypes = {
12806     /**
12807      * Default sort that does nothing
12808      * @param {Mixed} s The value being converted
12809      * @return {Mixed} The comparison value
12810      */
12811     none : function(s){
12812         return s;
12813     },
12814     
12815     /**
12816      * The regular expression used to strip tags
12817      * @type {RegExp}
12818      * @property
12819      */
12820     stripTagsRE : /<\/?[^>]+>/gi,
12821     
12822     /**
12823      * Strips all HTML tags to sort on text only
12824      * @param {Mixed} s The value being converted
12825      * @return {String} The comparison value
12826      */
12827     asText : function(s){
12828         return String(s).replace(this.stripTagsRE, "");
12829     },
12830     
12831     /**
12832      * Strips all HTML tags to sort on text only - Case insensitive
12833      * @param {Mixed} s The value being converted
12834      * @return {String} The comparison value
12835      */
12836     asUCText : function(s){
12837         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12838     },
12839     
12840     /**
12841      * Case insensitive string
12842      * @param {Mixed} s The value being converted
12843      * @return {String} The comparison value
12844      */
12845     asUCString : function(s) {
12846         return String(s).toUpperCase();
12847     },
12848     
12849     /**
12850      * Date sorting
12851      * @param {Mixed} s The value being converted
12852      * @return {Number} The comparison value
12853      */
12854     asDate : function(s) {
12855         if(!s){
12856             return 0;
12857         }
12858         if(s instanceof Date){
12859             return s.getTime();
12860         }
12861         return Date.parse(String(s));
12862     },
12863     
12864     /**
12865      * Float sorting
12866      * @param {Mixed} s The value being converted
12867      * @return {Float} The comparison value
12868      */
12869     asFloat : function(s) {
12870         var val = parseFloat(String(s).replace(/,/g, ""));
12871         if(isNaN(val)) {
12872             val = 0;
12873         }
12874         return val;
12875     },
12876     
12877     /**
12878      * Integer sorting
12879      * @param {Mixed} s The value being converted
12880      * @return {Number} The comparison value
12881      */
12882     asInt : function(s) {
12883         var val = parseInt(String(s).replace(/,/g, ""));
12884         if(isNaN(val)) {
12885             val = 0;
12886         }
12887         return val;
12888     }
12889 };/*
12890  * Based on:
12891  * Ext JS Library 1.1.1
12892  * Copyright(c) 2006-2007, Ext JS, LLC.
12893  *
12894  * Originally Released Under LGPL - original licence link has changed is not relivant.
12895  *
12896  * Fork - LGPL
12897  * <script type="text/javascript">
12898  */
12899
12900 /**
12901 * @class Roo.data.Record
12902  * Instances of this class encapsulate both record <em>definition</em> information, and record
12903  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12904  * to access Records cached in an {@link Roo.data.Store} object.<br>
12905  * <p>
12906  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12907  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12908  * objects.<br>
12909  * <p>
12910  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12911  * @constructor
12912  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12913  * {@link #create}. The parameters are the same.
12914  * @param {Array} data An associative Array of data values keyed by the field name.
12915  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12916  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12917  * not specified an integer id is generated.
12918  */
12919 Roo.data.Record = function(data, id){
12920     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12921     this.data = data;
12922 };
12923
12924 /**
12925  * Generate a constructor for a specific record layout.
12926  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12927  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12928  * Each field definition object may contain the following properties: <ul>
12929  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
12930  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12931  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12932  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12933  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12934  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12935  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12936  * this may be omitted.</p></li>
12937  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12938  * <ul><li>auto (Default, implies no conversion)</li>
12939  * <li>string</li>
12940  * <li>int</li>
12941  * <li>float</li>
12942  * <li>boolean</li>
12943  * <li>date</li></ul></p></li>
12944  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12945  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12946  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12947  * by the Reader into an object that will be stored in the Record. It is passed the
12948  * following parameters:<ul>
12949  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12950  * </ul></p></li>
12951  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12952  * </ul>
12953  * <br>usage:<br><pre><code>
12954 var TopicRecord = Roo.data.Record.create(
12955     {name: 'title', mapping: 'topic_title'},
12956     {name: 'author', mapping: 'username'},
12957     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12958     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12959     {name: 'lastPoster', mapping: 'user2'},
12960     {name: 'excerpt', mapping: 'post_text'}
12961 );
12962
12963 var myNewRecord = new TopicRecord({
12964     title: 'Do my job please',
12965     author: 'noobie',
12966     totalPosts: 1,
12967     lastPost: new Date(),
12968     lastPoster: 'Animal',
12969     excerpt: 'No way dude!'
12970 });
12971 myStore.add(myNewRecord);
12972 </code></pre>
12973  * @method create
12974  * @static
12975  */
12976 Roo.data.Record.create = function(o){
12977     var f = function(){
12978         f.superclass.constructor.apply(this, arguments);
12979     };
12980     Roo.extend(f, Roo.data.Record);
12981     var p = f.prototype;
12982     p.fields = new Roo.util.MixedCollection(false, function(field){
12983         return field.name;
12984     });
12985     for(var i = 0, len = o.length; i < len; i++){
12986         p.fields.add(new Roo.data.Field(o[i]));
12987     }
12988     f.getField = function(name){
12989         return p.fields.get(name);  
12990     };
12991     return f;
12992 };
12993
12994 Roo.data.Record.AUTO_ID = 1000;
12995 Roo.data.Record.EDIT = 'edit';
12996 Roo.data.Record.REJECT = 'reject';
12997 Roo.data.Record.COMMIT = 'commit';
12998
12999 Roo.data.Record.prototype = {
13000     /**
13001      * Readonly flag - true if this record has been modified.
13002      * @type Boolean
13003      */
13004     dirty : false,
13005     editing : false,
13006     error: null,
13007     modified: null,
13008
13009     // private
13010     join : function(store){
13011         this.store = store;
13012     },
13013
13014     /**
13015      * Set the named field to the specified value.
13016      * @param {String} name The name of the field to set.
13017      * @param {Object} value The value to set the field to.
13018      */
13019     set : function(name, value){
13020         if(this.data[name] == value){
13021             return;
13022         }
13023         this.dirty = true;
13024         if(!this.modified){
13025             this.modified = {};
13026         }
13027         if(typeof this.modified[name] == 'undefined'){
13028             this.modified[name] = this.data[name];
13029         }
13030         this.data[name] = value;
13031         if(!this.editing && this.store){
13032             this.store.afterEdit(this);
13033         }       
13034     },
13035
13036     /**
13037      * Get the value of the named field.
13038      * @param {String} name The name of the field to get the value of.
13039      * @return {Object} The value of the field.
13040      */
13041     get : function(name){
13042         return this.data[name]; 
13043     },
13044
13045     // private
13046     beginEdit : function(){
13047         this.editing = true;
13048         this.modified = {}; 
13049     },
13050
13051     // private
13052     cancelEdit : function(){
13053         this.editing = false;
13054         delete this.modified;
13055     },
13056
13057     // private
13058     endEdit : function(){
13059         this.editing = false;
13060         if(this.dirty && this.store){
13061             this.store.afterEdit(this);
13062         }
13063     },
13064
13065     /**
13066      * Usually called by the {@link Roo.data.Store} which owns the Record.
13067      * Rejects all changes made to the Record since either creation, or the last commit operation.
13068      * Modified fields are reverted to their original values.
13069      * <p>
13070      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13071      * of reject operations.
13072      */
13073     reject : function(){
13074         var m = this.modified;
13075         for(var n in m){
13076             if(typeof m[n] != "function"){
13077                 this.data[n] = m[n];
13078             }
13079         }
13080         this.dirty = false;
13081         delete this.modified;
13082         this.editing = false;
13083         if(this.store){
13084             this.store.afterReject(this);
13085         }
13086     },
13087
13088     /**
13089      * Usually called by the {@link Roo.data.Store} which owns the Record.
13090      * Commits all changes made to the Record since either creation, or the last commit operation.
13091      * <p>
13092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13093      * of commit operations.
13094      */
13095     commit : function(){
13096         this.dirty = false;
13097         delete this.modified;
13098         this.editing = false;
13099         if(this.store){
13100             this.store.afterCommit(this);
13101         }
13102     },
13103
13104     // private
13105     hasError : function(){
13106         return this.error != null;
13107     },
13108
13109     // private
13110     clearError : function(){
13111         this.error = null;
13112     },
13113
13114     /**
13115      * Creates a copy of this record.
13116      * @param {String} id (optional) A new record id if you don't want to use this record's id
13117      * @return {Record}
13118      */
13119     copy : function(newId) {
13120         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13121     }
13122 };/*
13123  * Based on:
13124  * Ext JS Library 1.1.1
13125  * Copyright(c) 2006-2007, Ext JS, LLC.
13126  *
13127  * Originally Released Under LGPL - original licence link has changed is not relivant.
13128  *
13129  * Fork - LGPL
13130  * <script type="text/javascript">
13131  */
13132
13133
13134
13135 /**
13136  * @class Roo.data.Store
13137  * @extends Roo.util.Observable
13138  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13139  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13140  * <p>
13141  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
13142  * has no knowledge of the format of the data returned by the Proxy.<br>
13143  * <p>
13144  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13145  * instances from the data object. These records are cached and made available through accessor functions.
13146  * @constructor
13147  * Creates a new Store.
13148  * @param {Object} config A config object containing the objects needed for the Store to access data,
13149  * and read the data into Records.
13150  */
13151 Roo.data.Store = function(config){
13152     this.data = new Roo.util.MixedCollection(false);
13153     this.data.getKey = function(o){
13154         return o.id;
13155     };
13156     this.baseParams = {};
13157     // private
13158     this.paramNames = {
13159         "start" : "start",
13160         "limit" : "limit",
13161         "sort" : "sort",
13162         "dir" : "dir",
13163         "multisort" : "_multisort"
13164     };
13165
13166     if(config && config.data){
13167         this.inlineData = config.data;
13168         delete config.data;
13169     }
13170
13171     Roo.apply(this, config);
13172     
13173     if(this.reader){ // reader passed
13174         this.reader = Roo.factory(this.reader, Roo.data);
13175         this.reader.xmodule = this.xmodule || false;
13176         if(!this.recordType){
13177             this.recordType = this.reader.recordType;
13178         }
13179         if(this.reader.onMetaChange){
13180             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13181         }
13182     }
13183
13184     if(this.recordType){
13185         this.fields = this.recordType.prototype.fields;
13186     }
13187     this.modified = [];
13188
13189     this.addEvents({
13190         /**
13191          * @event datachanged
13192          * Fires when the data cache has changed, and a widget which is using this Store
13193          * as a Record cache should refresh its view.
13194          * @param {Store} this
13195          */
13196         datachanged : true,
13197         /**
13198          * @event metachange
13199          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13200          * @param {Store} this
13201          * @param {Object} meta The JSON metadata
13202          */
13203         metachange : true,
13204         /**
13205          * @event add
13206          * Fires when Records have been added to the Store
13207          * @param {Store} this
13208          * @param {Roo.data.Record[]} records The array of Records added
13209          * @param {Number} index The index at which the record(s) were added
13210          */
13211         add : true,
13212         /**
13213          * @event remove
13214          * Fires when a Record has been removed from the Store
13215          * @param {Store} this
13216          * @param {Roo.data.Record} record The Record that was removed
13217          * @param {Number} index The index at which the record was removed
13218          */
13219         remove : true,
13220         /**
13221          * @event update
13222          * Fires when a Record has been updated
13223          * @param {Store} this
13224          * @param {Roo.data.Record} record The Record that was updated
13225          * @param {String} operation The update operation being performed.  Value may be one of:
13226          * <pre><code>
13227  Roo.data.Record.EDIT
13228  Roo.data.Record.REJECT
13229  Roo.data.Record.COMMIT
13230          * </code></pre>
13231          */
13232         update : true,
13233         /**
13234          * @event clear
13235          * Fires when the data cache has been cleared.
13236          * @param {Store} this
13237          */
13238         clear : true,
13239         /**
13240          * @event beforeload
13241          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13242          * the load action will be canceled.
13243          * @param {Store} this
13244          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13245          */
13246         beforeload : true,
13247         /**
13248          * @event beforeloadadd
13249          * Fires after a new set of Records has been loaded.
13250          * @param {Store} this
13251          * @param {Roo.data.Record[]} records The Records that were loaded
13252          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13253          */
13254         beforeloadadd : true,
13255         /**
13256          * @event load
13257          * Fires after a new set of Records has been loaded, before they are added to the store.
13258          * @param {Store} this
13259          * @param {Roo.data.Record[]} records The Records that were loaded
13260          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13261          * @params {Object} return from reader
13262          */
13263         load : true,
13264         /**
13265          * @event loadexception
13266          * Fires if an exception occurs in the Proxy during loading.
13267          * Called with the signature of the Proxy's "loadexception" event.
13268          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13269          * 
13270          * @param {Proxy} 
13271          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13272          * @param {Object} load options 
13273          * @param {Object} jsonData from your request (normally this contains the Exception)
13274          */
13275         loadexception : true
13276     });
13277     
13278     if(this.proxy){
13279         this.proxy = Roo.factory(this.proxy, Roo.data);
13280         this.proxy.xmodule = this.xmodule || false;
13281         this.relayEvents(this.proxy,  ["loadexception"]);
13282     }
13283     this.sortToggle = {};
13284     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13285
13286     Roo.data.Store.superclass.constructor.call(this);
13287
13288     if(this.inlineData){
13289         this.loadData(this.inlineData);
13290         delete this.inlineData;
13291     }
13292 };
13293
13294 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13295      /**
13296     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13297     * without a remote query - used by combo/forms at present.
13298     */
13299     
13300     /**
13301     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13302     */
13303     /**
13304     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13305     */
13306     /**
13307     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13308     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13309     */
13310     /**
13311     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13312     * on any HTTP request
13313     */
13314     /**
13315     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13316     */
13317     /**
13318     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13319     */
13320     multiSort: false,
13321     /**
13322     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13323     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13324     */
13325     remoteSort : false,
13326
13327     /**
13328     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13329      * loaded or when a record is removed. (defaults to false).
13330     */
13331     pruneModifiedRecords : false,
13332
13333     // private
13334     lastOptions : null,
13335
13336     /**
13337      * Add Records to the Store and fires the add event.
13338      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13339      */
13340     add : function(records){
13341         records = [].concat(records);
13342         for(var i = 0, len = records.length; i < len; i++){
13343             records[i].join(this);
13344         }
13345         var index = this.data.length;
13346         this.data.addAll(records);
13347         this.fireEvent("add", this, records, index);
13348     },
13349
13350     /**
13351      * Remove a Record from the Store and fires the remove event.
13352      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13353      */
13354     remove : function(record){
13355         var index = this.data.indexOf(record);
13356         this.data.removeAt(index);
13357  
13358         if(this.pruneModifiedRecords){
13359             this.modified.remove(record);
13360         }
13361         this.fireEvent("remove", this, record, index);
13362     },
13363
13364     /**
13365      * Remove all Records from the Store and fires the clear event.
13366      */
13367     removeAll : function(){
13368         this.data.clear();
13369         if(this.pruneModifiedRecords){
13370             this.modified = [];
13371         }
13372         this.fireEvent("clear", this);
13373     },
13374
13375     /**
13376      * Inserts Records to the Store at the given index and fires the add event.
13377      * @param {Number} index The start index at which to insert the passed Records.
13378      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13379      */
13380     insert : function(index, records){
13381         records = [].concat(records);
13382         for(var i = 0, len = records.length; i < len; i++){
13383             this.data.insert(index, records[i]);
13384             records[i].join(this);
13385         }
13386         this.fireEvent("add", this, records, index);
13387     },
13388
13389     /**
13390      * Get the index within the cache of the passed Record.
13391      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13392      * @return {Number} The index of the passed Record. Returns -1 if not found.
13393      */
13394     indexOf : function(record){
13395         return this.data.indexOf(record);
13396     },
13397
13398     /**
13399      * Get the index within the cache of the Record with the passed id.
13400      * @param {String} id The id of the Record to find.
13401      * @return {Number} The index of the Record. Returns -1 if not found.
13402      */
13403     indexOfId : function(id){
13404         return this.data.indexOfKey(id);
13405     },
13406
13407     /**
13408      * Get the Record with the specified id.
13409      * @param {String} id The id of the Record to find.
13410      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13411      */
13412     getById : function(id){
13413         return this.data.key(id);
13414     },
13415
13416     /**
13417      * Get the Record at the specified index.
13418      * @param {Number} index The index of the Record to find.
13419      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13420      */
13421     getAt : function(index){
13422         return this.data.itemAt(index);
13423     },
13424
13425     /**
13426      * Returns a range of Records between specified indices.
13427      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13428      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13429      * @return {Roo.data.Record[]} An array of Records
13430      */
13431     getRange : function(start, end){
13432         return this.data.getRange(start, end);
13433     },
13434
13435     // private
13436     storeOptions : function(o){
13437         o = Roo.apply({}, o);
13438         delete o.callback;
13439         delete o.scope;
13440         this.lastOptions = o;
13441     },
13442
13443     /**
13444      * Loads the Record cache from the configured Proxy using the configured Reader.
13445      * <p>
13446      * If using remote paging, then the first load call must specify the <em>start</em>
13447      * and <em>limit</em> properties in the options.params property to establish the initial
13448      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13449      * <p>
13450      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13451      * and this call will return before the new data has been loaded. Perform any post-processing
13452      * in a callback function, or in a "load" event handler.</strong>
13453      * <p>
13454      * @param {Object} options An object containing properties which control loading options:<ul>
13455      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13456      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13457      * passed the following arguments:<ul>
13458      * <li>r : Roo.data.Record[]</li>
13459      * <li>options: Options object from the load call</li>
13460      * <li>success: Boolean success indicator</li></ul></li>
13461      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13462      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13463      * </ul>
13464      */
13465     load : function(options){
13466         options = options || {};
13467         if(this.fireEvent("beforeload", this, options) !== false){
13468             this.storeOptions(options);
13469             var p = Roo.apply(options.params || {}, this.baseParams);
13470             // if meta was not loaded from remote source.. try requesting it.
13471             if (!this.reader.metaFromRemote) {
13472                 p._requestMeta = 1;
13473             }
13474             if(this.sortInfo && this.remoteSort){
13475                 var pn = this.paramNames;
13476                 p[pn["sort"]] = this.sortInfo.field;
13477                 p[pn["dir"]] = this.sortInfo.direction;
13478             }
13479             if (this.multiSort) {
13480                 var pn = this.paramNames;
13481                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13482             }
13483             
13484             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13485         }
13486     },
13487
13488     /**
13489      * Reloads the Record cache from the configured Proxy using the configured Reader and
13490      * the options from the last load operation performed.
13491      * @param {Object} options (optional) An object containing properties which may override the options
13492      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13493      * the most recently used options are reused).
13494      */
13495     reload : function(options){
13496         this.load(Roo.applyIf(options||{}, this.lastOptions));
13497     },
13498
13499     // private
13500     // Called as a callback by the Reader during a load operation.
13501     loadRecords : function(o, options, success){
13502         if(!o || success === false){
13503             if(success !== false){
13504                 this.fireEvent("load", this, [], options, o);
13505             }
13506             if(options.callback){
13507                 options.callback.call(options.scope || this, [], options, false);
13508             }
13509             return;
13510         }
13511         // if data returned failure - throw an exception.
13512         if (o.success === false) {
13513             // show a message if no listener is registered.
13514             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13515                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13516             }
13517             // loadmask wil be hooked into this..
13518             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13519             return;
13520         }
13521         var r = o.records, t = o.totalRecords || r.length;
13522         
13523         this.fireEvent("beforeloadadd", this, r, options, o);
13524         
13525         if(!options || options.add !== true){
13526             if(this.pruneModifiedRecords){
13527                 this.modified = [];
13528             }
13529             for(var i = 0, len = r.length; i < len; i++){
13530                 r[i].join(this);
13531             }
13532             if(this.snapshot){
13533                 this.data = this.snapshot;
13534                 delete this.snapshot;
13535             }
13536             this.data.clear();
13537             this.data.addAll(r);
13538             this.totalLength = t;
13539             this.applySort();
13540             this.fireEvent("datachanged", this);
13541         }else{
13542             this.totalLength = Math.max(t, this.data.length+r.length);
13543             this.add(r);
13544         }
13545         
13546         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13547                 
13548             var e = new Roo.data.Record({});
13549
13550             e.set(this.parent.displayField, this.parent.emptyTitle);
13551             e.set(this.parent.valueField, '');
13552
13553             this.insert(0, e);
13554         }
13555             
13556         this.fireEvent("load", this, r, options, o);
13557         if(options.callback){
13558             options.callback.call(options.scope || this, r, options, true);
13559         }
13560     },
13561
13562
13563     /**
13564      * Loads data from a passed data block. A Reader which understands the format of the data
13565      * must have been configured in the constructor.
13566      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13567      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13568      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13569      */
13570     loadData : function(o, append){
13571         var r = this.reader.readRecords(o);
13572         this.loadRecords(r, {add: append}, true);
13573     },
13574     
13575      /**
13576      * using 'cn' the nested child reader read the child array into it's child stores.
13577      * @param {Object} rec The record with a 'children array
13578      */
13579     loadDataFromChildren : function(rec)
13580     {
13581         this.loadData(this.reader.toLoadData(rec));
13582     },
13583     
13584
13585     /**
13586      * Gets the number of cached records.
13587      * <p>
13588      * <em>If using paging, this may not be the total size of the dataset. If the data object
13589      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13590      * the data set size</em>
13591      */
13592     getCount : function(){
13593         return this.data.length || 0;
13594     },
13595
13596     /**
13597      * Gets the total number of records in the dataset as returned by the server.
13598      * <p>
13599      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13600      * the dataset size</em>
13601      */
13602     getTotalCount : function(){
13603         return this.totalLength || 0;
13604     },
13605
13606     /**
13607      * Returns the sort state of the Store as an object with two properties:
13608      * <pre><code>
13609  field {String} The name of the field by which the Records are sorted
13610  direction {String} The sort order, "ASC" or "DESC"
13611      * </code></pre>
13612      */
13613     getSortState : function(){
13614         return this.sortInfo;
13615     },
13616
13617     // private
13618     applySort : function(){
13619         if(this.sortInfo && !this.remoteSort){
13620             var s = this.sortInfo, f = s.field;
13621             var st = this.fields.get(f).sortType;
13622             var fn = function(r1, r2){
13623                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13624                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13625             };
13626             this.data.sort(s.direction, fn);
13627             if(this.snapshot && this.snapshot != this.data){
13628                 this.snapshot.sort(s.direction, fn);
13629             }
13630         }
13631     },
13632
13633     /**
13634      * Sets the default sort column and order to be used by the next load operation.
13635      * @param {String} fieldName The name of the field to sort by.
13636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13637      */
13638     setDefaultSort : function(field, dir){
13639         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13640     },
13641
13642     /**
13643      * Sort the Records.
13644      * If remote sorting is used, the sort is performed on the server, and the cache is
13645      * reloaded. If local sorting is used, the cache is sorted internally.
13646      * @param {String} fieldName The name of the field to sort by.
13647      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13648      */
13649     sort : function(fieldName, dir){
13650         var f = this.fields.get(fieldName);
13651         if(!dir){
13652             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13653             
13654             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13655                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13656             }else{
13657                 dir = f.sortDir;
13658             }
13659         }
13660         this.sortToggle[f.name] = dir;
13661         this.sortInfo = {field: f.name, direction: dir};
13662         if(!this.remoteSort){
13663             this.applySort();
13664             this.fireEvent("datachanged", this);
13665         }else{
13666             this.load(this.lastOptions);
13667         }
13668     },
13669
13670     /**
13671      * Calls the specified function for each of the Records in the cache.
13672      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13673      * Returning <em>false</em> aborts and exits the iteration.
13674      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13675      */
13676     each : function(fn, scope){
13677         this.data.each(fn, scope);
13678     },
13679
13680     /**
13681      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13682      * (e.g., during paging).
13683      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13684      */
13685     getModifiedRecords : function(){
13686         return this.modified;
13687     },
13688
13689     // private
13690     createFilterFn : function(property, value, anyMatch){
13691         if(!value.exec){ // not a regex
13692             value = String(value);
13693             if(value.length == 0){
13694                 return false;
13695             }
13696             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13697         }
13698         return function(r){
13699             return value.test(r.data[property]);
13700         };
13701     },
13702
13703     /**
13704      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13705      * @param {String} property A field on your records
13706      * @param {Number} start The record index to start at (defaults to 0)
13707      * @param {Number} end The last record index to include (defaults to length - 1)
13708      * @return {Number} The sum
13709      */
13710     sum : function(property, start, end){
13711         var rs = this.data.items, v = 0;
13712         start = start || 0;
13713         end = (end || end === 0) ? end : rs.length-1;
13714
13715         for(var i = start; i <= end; i++){
13716             v += (rs[i].data[property] || 0);
13717         }
13718         return v;
13719     },
13720
13721     /**
13722      * Filter the records by a specified property.
13723      * @param {String} field A field on your records
13724      * @param {String/RegExp} value Either a string that the field
13725      * should start with or a RegExp to test against the field
13726      * @param {Boolean} anyMatch True to match any part not just the beginning
13727      */
13728     filter : function(property, value, anyMatch){
13729         var fn = this.createFilterFn(property, value, anyMatch);
13730         return fn ? this.filterBy(fn) : this.clearFilter();
13731     },
13732
13733     /**
13734      * Filter by a function. The specified function will be called with each
13735      * record in this data source. If the function returns true the record is included,
13736      * otherwise it is filtered.
13737      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13738      * @param {Object} scope (optional) The scope of the function (defaults to this)
13739      */
13740     filterBy : function(fn, scope){
13741         this.snapshot = this.snapshot || this.data;
13742         this.data = this.queryBy(fn, scope||this);
13743         this.fireEvent("datachanged", this);
13744     },
13745
13746     /**
13747      * Query the records by a specified property.
13748      * @param {String} field A field on your records
13749      * @param {String/RegExp} value Either a string that the field
13750      * should start with or a RegExp to test against the field
13751      * @param {Boolean} anyMatch True to match any part not just the beginning
13752      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13753      */
13754     query : function(property, value, anyMatch){
13755         var fn = this.createFilterFn(property, value, anyMatch);
13756         return fn ? this.queryBy(fn) : this.data.clone();
13757     },
13758
13759     /**
13760      * Query by a function. The specified function will be called with each
13761      * record in this data source. If the function returns true the record is included
13762      * in the results.
13763      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13764      * @param {Object} scope (optional) The scope of the function (defaults to this)
13765       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13766      **/
13767     queryBy : function(fn, scope){
13768         var data = this.snapshot || this.data;
13769         return data.filterBy(fn, scope||this);
13770     },
13771
13772     /**
13773      * Collects unique values for a particular dataIndex from this store.
13774      * @param {String} dataIndex The property to collect
13775      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13776      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13777      * @return {Array} An array of the unique values
13778      **/
13779     collect : function(dataIndex, allowNull, bypassFilter){
13780         var d = (bypassFilter === true && this.snapshot) ?
13781                 this.snapshot.items : this.data.items;
13782         var v, sv, r = [], l = {};
13783         for(var i = 0, len = d.length; i < len; i++){
13784             v = d[i].data[dataIndex];
13785             sv = String(v);
13786             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13787                 l[sv] = true;
13788                 r[r.length] = v;
13789             }
13790         }
13791         return r;
13792     },
13793
13794     /**
13795      * Revert to a view of the Record cache with no filtering applied.
13796      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13797      */
13798     clearFilter : function(suppressEvent){
13799         if(this.snapshot && this.snapshot != this.data){
13800             this.data = this.snapshot;
13801             delete this.snapshot;
13802             if(suppressEvent !== true){
13803                 this.fireEvent("datachanged", this);
13804             }
13805         }
13806     },
13807
13808     // private
13809     afterEdit : function(record){
13810         if(this.modified.indexOf(record) == -1){
13811             this.modified.push(record);
13812         }
13813         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13814     },
13815     
13816     // private
13817     afterReject : function(record){
13818         this.modified.remove(record);
13819         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13820     },
13821
13822     // private
13823     afterCommit : function(record){
13824         this.modified.remove(record);
13825         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13826     },
13827
13828     /**
13829      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13830      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13831      */
13832     commitChanges : function(){
13833         var m = this.modified.slice(0);
13834         this.modified = [];
13835         for(var i = 0, len = m.length; i < len; i++){
13836             m[i].commit();
13837         }
13838     },
13839
13840     /**
13841      * Cancel outstanding changes on all changed records.
13842      */
13843     rejectChanges : function(){
13844         var m = this.modified.slice(0);
13845         this.modified = [];
13846         for(var i = 0, len = m.length; i < len; i++){
13847             m[i].reject();
13848         }
13849     },
13850
13851     onMetaChange : function(meta, rtype, o){
13852         this.recordType = rtype;
13853         this.fields = rtype.prototype.fields;
13854         delete this.snapshot;
13855         this.sortInfo = meta.sortInfo || this.sortInfo;
13856         this.modified = [];
13857         this.fireEvent('metachange', this, this.reader.meta);
13858     },
13859     
13860     moveIndex : function(data, type)
13861     {
13862         var index = this.indexOf(data);
13863         
13864         var newIndex = index + type;
13865         
13866         this.remove(data);
13867         
13868         this.insert(newIndex, data);
13869         
13870     }
13871 });/*
13872  * Based on:
13873  * Ext JS Library 1.1.1
13874  * Copyright(c) 2006-2007, Ext JS, LLC.
13875  *
13876  * Originally Released Under LGPL - original licence link has changed is not relivant.
13877  *
13878  * Fork - LGPL
13879  * <script type="text/javascript">
13880  */
13881
13882 /**
13883  * @class Roo.data.SimpleStore
13884  * @extends Roo.data.Store
13885  * Small helper class to make creating Stores from Array data easier.
13886  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13887  * @cfg {Array} fields An array of field definition objects, or field name strings.
13888  * @cfg {Object} an existing reader (eg. copied from another store)
13889  * @cfg {Array} data The multi-dimensional array of data
13890  * @constructor
13891  * @param {Object} config
13892  */
13893 Roo.data.SimpleStore = function(config)
13894 {
13895     Roo.data.SimpleStore.superclass.constructor.call(this, {
13896         isLocal : true,
13897         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13898                 id: config.id
13899             },
13900             Roo.data.Record.create(config.fields)
13901         ),
13902         proxy : new Roo.data.MemoryProxy(config.data)
13903     });
13904     this.load();
13905 };
13906 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916
13917 /**
13918 /**
13919  * @extends Roo.data.Store
13920  * @class Roo.data.JsonStore
13921  * Small helper class to make creating Stores for JSON data easier. <br/>
13922 <pre><code>
13923 var store = new Roo.data.JsonStore({
13924     url: 'get-images.php',
13925     root: 'images',
13926     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13927 });
13928 </code></pre>
13929  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13930  * JsonReader and HttpProxy (unless inline data is provided).</b>
13931  * @cfg {Array} fields An array of field definition objects, or field name strings.
13932  * @constructor
13933  * @param {Object} config
13934  */
13935 Roo.data.JsonStore = function(c){
13936     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13937         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13938         reader: new Roo.data.JsonReader(c, c.fields)
13939     }));
13940 };
13941 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13942  * Based on:
13943  * Ext JS Library 1.1.1
13944  * Copyright(c) 2006-2007, Ext JS, LLC.
13945  *
13946  * Originally Released Under LGPL - original licence link has changed is not relivant.
13947  *
13948  * Fork - LGPL
13949  * <script type="text/javascript">
13950  */
13951
13952  
13953 Roo.data.Field = function(config){
13954     if(typeof config == "string"){
13955         config = {name: config};
13956     }
13957     Roo.apply(this, config);
13958     
13959     if(!this.type){
13960         this.type = "auto";
13961     }
13962     
13963     var st = Roo.data.SortTypes;
13964     // named sortTypes are supported, here we look them up
13965     if(typeof this.sortType == "string"){
13966         this.sortType = st[this.sortType];
13967     }
13968     
13969     // set default sortType for strings and dates
13970     if(!this.sortType){
13971         switch(this.type){
13972             case "string":
13973                 this.sortType = st.asUCString;
13974                 break;
13975             case "date":
13976                 this.sortType = st.asDate;
13977                 break;
13978             default:
13979                 this.sortType = st.none;
13980         }
13981     }
13982
13983     // define once
13984     var stripRe = /[\$,%]/g;
13985
13986     // prebuilt conversion function for this field, instead of
13987     // switching every time we're reading a value
13988     if(!this.convert){
13989         var cv, dateFormat = this.dateFormat;
13990         switch(this.type){
13991             case "":
13992             case "auto":
13993             case undefined:
13994                 cv = function(v){ return v; };
13995                 break;
13996             case "string":
13997                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13998                 break;
13999             case "int":
14000                 cv = function(v){
14001                     return v !== undefined && v !== null && v !== '' ?
14002                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14003                     };
14004                 break;
14005             case "float":
14006                 cv = function(v){
14007                     return v !== undefined && v !== null && v !== '' ?
14008                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14009                     };
14010                 break;
14011             case "bool":
14012             case "boolean":
14013                 cv = function(v){ return v === true || v === "true" || v == 1; };
14014                 break;
14015             case "date":
14016                 cv = function(v){
14017                     if(!v){
14018                         return '';
14019                     }
14020                     if(v instanceof Date){
14021                         return v;
14022                     }
14023                     if(dateFormat){
14024                         if(dateFormat == "timestamp"){
14025                             return new Date(v*1000);
14026                         }
14027                         return Date.parseDate(v, dateFormat);
14028                     }
14029                     var parsed = Date.parse(v);
14030                     return parsed ? new Date(parsed) : null;
14031                 };
14032              break;
14033             
14034         }
14035         this.convert = cv;
14036     }
14037 };
14038
14039 Roo.data.Field.prototype = {
14040     dateFormat: null,
14041     defaultValue: "",
14042     mapping: null,
14043     sortType : null,
14044     sortDir : "ASC"
14045 };/*
14046  * Based on:
14047  * Ext JS Library 1.1.1
14048  * Copyright(c) 2006-2007, Ext JS, LLC.
14049  *
14050  * Originally Released Under LGPL - original licence link has changed is not relivant.
14051  *
14052  * Fork - LGPL
14053  * <script type="text/javascript">
14054  */
14055  
14056 // Base class for reading structured data from a data source.  This class is intended to be
14057 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14058
14059 /**
14060  * @class Roo.data.DataReader
14061  * Base class for reading structured data from a data source.  This class is intended to be
14062  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14063  */
14064
14065 Roo.data.DataReader = function(meta, recordType){
14066     
14067     this.meta = meta;
14068     
14069     this.recordType = recordType instanceof Array ? 
14070         Roo.data.Record.create(recordType) : recordType;
14071 };
14072
14073 Roo.data.DataReader.prototype = {
14074     
14075     
14076     readerType : 'Data',
14077      /**
14078      * Create an empty record
14079      * @param {Object} data (optional) - overlay some values
14080      * @return {Roo.data.Record} record created.
14081      */
14082     newRow :  function(d) {
14083         var da =  {};
14084         this.recordType.prototype.fields.each(function(c) {
14085             switch( c.type) {
14086                 case 'int' : da[c.name] = 0; break;
14087                 case 'date' : da[c.name] = new Date(); break;
14088                 case 'float' : da[c.name] = 0.0; break;
14089                 case 'boolean' : da[c.name] = false; break;
14090                 default : da[c.name] = ""; break;
14091             }
14092             
14093         });
14094         return new this.recordType(Roo.apply(da, d));
14095     }
14096     
14097     
14098 };/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108
14109 /**
14110  * @class Roo.data.DataProxy
14111  * @extends Roo.data.Observable
14112  * This class is an abstract base class for implementations which provide retrieval of
14113  * unformatted data objects.<br>
14114  * <p>
14115  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14116  * (of the appropriate type which knows how to parse the data object) to provide a block of
14117  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14118  * <p>
14119  * Custom implementations must implement the load method as described in
14120  * {@link Roo.data.HttpProxy#load}.
14121  */
14122 Roo.data.DataProxy = function(){
14123     this.addEvents({
14124         /**
14125          * @event beforeload
14126          * Fires before a network request is made to retrieve a data object.
14127          * @param {Object} This DataProxy object.
14128          * @param {Object} params The params parameter to the load function.
14129          */
14130         beforeload : true,
14131         /**
14132          * @event load
14133          * Fires before the load method's callback is called.
14134          * @param {Object} This DataProxy object.
14135          * @param {Object} o The data object.
14136          * @param {Object} arg The callback argument object passed to the load function.
14137          */
14138         load : true,
14139         /**
14140          * @event loadexception
14141          * Fires if an Exception occurs during data retrieval.
14142          * @param {Object} This DataProxy object.
14143          * @param {Object} o The data object.
14144          * @param {Object} arg The callback argument object passed to the load function.
14145          * @param {Object} e The Exception.
14146          */
14147         loadexception : true
14148     });
14149     Roo.data.DataProxy.superclass.constructor.call(this);
14150 };
14151
14152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14153
14154     /**
14155      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14156      */
14157 /*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167 /**
14168  * @class Roo.data.MemoryProxy
14169  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14170  * to the Reader when its load method is called.
14171  * @constructor
14172  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14173  */
14174 Roo.data.MemoryProxy = function(data){
14175     if (data.data) {
14176         data = data.data;
14177     }
14178     Roo.data.MemoryProxy.superclass.constructor.call(this);
14179     this.data = data;
14180 };
14181
14182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14183     
14184     /**
14185      * Load data from the requested source (in this case an in-memory
14186      * data object passed to the constructor), read the data object into
14187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14188      * process that block using the passed callback.
14189      * @param {Object} params This parameter is not used by the MemoryProxy class.
14190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14191      * object into a block of Roo.data.Records.
14192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14193      * The function must be passed <ul>
14194      * <li>The Record block object</li>
14195      * <li>The "arg" argument from the load function</li>
14196      * <li>A boolean success indicator</li>
14197      * </ul>
14198      * @param {Object} scope The scope in which to call the callback
14199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14200      */
14201     load : function(params, reader, callback, scope, arg){
14202         params = params || {};
14203         var result;
14204         try {
14205             result = reader.readRecords(params.data ? params.data :this.data);
14206         }catch(e){
14207             this.fireEvent("loadexception", this, arg, null, e);
14208             callback.call(scope, null, arg, false);
14209             return;
14210         }
14211         callback.call(scope, result, arg, true);
14212     },
14213     
14214     // private
14215     update : function(params, records){
14216         
14217     }
14218 });/*
14219  * Based on:
14220  * Ext JS Library 1.1.1
14221  * Copyright(c) 2006-2007, Ext JS, LLC.
14222  *
14223  * Originally Released Under LGPL - original licence link has changed is not relivant.
14224  *
14225  * Fork - LGPL
14226  * <script type="text/javascript">
14227  */
14228 /**
14229  * @class Roo.data.HttpProxy
14230  * @extends Roo.data.DataProxy
14231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14232  * configured to reference a certain URL.<br><br>
14233  * <p>
14234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14235  * from which the running page was served.<br><br>
14236  * <p>
14237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14238  * <p>
14239  * Be aware that to enable the browser to parse an XML document, the server must set
14240  * the Content-Type header in the HTTP response to "text/xml".
14241  * @constructor
14242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14244  * will be used to make the request.
14245  */
14246 Roo.data.HttpProxy = function(conn){
14247     Roo.data.HttpProxy.superclass.constructor.call(this);
14248     // is conn a conn config or a real conn?
14249     this.conn = conn;
14250     this.useAjax = !conn || !conn.events;
14251   
14252 };
14253
14254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14255     // thse are take from connection...
14256     
14257     /**
14258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14259      */
14260     /**
14261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14262      * extra parameters to each request made by this object. (defaults to undefined)
14263      */
14264     /**
14265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14266      *  to each request made by this object. (defaults to undefined)
14267      */
14268     /**
14269      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
14270      */
14271     /**
14272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14273      */
14274      /**
14275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14276      * @type Boolean
14277      */
14278   
14279
14280     /**
14281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14282      * @type Boolean
14283      */
14284     /**
14285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14287      * a finer-grained basis than the DataProxy events.
14288      */
14289     getConnection : function(){
14290         return this.useAjax ? Roo.Ajax : this.conn;
14291     },
14292
14293     /**
14294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14296      * process that block using the passed callback.
14297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14298      * for the request to the remote server.
14299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14300      * object into a block of Roo.data.Records.
14301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14302      * The function must be passed <ul>
14303      * <li>The Record block object</li>
14304      * <li>The "arg" argument from the load function</li>
14305      * <li>A boolean success indicator</li>
14306      * </ul>
14307      * @param {Object} scope The scope in which to call the callback
14308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14309      */
14310     load : function(params, reader, callback, scope, arg){
14311         if(this.fireEvent("beforeload", this, params) !== false){
14312             var  o = {
14313                 params : params || {},
14314                 request: {
14315                     callback : callback,
14316                     scope : scope,
14317                     arg : arg
14318                 },
14319                 reader: reader,
14320                 callback : this.loadResponse,
14321                 scope: this
14322             };
14323             if(this.useAjax){
14324                 Roo.applyIf(o, this.conn);
14325                 if(this.activeRequest){
14326                     Roo.Ajax.abort(this.activeRequest);
14327                 }
14328                 this.activeRequest = Roo.Ajax.request(o);
14329             }else{
14330                 this.conn.request(o);
14331             }
14332         }else{
14333             callback.call(scope||this, null, arg, false);
14334         }
14335     },
14336
14337     // private
14338     loadResponse : function(o, success, response){
14339         delete this.activeRequest;
14340         if(!success){
14341             this.fireEvent("loadexception", this, o, response);
14342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14343             return;
14344         }
14345         var result;
14346         try {
14347             result = o.reader.read(response);
14348         }catch(e){
14349             this.fireEvent("loadexception", this, o, response, e);
14350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14351             return;
14352         }
14353         
14354         this.fireEvent("load", this, o, o.request.arg);
14355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14356     },
14357
14358     // private
14359     update : function(dataSet){
14360
14361     },
14362
14363     // private
14364     updateResponse : function(dataSet){
14365
14366     }
14367 });/*
14368  * Based on:
14369  * Ext JS Library 1.1.1
14370  * Copyright(c) 2006-2007, Ext JS, LLC.
14371  *
14372  * Originally Released Under LGPL - original licence link has changed is not relivant.
14373  *
14374  * Fork - LGPL
14375  * <script type="text/javascript">
14376  */
14377
14378 /**
14379  * @class Roo.data.ScriptTagProxy
14380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14381  * other than the originating domain of the running page.<br><br>
14382  * <p>
14383  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
14384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14385  * <p>
14386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14387  * source code that is used as the source inside a &lt;script> tag.<br><br>
14388  * <p>
14389  * In order for the browser to process the returned data, the server must wrap the data object
14390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14392  * depending on whether the callback name was passed:
14393  * <p>
14394  * <pre><code>
14395 boolean scriptTag = false;
14396 String cb = request.getParameter("callback");
14397 if (cb != null) {
14398     scriptTag = true;
14399     response.setContentType("text/javascript");
14400 } else {
14401     response.setContentType("application/x-json");
14402 }
14403 Writer out = response.getWriter();
14404 if (scriptTag) {
14405     out.write(cb + "(");
14406 }
14407 out.print(dataBlock.toJsonString());
14408 if (scriptTag) {
14409     out.write(");");
14410 }
14411 </pre></code>
14412  *
14413  * @constructor
14414  * @param {Object} config A configuration object.
14415  */
14416 Roo.data.ScriptTagProxy = function(config){
14417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14418     Roo.apply(this, config);
14419     this.head = document.getElementsByTagName("head")[0];
14420 };
14421
14422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14423
14424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14425     /**
14426      * @cfg {String} url The URL from which to request the data object.
14427      */
14428     /**
14429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14430      */
14431     timeout : 30000,
14432     /**
14433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14434      * the server the name of the callback function set up by the load call to process the returned data object.
14435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14436      * javascript output which calls this named function passing the data object as its only parameter.
14437      */
14438     callbackParam : "callback",
14439     /**
14440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14441      * name to the request.
14442      */
14443     nocache : true,
14444
14445     /**
14446      * Load data from the configured URL, read the data object into
14447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14448      * process that block using the passed callback.
14449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14450      * for the request to the remote server.
14451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14452      * object into a block of Roo.data.Records.
14453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14454      * The function must be passed <ul>
14455      * <li>The Record block object</li>
14456      * <li>The "arg" argument from the load function</li>
14457      * <li>A boolean success indicator</li>
14458      * </ul>
14459      * @param {Object} scope The scope in which to call the callback
14460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14461      */
14462     load : function(params, reader, callback, scope, arg){
14463         if(this.fireEvent("beforeload", this, params) !== false){
14464
14465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14466
14467             var url = this.url;
14468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14469             if(this.nocache){
14470                 url += "&_dc=" + (new Date().getTime());
14471             }
14472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14473             var trans = {
14474                 id : transId,
14475                 cb : "stcCallback"+transId,
14476                 scriptId : "stcScript"+transId,
14477                 params : params,
14478                 arg : arg,
14479                 url : url,
14480                 callback : callback,
14481                 scope : scope,
14482                 reader : reader
14483             };
14484             var conn = this;
14485
14486             window[trans.cb] = function(o){
14487                 conn.handleResponse(o, trans);
14488             };
14489
14490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14491
14492             if(this.autoAbort !== false){
14493                 this.abort();
14494             }
14495
14496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14497
14498             var script = document.createElement("script");
14499             script.setAttribute("src", url);
14500             script.setAttribute("type", "text/javascript");
14501             script.setAttribute("id", trans.scriptId);
14502             this.head.appendChild(script);
14503
14504             this.trans = trans;
14505         }else{
14506             callback.call(scope||this, null, arg, false);
14507         }
14508     },
14509
14510     // private
14511     isLoading : function(){
14512         return this.trans ? true : false;
14513     },
14514
14515     /**
14516      * Abort the current server request.
14517      */
14518     abort : function(){
14519         if(this.isLoading()){
14520             this.destroyTrans(this.trans);
14521         }
14522     },
14523
14524     // private
14525     destroyTrans : function(trans, isLoaded){
14526         this.head.removeChild(document.getElementById(trans.scriptId));
14527         clearTimeout(trans.timeoutId);
14528         if(isLoaded){
14529             window[trans.cb] = undefined;
14530             try{
14531                 delete window[trans.cb];
14532             }catch(e){}
14533         }else{
14534             // if hasn't been loaded, wait for load to remove it to prevent script error
14535             window[trans.cb] = function(){
14536                 window[trans.cb] = undefined;
14537                 try{
14538                     delete window[trans.cb];
14539                 }catch(e){}
14540             };
14541         }
14542     },
14543
14544     // private
14545     handleResponse : function(o, trans){
14546         this.trans = false;
14547         this.destroyTrans(trans, true);
14548         var result;
14549         try {
14550             result = trans.reader.readRecords(o);
14551         }catch(e){
14552             this.fireEvent("loadexception", this, o, trans.arg, e);
14553             trans.callback.call(trans.scope||window, null, trans.arg, false);
14554             return;
14555         }
14556         this.fireEvent("load", this, o, trans.arg);
14557         trans.callback.call(trans.scope||window, result, trans.arg, true);
14558     },
14559
14560     // private
14561     handleFailure : function(trans){
14562         this.trans = false;
14563         this.destroyTrans(trans, false);
14564         this.fireEvent("loadexception", this, null, trans.arg);
14565         trans.callback.call(trans.scope||window, null, trans.arg, false);
14566     }
14567 });/*
14568  * Based on:
14569  * Ext JS Library 1.1.1
14570  * Copyright(c) 2006-2007, Ext JS, LLC.
14571  *
14572  * Originally Released Under LGPL - original licence link has changed is not relivant.
14573  *
14574  * Fork - LGPL
14575  * <script type="text/javascript">
14576  */
14577
14578 /**
14579  * @class Roo.data.JsonReader
14580  * @extends Roo.data.DataReader
14581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14582  * based on mappings in a provided Roo.data.Record constructor.
14583  * 
14584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14585  * in the reply previously. 
14586  * 
14587  * <p>
14588  * Example code:
14589  * <pre><code>
14590 var RecordDef = Roo.data.Record.create([
14591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14593 ]);
14594 var myReader = new Roo.data.JsonReader({
14595     totalProperty: "results",    // The property which contains the total dataset size (optional)
14596     root: "rows",                // The property which contains an Array of row objects
14597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14598 }, RecordDef);
14599 </code></pre>
14600  * <p>
14601  * This would consume a JSON file like this:
14602  * <pre><code>
14603 { 'results': 2, 'rows': [
14604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14606 }
14607 </code></pre>
14608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14610  * paged from the remote server.
14611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14612  * @cfg {String} root name of the property which contains the Array of row objects.
14613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14614  * @cfg {Array} fields Array of field definition objects
14615  * @constructor
14616  * Create a new JsonReader
14617  * @param {Object} meta Metadata configuration options
14618  * @param {Object} recordType Either an Array of field definition objects,
14619  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14620  */
14621 Roo.data.JsonReader = function(meta, recordType){
14622     
14623     meta = meta || {};
14624     // set some defaults:
14625     Roo.applyIf(meta, {
14626         totalProperty: 'total',
14627         successProperty : 'success',
14628         root : 'data',
14629         id : 'id'
14630     });
14631     
14632     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14633 };
14634 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14635     
14636     readerType : 'Json',
14637     
14638     /**
14639      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14640      * Used by Store query builder to append _requestMeta to params.
14641      * 
14642      */
14643     metaFromRemote : false,
14644     /**
14645      * This method is only used by a DataProxy which has retrieved data from a remote server.
14646      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14647      * @return {Object} data A data block which is used by an Roo.data.Store object as
14648      * a cache of Roo.data.Records.
14649      */
14650     read : function(response){
14651         var json = response.responseText;
14652        
14653         var o = /* eval:var:o */ eval("("+json+")");
14654         if(!o) {
14655             throw {message: "JsonReader.read: Json object not found"};
14656         }
14657         
14658         if(o.metaData){
14659             
14660             delete this.ef;
14661             this.metaFromRemote = true;
14662             this.meta = o.metaData;
14663             this.recordType = Roo.data.Record.create(o.metaData.fields);
14664             this.onMetaChange(this.meta, this.recordType, o);
14665         }
14666         return this.readRecords(o);
14667     },
14668
14669     // private function a store will implement
14670     onMetaChange : function(meta, recordType, o){
14671
14672     },
14673
14674     /**
14675          * @ignore
14676          */
14677     simpleAccess: function(obj, subsc) {
14678         return obj[subsc];
14679     },
14680
14681         /**
14682          * @ignore
14683          */
14684     getJsonAccessor: function(){
14685         var re = /[\[\.]/;
14686         return function(expr) {
14687             try {
14688                 return(re.test(expr))
14689                     ? new Function("obj", "return obj." + expr)
14690                     : function(obj){
14691                         return obj[expr];
14692                     };
14693             } catch(e){}
14694             return Roo.emptyFn;
14695         };
14696     }(),
14697
14698     /**
14699      * Create a data block containing Roo.data.Records from an XML document.
14700      * @param {Object} o An object which contains an Array of row objects in the property specified
14701      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14702      * which contains the total size of the dataset.
14703      * @return {Object} data A data block which is used by an Roo.data.Store object as
14704      * a cache of Roo.data.Records.
14705      */
14706     readRecords : function(o){
14707         /**
14708          * After any data loads, the raw JSON data is available for further custom processing.
14709          * @type Object
14710          */
14711         this.o = o;
14712         var s = this.meta, Record = this.recordType,
14713             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14714
14715 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14716         if (!this.ef) {
14717             if(s.totalProperty) {
14718                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14719                 }
14720                 if(s.successProperty) {
14721                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14722                 }
14723                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14724                 if (s.id) {
14725                         var g = this.getJsonAccessor(s.id);
14726                         this.getId = function(rec) {
14727                                 var r = g(rec);  
14728                                 return (r === undefined || r === "") ? null : r;
14729                         };
14730                 } else {
14731                         this.getId = function(){return null;};
14732                 }
14733             this.ef = [];
14734             for(var jj = 0; jj < fl; jj++){
14735                 f = fi[jj];
14736                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14737                 this.ef[jj] = this.getJsonAccessor(map);
14738             }
14739         }
14740
14741         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14742         if(s.totalProperty){
14743             var vt = parseInt(this.getTotal(o), 10);
14744             if(!isNaN(vt)){
14745                 totalRecords = vt;
14746             }
14747         }
14748         if(s.successProperty){
14749             var vs = this.getSuccess(o);
14750             if(vs === false || vs === 'false'){
14751                 success = false;
14752             }
14753         }
14754         var records = [];
14755         for(var i = 0; i < c; i++){
14756                 var n = root[i];
14757             var values = {};
14758             var id = this.getId(n);
14759             for(var j = 0; j < fl; j++){
14760                 f = fi[j];
14761             var v = this.ef[j](n);
14762             if (!f.convert) {
14763                 Roo.log('missing convert for ' + f.name);
14764                 Roo.log(f);
14765                 continue;
14766             }
14767             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14768             }
14769             var record = new Record(values, id);
14770             record.json = n;
14771             records[i] = record;
14772         }
14773         return {
14774             raw : o,
14775             success : success,
14776             records : records,
14777             totalRecords : totalRecords
14778         };
14779     },
14780     // used when loading children.. @see loadDataFromChildren
14781     toLoadData: function(rec)
14782     {
14783         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14784         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14785         return { data : data, total : data.length };
14786         
14787     }
14788 });/*
14789  * Based on:
14790  * Ext JS Library 1.1.1
14791  * Copyright(c) 2006-2007, Ext JS, LLC.
14792  *
14793  * Originally Released Under LGPL - original licence link has changed is not relivant.
14794  *
14795  * Fork - LGPL
14796  * <script type="text/javascript">
14797  */
14798
14799 /**
14800  * @class Roo.data.ArrayReader
14801  * @extends Roo.data.DataReader
14802  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14803  * Each element of that Array represents a row of data fields. The
14804  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14805  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14806  * <p>
14807  * Example code:.
14808  * <pre><code>
14809 var RecordDef = Roo.data.Record.create([
14810     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14811     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14812 ]);
14813 var myReader = new Roo.data.ArrayReader({
14814     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14815 }, RecordDef);
14816 </code></pre>
14817  * <p>
14818  * This would consume an Array like this:
14819  * <pre><code>
14820 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14821   </code></pre>
14822  
14823  * @constructor
14824  * Create a new JsonReader
14825  * @param {Object} meta Metadata configuration options.
14826  * @param {Object|Array} recordType Either an Array of field definition objects
14827  * 
14828  * @cfg {Array} fields Array of field definition objects
14829  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14830  * as specified to {@link Roo.data.Record#create},
14831  * or an {@link Roo.data.Record} object
14832  *
14833  * 
14834  * created using {@link Roo.data.Record#create}.
14835  */
14836 Roo.data.ArrayReader = function(meta, recordType)
14837 {    
14838     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14839 };
14840
14841 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14842     
14843       /**
14844      * Create a data block containing Roo.data.Records from an XML document.
14845      * @param {Object} o An Array of row objects which represents the dataset.
14846      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14847      * a cache of Roo.data.Records.
14848      */
14849     readRecords : function(o)
14850     {
14851         var sid = this.meta ? this.meta.id : null;
14852         var recordType = this.recordType, fields = recordType.prototype.fields;
14853         var records = [];
14854         var root = o;
14855         for(var i = 0; i < root.length; i++){
14856                 var n = root[i];
14857             var values = {};
14858             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14859             for(var j = 0, jlen = fields.length; j < jlen; j++){
14860                 var f = fields.items[j];
14861                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14862                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14863                 v = f.convert(v);
14864                 values[f.name] = v;
14865             }
14866             var record = new recordType(values, id);
14867             record.json = n;
14868             records[records.length] = record;
14869         }
14870         return {
14871             records : records,
14872             totalRecords : records.length
14873         };
14874     },
14875     // used when loading children.. @see loadDataFromChildren
14876     toLoadData: function(rec)
14877     {
14878         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14879         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14880         
14881     }
14882     
14883     
14884 });/*
14885  * - LGPL
14886  * * 
14887  */
14888
14889 /**
14890  * @class Roo.bootstrap.ComboBox
14891  * @extends Roo.bootstrap.TriggerField
14892  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14893  * @cfg {Boolean} append (true|false) default false
14894  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14895  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14896  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14897  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14898  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14899  * @cfg {Boolean} animate default true
14900  * @cfg {Boolean} emptyResultText only for touch device
14901  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14902  * @cfg {String} emptyTitle default ''
14903  * @cfg {Number} width fixed with? experimental
14904  * @constructor
14905  * Create a new ComboBox.
14906  * @param {Object} config Configuration options
14907  */
14908 Roo.bootstrap.ComboBox = function(config){
14909     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14910     this.addEvents({
14911         /**
14912          * @event expand
14913          * Fires when the dropdown list is expanded
14914         * @param {Roo.bootstrap.ComboBox} combo This combo box
14915         */
14916         'expand' : true,
14917         /**
14918          * @event collapse
14919          * Fires when the dropdown list is collapsed
14920         * @param {Roo.bootstrap.ComboBox} combo This combo box
14921         */
14922         'collapse' : true,
14923         /**
14924          * @event beforeselect
14925          * Fires before a list item is selected. Return false to cancel the selection.
14926         * @param {Roo.bootstrap.ComboBox} combo This combo box
14927         * @param {Roo.data.Record} record The data record returned from the underlying store
14928         * @param {Number} index The index of the selected item in the dropdown list
14929         */
14930         'beforeselect' : true,
14931         /**
14932          * @event select
14933          * Fires when a list item is selected
14934         * @param {Roo.bootstrap.ComboBox} combo This combo box
14935         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14936         * @param {Number} index The index of the selected item in the dropdown list
14937         */
14938         'select' : true,
14939         /**
14940          * @event beforequery
14941          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14942          * The event object passed has these properties:
14943         * @param {Roo.bootstrap.ComboBox} combo This combo box
14944         * @param {String} query The query
14945         * @param {Boolean} forceAll true to force "all" query
14946         * @param {Boolean} cancel true to cancel the query
14947         * @param {Object} e The query event object
14948         */
14949         'beforequery': true,
14950          /**
14951          * @event add
14952          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14953         * @param {Roo.bootstrap.ComboBox} combo This combo box
14954         */
14955         'add' : true,
14956         /**
14957          * @event edit
14958          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14959         * @param {Roo.bootstrap.ComboBox} combo This combo box
14960         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14961         */
14962         'edit' : true,
14963         /**
14964          * @event remove
14965          * Fires when the remove value from the combobox array
14966         * @param {Roo.bootstrap.ComboBox} combo This combo box
14967         */
14968         'remove' : true,
14969         /**
14970          * @event afterremove
14971          * Fires when the remove value from the combobox array
14972         * @param {Roo.bootstrap.ComboBox} combo This combo box
14973         */
14974         'afterremove' : true,
14975         /**
14976          * @event specialfilter
14977          * Fires when specialfilter
14978             * @param {Roo.bootstrap.ComboBox} combo This combo box
14979             */
14980         'specialfilter' : true,
14981         /**
14982          * @event tick
14983          * Fires when tick the element
14984             * @param {Roo.bootstrap.ComboBox} combo This combo box
14985             */
14986         'tick' : true,
14987         /**
14988          * @event touchviewdisplay
14989          * Fires when touch view require special display (default is using displayField)
14990             * @param {Roo.bootstrap.ComboBox} combo This combo box
14991             * @param {Object} cfg set html .
14992             */
14993         'touchviewdisplay' : true
14994         
14995     });
14996     
14997     this.item = [];
14998     this.tickItems = [];
14999     
15000     this.selectedIndex = -1;
15001     if(this.mode == 'local'){
15002         if(config.queryDelay === undefined){
15003             this.queryDelay = 10;
15004         }
15005         if(config.minChars === undefined){
15006             this.minChars = 0;
15007         }
15008     }
15009 };
15010
15011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15012      
15013     /**
15014      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15015      * rendering into an Roo.Editor, defaults to false)
15016      */
15017     /**
15018      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15019      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15020      */
15021     /**
15022      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15023      */
15024     /**
15025      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15026      * the dropdown list (defaults to undefined, with no header element)
15027      */
15028
15029      /**
15030      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15031      */
15032      
15033      /**
15034      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15035      */
15036     listWidth: undefined,
15037     /**
15038      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15039      * mode = 'remote' or 'text' if mode = 'local')
15040      */
15041     displayField: undefined,
15042     
15043     /**
15044      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15045      * mode = 'remote' or 'value' if mode = 'local'). 
15046      * Note: use of a valueField requires the user make a selection
15047      * in order for a value to be mapped.
15048      */
15049     valueField: undefined,
15050     /**
15051      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15052      */
15053     modalTitle : '',
15054     
15055     /**
15056      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15057      * field's data value (defaults to the underlying DOM element's name)
15058      */
15059     hiddenName: undefined,
15060     /**
15061      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15062      */
15063     listClass: '',
15064     /**
15065      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15066      */
15067     selectedClass: 'active',
15068     
15069     /**
15070      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15071      */
15072     shadow:'sides',
15073     /**
15074      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15075      * anchor positions (defaults to 'tl-bl')
15076      */
15077     listAlign: 'tl-bl?',
15078     /**
15079      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15080      */
15081     maxHeight: 300,
15082     /**
15083      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15084      * query specified by the allQuery config option (defaults to 'query')
15085      */
15086     triggerAction: 'query',
15087     /**
15088      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15089      * (defaults to 4, does not apply if editable = false)
15090      */
15091     minChars : 4,
15092     /**
15093      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15094      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15095      */
15096     typeAhead: false,
15097     /**
15098      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15099      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15100      */
15101     queryDelay: 500,
15102     /**
15103      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15104      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15105      */
15106     pageSize: 0,
15107     /**
15108      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15109      * when editable = true (defaults to false)
15110      */
15111     selectOnFocus:false,
15112     /**
15113      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15114      */
15115     queryParam: 'query',
15116     /**
15117      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15118      * when mode = 'remote' (defaults to 'Loading...')
15119      */
15120     loadingText: 'Loading...',
15121     /**
15122      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15123      */
15124     resizable: false,
15125     /**
15126      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15127      */
15128     handleHeight : 8,
15129     /**
15130      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15131      * traditional select (defaults to true)
15132      */
15133     editable: true,
15134     /**
15135      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15136      */
15137     allQuery: '',
15138     /**
15139      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15140      */
15141     mode: 'remote',
15142     /**
15143      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15144      * listWidth has a higher value)
15145      */
15146     minListWidth : 70,
15147     /**
15148      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15149      * allow the user to set arbitrary text into the field (defaults to false)
15150      */
15151     forceSelection:false,
15152     /**
15153      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15154      * if typeAhead = true (defaults to 250)
15155      */
15156     typeAheadDelay : 250,
15157     /**
15158      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15159      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15160      */
15161     valueNotFoundText : undefined,
15162     /**
15163      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15164      */
15165     blockFocus : false,
15166     
15167     /**
15168      * @cfg {Boolean} disableClear Disable showing of clear button.
15169      */
15170     disableClear : false,
15171     /**
15172      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15173      */
15174     alwaysQuery : false,
15175     
15176     /**
15177      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15178      */
15179     multiple : false,
15180     
15181     /**
15182      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15183      */
15184     invalidClass : "has-warning",
15185     
15186     /**
15187      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     validClass : "has-success",
15190     
15191     /**
15192      * @cfg {Boolean} specialFilter (true|false) special filter default false
15193      */
15194     specialFilter : false,
15195     
15196     /**
15197      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15198      */
15199     mobileTouchView : true,
15200     
15201     /**
15202      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15203      */
15204     useNativeIOS : false,
15205     
15206     /**
15207      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15208      */
15209     mobile_restrict_height : false,
15210     
15211     ios_options : false,
15212     
15213     //private
15214     addicon : false,
15215     editicon: false,
15216     
15217     page: 0,
15218     hasQuery: false,
15219     append: false,
15220     loadNext: false,
15221     autoFocus : true,
15222     tickable : false,
15223     btnPosition : 'right',
15224     triggerList : true,
15225     showToggleBtn : true,
15226     animate : true,
15227     emptyResultText: 'Empty',
15228     triggerText : 'Select',
15229     emptyTitle : '',
15230     width : false,
15231     
15232     // element that contains real text value.. (when hidden is used..)
15233     
15234     getAutoCreate : function()
15235     {   
15236         var cfg = false;
15237         //render
15238         /*
15239          * Render classic select for iso
15240          */
15241         
15242         if(Roo.isIOS && this.useNativeIOS){
15243             cfg = this.getAutoCreateNativeIOS();
15244             return cfg;
15245         }
15246         
15247         /*
15248          * Touch Devices
15249          */
15250         
15251         if(Roo.isTouch && this.mobileTouchView){
15252             cfg = this.getAutoCreateTouchView();
15253             return cfg;;
15254         }
15255         
15256         /*
15257          *  Normal ComboBox
15258          */
15259         if(!this.tickable){
15260             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15261             return cfg;
15262         }
15263         
15264         /*
15265          *  ComboBox with tickable selections
15266          */
15267              
15268         var align = this.labelAlign || this.parentLabelAlign();
15269         
15270         cfg = {
15271             cls : 'form-group roo-combobox-tickable' //input-group
15272         };
15273         
15274         var btn_text_select = '';
15275         var btn_text_done = '';
15276         var btn_text_cancel = '';
15277         
15278         if (this.btn_text_show) {
15279             btn_text_select = 'Select';
15280             btn_text_done = 'Done';
15281             btn_text_cancel = 'Cancel'; 
15282         }
15283         
15284         var buttons = {
15285             tag : 'div',
15286             cls : 'tickable-buttons',
15287             cn : [
15288                 {
15289                     tag : 'button',
15290                     type : 'button',
15291                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15292                     //html : this.triggerText
15293                     html: btn_text_select
15294                 },
15295                 {
15296                     tag : 'button',
15297                     type : 'button',
15298                     name : 'ok',
15299                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15300                     //html : 'Done'
15301                     html: btn_text_done
15302                 },
15303                 {
15304                     tag : 'button',
15305                     type : 'button',
15306                     name : 'cancel',
15307                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15308                     //html : 'Cancel'
15309                     html: btn_text_cancel
15310                 }
15311             ]
15312         };
15313         
15314         if(this.editable){
15315             buttons.cn.unshift({
15316                 tag: 'input',
15317                 cls: 'roo-select2-search-field-input'
15318             });
15319         }
15320         
15321         var _this = this;
15322         
15323         Roo.each(buttons.cn, function(c){
15324             if (_this.size) {
15325                 c.cls += ' btn-' + _this.size;
15326             }
15327
15328             if (_this.disabled) {
15329                 c.disabled = true;
15330             }
15331         });
15332         
15333         var box = {
15334             tag: 'div',
15335             style : 'display: contents',
15336             cn: [
15337                 {
15338                     tag: 'input',
15339                     type : 'hidden',
15340                     cls: 'form-hidden-field'
15341                 },
15342                 {
15343                     tag: 'ul',
15344                     cls: 'roo-select2-choices',
15345                     cn:[
15346                         {
15347                             tag: 'li',
15348                             cls: 'roo-select2-search-field',
15349                             cn: [
15350                                 buttons
15351                             ]
15352                         }
15353                     ]
15354                 }
15355             ]
15356         };
15357         
15358         var combobox = {
15359             cls: 'roo-select2-container input-group roo-select2-container-multi',
15360             cn: [
15361                 
15362                 box
15363 //                {
15364 //                    tag: 'ul',
15365 //                    cls: 'typeahead typeahead-long dropdown-menu',
15366 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15367 //                }
15368             ]
15369         };
15370         
15371         if(this.hasFeedback && !this.allowBlank){
15372             
15373             var feedback = {
15374                 tag: 'span',
15375                 cls: 'glyphicon form-control-feedback'
15376             };
15377
15378             combobox.cn.push(feedback);
15379         }
15380         
15381         
15382         
15383         var indicator = {
15384             tag : 'i',
15385             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15386             tooltip : 'This field is required'
15387         };
15388         if (Roo.bootstrap.version == 4) {
15389             indicator = {
15390                 tag : 'i',
15391                 style : 'display:none'
15392             };
15393         }
15394         if (align ==='left' && this.fieldLabel.length) {
15395             
15396             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15397             
15398             cfg.cn = [
15399                 indicator,
15400                 {
15401                     tag: 'label',
15402                     'for' :  id,
15403                     cls : 'control-label col-form-label',
15404                     html : this.fieldLabel
15405
15406                 },
15407                 {
15408                     cls : "", 
15409                     cn: [
15410                         combobox
15411                     ]
15412                 }
15413
15414             ];
15415             
15416             var labelCfg = cfg.cn[1];
15417             var contentCfg = cfg.cn[2];
15418             
15419
15420             if(this.indicatorpos == 'right'){
15421                 
15422                 cfg.cn = [
15423                     {
15424                         tag: 'label',
15425                         'for' :  id,
15426                         cls : 'control-label col-form-label',
15427                         cn : [
15428                             {
15429                                 tag : 'span',
15430                                 html : this.fieldLabel
15431                             },
15432                             indicator
15433                         ]
15434                     },
15435                     {
15436                         cls : "",
15437                         cn: [
15438                             combobox
15439                         ]
15440                     }
15441
15442                 ];
15443                 
15444                 
15445                 
15446                 labelCfg = cfg.cn[0];
15447                 contentCfg = cfg.cn[1];
15448             
15449             }
15450             
15451             if(this.labelWidth > 12){
15452                 labelCfg.style = "width: " + this.labelWidth + 'px';
15453             }
15454             if(this.width * 1 > 0){
15455                 contentCfg.style = "width: " + this.width + 'px';
15456             }
15457             if(this.labelWidth < 13 && this.labelmd == 0){
15458                 this.labelmd = this.labelWidth;
15459             }
15460             
15461             if(this.labellg > 0){
15462                 labelCfg.cls += ' col-lg-' + this.labellg;
15463                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15464             }
15465             
15466             if(this.labelmd > 0){
15467                 labelCfg.cls += ' col-md-' + this.labelmd;
15468                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15469             }
15470             
15471             if(this.labelsm > 0){
15472                 labelCfg.cls += ' col-sm-' + this.labelsm;
15473                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15474             }
15475             
15476             if(this.labelxs > 0){
15477                 labelCfg.cls += ' col-xs-' + this.labelxs;
15478                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15479             }
15480                 
15481                 
15482         } else if ( this.fieldLabel.length) {
15483 //                Roo.log(" label");
15484                  cfg.cn = [
15485                    indicator,
15486                     {
15487                         tag: 'label',
15488                         //cls : 'input-group-addon',
15489                         html : this.fieldLabel
15490                     },
15491                     combobox
15492                 ];
15493                 
15494                 if(this.indicatorpos == 'right'){
15495                     cfg.cn = [
15496                         {
15497                             tag: 'label',
15498                             //cls : 'input-group-addon',
15499                             html : this.fieldLabel
15500                         },
15501                         indicator,
15502                         combobox
15503                     ];
15504                     
15505                 }
15506
15507         } else {
15508             
15509 //                Roo.log(" no label && no align");
15510                 cfg = combobox
15511                      
15512                 
15513         }
15514          
15515         var settings=this;
15516         ['xs','sm','md','lg'].map(function(size){
15517             if (settings[size]) {
15518                 cfg.cls += ' col-' + size + '-' + settings[size];
15519             }
15520         });
15521         
15522         return cfg;
15523         
15524     },
15525     
15526     _initEventsCalled : false,
15527     
15528     // private
15529     initEvents: function()
15530     {   
15531         if (this._initEventsCalled) { // as we call render... prevent looping...
15532             return;
15533         }
15534         this._initEventsCalled = true;
15535         
15536         if (!this.store) {
15537             throw "can not find store for combo";
15538         }
15539         
15540         this.indicator = this.indicatorEl();
15541         
15542         this.store = Roo.factory(this.store, Roo.data);
15543         this.store.parent = this;
15544         
15545         // if we are building from html. then this element is so complex, that we can not really
15546         // use the rendered HTML.
15547         // so we have to trash and replace the previous code.
15548         if (Roo.XComponent.build_from_html) {
15549             // remove this element....
15550             var e = this.el.dom, k=0;
15551             while (e ) { e = e.previousSibling;  ++k;}
15552
15553             this.el.remove();
15554             
15555             this.el=false;
15556             this.rendered = false;
15557             
15558             this.render(this.parent().getChildContainer(true), k);
15559         }
15560         
15561         if(Roo.isIOS && this.useNativeIOS){
15562             this.initIOSView();
15563             return;
15564         }
15565         
15566         /*
15567          * Touch Devices
15568          */
15569         
15570         if(Roo.isTouch && this.mobileTouchView){
15571             this.initTouchView();
15572             return;
15573         }
15574         
15575         if(this.tickable){
15576             this.initTickableEvents();
15577             return;
15578         }
15579         
15580         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15581         
15582         if(this.hiddenName){
15583             
15584             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15585             
15586             this.hiddenField.dom.value =
15587                 this.hiddenValue !== undefined ? this.hiddenValue :
15588                 this.value !== undefined ? this.value : '';
15589
15590             // prevent input submission
15591             this.el.dom.removeAttribute('name');
15592             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15593              
15594              
15595         }
15596         //if(Roo.isGecko){
15597         //    this.el.dom.setAttribute('autocomplete', 'off');
15598         //}
15599         
15600         var cls = 'x-combo-list';
15601         
15602         //this.list = new Roo.Layer({
15603         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15604         //});
15605         
15606         var _this = this;
15607         
15608         (function(){
15609             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15610             _this.list.setWidth(lw);
15611         }).defer(100);
15612         
15613         this.list.on('mouseover', this.onViewOver, this);
15614         this.list.on('mousemove', this.onViewMove, this);
15615         this.list.on('scroll', this.onViewScroll, this);
15616         
15617         /*
15618         this.list.swallowEvent('mousewheel');
15619         this.assetHeight = 0;
15620
15621         if(this.title){
15622             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15623             this.assetHeight += this.header.getHeight();
15624         }
15625
15626         this.innerList = this.list.createChild({cls:cls+'-inner'});
15627         this.innerList.on('mouseover', this.onViewOver, this);
15628         this.innerList.on('mousemove', this.onViewMove, this);
15629         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15630         
15631         if(this.allowBlank && !this.pageSize && !this.disableClear){
15632             this.footer = this.list.createChild({cls:cls+'-ft'});
15633             this.pageTb = new Roo.Toolbar(this.footer);
15634            
15635         }
15636         if(this.pageSize){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15639                     {pageSize: this.pageSize});
15640             
15641         }
15642         
15643         if (this.pageTb && this.allowBlank && !this.disableClear) {
15644             var _this = this;
15645             this.pageTb.add(new Roo.Toolbar.Fill(), {
15646                 cls: 'x-btn-icon x-btn-clear',
15647                 text: '&#160;',
15648                 handler: function()
15649                 {
15650                     _this.collapse();
15651                     _this.clearValue();
15652                     _this.onSelect(false, -1);
15653                 }
15654             });
15655         }
15656         if (this.footer) {
15657             this.assetHeight += this.footer.getHeight();
15658         }
15659         */
15660             
15661         if(!this.tpl){
15662             this.tpl = Roo.bootstrap.version == 4 ?
15663                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15664                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15665         }
15666
15667         this.view = new Roo.View(this.list, this.tpl, {
15668             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15669         });
15670         //this.view.wrapEl.setDisplayed(false);
15671         this.view.on('click', this.onViewClick, this);
15672         
15673         
15674         this.store.on('beforeload', this.onBeforeLoad, this);
15675         this.store.on('load', this.onLoad, this);
15676         this.store.on('loadexception', this.onLoadException, this);
15677         /*
15678         if(this.resizable){
15679             this.resizer = new Roo.Resizable(this.list,  {
15680                pinned:true, handles:'se'
15681             });
15682             this.resizer.on('resize', function(r, w, h){
15683                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15684                 this.listWidth = w;
15685                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15686                 this.restrictHeight();
15687             }, this);
15688             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15689         }
15690         */
15691         if(!this.editable){
15692             this.editable = true;
15693             this.setEditable(false);
15694         }
15695         
15696         /*
15697         
15698         if (typeof(this.events.add.listeners) != 'undefined') {
15699             
15700             this.addicon = this.wrap.createChild(
15701                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15702        
15703             this.addicon.on('click', function(e) {
15704                 this.fireEvent('add', this);
15705             }, this);
15706         }
15707         if (typeof(this.events.edit.listeners) != 'undefined') {
15708             
15709             this.editicon = this.wrap.createChild(
15710                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15711             if (this.addicon) {
15712                 this.editicon.setStyle('margin-left', '40px');
15713             }
15714             this.editicon.on('click', function(e) {
15715                 
15716                 // we fire even  if inothing is selected..
15717                 this.fireEvent('edit', this, this.lastData );
15718                 
15719             }, this);
15720         }
15721         */
15722         
15723         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15724             "up" : function(e){
15725                 this.inKeyMode = true;
15726                 this.selectPrev();
15727             },
15728
15729             "down" : function(e){
15730                 if(!this.isExpanded()){
15731                     this.onTriggerClick();
15732                 }else{
15733                     this.inKeyMode = true;
15734                     this.selectNext();
15735                 }
15736             },
15737
15738             "enter" : function(e){
15739 //                this.onViewClick();
15740                 //return true;
15741                 this.collapse();
15742                 
15743                 if(this.fireEvent("specialkey", this, e)){
15744                     this.onViewClick(false);
15745                 }
15746                 
15747                 return true;
15748             },
15749
15750             "esc" : function(e){
15751                 this.collapse();
15752             },
15753
15754             "tab" : function(e){
15755                 this.collapse();
15756                 
15757                 if(this.fireEvent("specialkey", this, e)){
15758                     this.onViewClick(false);
15759                 }
15760                 
15761                 return true;
15762             },
15763
15764             scope : this,
15765
15766             doRelay : function(foo, bar, hname){
15767                 if(hname == 'down' || this.scope.isExpanded()){
15768                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15769                 }
15770                 return true;
15771             },
15772
15773             forceKeyDown: true
15774         });
15775         
15776         
15777         this.queryDelay = Math.max(this.queryDelay || 10,
15778                 this.mode == 'local' ? 10 : 250);
15779         
15780         
15781         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15782         
15783         if(this.typeAhead){
15784             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15785         }
15786         if(this.editable !== false){
15787             this.inputEl().on("keyup", this.onKeyUp, this);
15788         }
15789         if(this.forceSelection){
15790             this.inputEl().on('blur', this.doForce, this);
15791         }
15792         
15793         if(this.multiple){
15794             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15795             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15796         }
15797     },
15798     
15799     initTickableEvents: function()
15800     {   
15801         this.createList();
15802         
15803         if(this.hiddenName){
15804             
15805             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15806             
15807             this.hiddenField.dom.value =
15808                 this.hiddenValue !== undefined ? this.hiddenValue :
15809                 this.value !== undefined ? this.value : '';
15810
15811             // prevent input submission
15812             this.el.dom.removeAttribute('name');
15813             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15814              
15815              
15816         }
15817         
15818 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15819         
15820         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15821         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15822         if(this.triggerList){
15823             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15824         }
15825          
15826         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15827         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15828         
15829         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15830         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15831         
15832         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15833         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15834         
15835         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15836         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15837         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15838         
15839         this.okBtn.hide();
15840         this.cancelBtn.hide();
15841         
15842         var _this = this;
15843         
15844         (function(){
15845             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15846             _this.list.setWidth(lw);
15847         }).defer(100);
15848         
15849         this.list.on('mouseover', this.onViewOver, this);
15850         this.list.on('mousemove', this.onViewMove, this);
15851         
15852         this.list.on('scroll', this.onViewScroll, this);
15853         
15854         if(!this.tpl){
15855             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15856                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15857         }
15858
15859         this.view = new Roo.View(this.list, this.tpl, {
15860             singleSelect:true,
15861             tickable:true,
15862             parent:this,
15863             store: this.store,
15864             selectedClass: this.selectedClass
15865         });
15866         
15867         //this.view.wrapEl.setDisplayed(false);
15868         this.view.on('click', this.onViewClick, this);
15869         
15870         
15871         
15872         this.store.on('beforeload', this.onBeforeLoad, this);
15873         this.store.on('load', this.onLoad, this);
15874         this.store.on('loadexception', this.onLoadException, this);
15875         
15876         if(this.editable){
15877             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15878                 "up" : function(e){
15879                     this.inKeyMode = true;
15880                     this.selectPrev();
15881                 },
15882
15883                 "down" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectNext();
15886                 },
15887
15888                 "enter" : function(e){
15889                     if(this.fireEvent("specialkey", this, e)){
15890                         this.onViewClick(false);
15891                     }
15892                     
15893                     return true;
15894                 },
15895
15896                 "esc" : function(e){
15897                     this.onTickableFooterButtonClick(e, false, false);
15898                 },
15899
15900                 "tab" : function(e){
15901                     this.fireEvent("specialkey", this, e);
15902                     
15903                     this.onTickableFooterButtonClick(e, false, false);
15904                     
15905                     return true;
15906                 },
15907
15908                 scope : this,
15909
15910                 doRelay : function(e, fn, key){
15911                     if(this.scope.isExpanded()){
15912                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15913                     }
15914                     return true;
15915                 },
15916
15917                 forceKeyDown: true
15918             });
15919         }
15920         
15921         this.queryDelay = Math.max(this.queryDelay || 10,
15922                 this.mode == 'local' ? 10 : 250);
15923         
15924         
15925         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15926         
15927         if(this.typeAhead){
15928             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15929         }
15930         
15931         if(this.editable !== false){
15932             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15933         }
15934         
15935         this.indicator = this.indicatorEl();
15936         
15937         if(this.indicator){
15938             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15939             this.indicator.hide();
15940         }
15941         
15942     },
15943
15944     onDestroy : function(){
15945         if(this.view){
15946             this.view.setStore(null);
15947             this.view.el.removeAllListeners();
15948             this.view.el.remove();
15949             this.view.purgeListeners();
15950         }
15951         if(this.list){
15952             this.list.dom.innerHTML  = '';
15953         }
15954         
15955         if(this.store){
15956             this.store.un('beforeload', this.onBeforeLoad, this);
15957             this.store.un('load', this.onLoad, this);
15958             this.store.un('loadexception', this.onLoadException, this);
15959         }
15960         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15961     },
15962
15963     // private
15964     fireKey : function(e){
15965         if(e.isNavKeyPress() && !this.list.isVisible()){
15966             this.fireEvent("specialkey", this, e);
15967         }
15968     },
15969
15970     // private
15971     onResize: function(w, h)
15972     {
15973         
15974         
15975 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15976 //        
15977 //        if(typeof w != 'number'){
15978 //            // we do not handle it!?!?
15979 //            return;
15980 //        }
15981 //        var tw = this.trigger.getWidth();
15982 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15983 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15984 //        var x = w - tw;
15985 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15986 //            
15987 //        //this.trigger.setStyle('left', x+'px');
15988 //        
15989 //        if(this.list && this.listWidth === undefined){
15990 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15991 //            this.list.setWidth(lw);
15992 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15993 //        }
15994         
15995     
15996         
15997     },
15998
15999     /**
16000      * Allow or prevent the user from directly editing the field text.  If false is passed,
16001      * the user will only be able to select from the items defined in the dropdown list.  This method
16002      * is the runtime equivalent of setting the 'editable' config option at config time.
16003      * @param {Boolean} value True to allow the user to directly edit the field text
16004      */
16005     setEditable : function(value){
16006         if(value == this.editable){
16007             return;
16008         }
16009         this.editable = value;
16010         if(!value){
16011             this.inputEl().dom.setAttribute('readOnly', true);
16012             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16013             this.inputEl().addClass('x-combo-noedit');
16014         }else{
16015             this.inputEl().dom.setAttribute('readOnly', false);
16016             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16017             this.inputEl().removeClass('x-combo-noedit');
16018         }
16019     },
16020
16021     // private
16022     
16023     onBeforeLoad : function(combo,opts){
16024         if(!this.hasFocus){
16025             return;
16026         }
16027          if (!opts.add) {
16028             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16029          }
16030         this.restrictHeight();
16031         this.selectedIndex = -1;
16032     },
16033
16034     // private
16035     onLoad : function(){
16036         
16037         this.hasQuery = false;
16038         
16039         if(!this.hasFocus){
16040             return;
16041         }
16042         
16043         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16044             this.loading.hide();
16045         }
16046         
16047         if(this.store.getCount() > 0){
16048             
16049             this.expand();
16050             this.restrictHeight();
16051             if(this.lastQuery == this.allQuery){
16052                 if(this.editable && !this.tickable){
16053                     this.inputEl().dom.select();
16054                 }
16055                 
16056                 if(
16057                     !this.selectByValue(this.value, true) &&
16058                     this.autoFocus && 
16059                     (
16060                         !this.store.lastOptions ||
16061                         typeof(this.store.lastOptions.add) == 'undefined' || 
16062                         this.store.lastOptions.add != true
16063                     )
16064                 ){
16065                     this.select(0, true);
16066                 }
16067             }else{
16068                 if(this.autoFocus){
16069                     this.selectNext();
16070                 }
16071                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16072                     this.taTask.delay(this.typeAheadDelay);
16073                 }
16074             }
16075         }else{
16076             this.onEmptyResults();
16077         }
16078         
16079         //this.el.focus();
16080     },
16081     // private
16082     onLoadException : function()
16083     {
16084         this.hasQuery = false;
16085         
16086         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16087             this.loading.hide();
16088         }
16089         
16090         if(this.tickable && this.editable){
16091             return;
16092         }
16093         
16094         this.collapse();
16095         // only causes errors at present
16096         //Roo.log(this.store.reader.jsonData);
16097         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16098             // fixme
16099             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16100         //}
16101         
16102         
16103     },
16104     // private
16105     onTypeAhead : function(){
16106         if(this.store.getCount() > 0){
16107             var r = this.store.getAt(0);
16108             var newValue = r.data[this.displayField];
16109             var len = newValue.length;
16110             var selStart = this.getRawValue().length;
16111             
16112             if(selStart != len){
16113                 this.setRawValue(newValue);
16114                 this.selectText(selStart, newValue.length);
16115             }
16116         }
16117     },
16118
16119     // private
16120     onSelect : function(record, index){
16121         
16122         if(this.fireEvent('beforeselect', this, record, index) !== false){
16123         
16124             this.setFromData(index > -1 ? record.data : false);
16125             
16126             this.collapse();
16127             this.fireEvent('select', this, record, index);
16128         }
16129     },
16130
16131     /**
16132      * Returns the currently selected field value or empty string if no value is set.
16133      * @return {String} value The selected value
16134      */
16135     getValue : function()
16136     {
16137         if(Roo.isIOS && this.useNativeIOS){
16138             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16139         }
16140         
16141         if(this.multiple){
16142             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16143         }
16144         
16145         if(this.valueField){
16146             return typeof this.value != 'undefined' ? this.value : '';
16147         }else{
16148             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16149         }
16150     },
16151     
16152     getRawValue : function()
16153     {
16154         if(Roo.isIOS && this.useNativeIOS){
16155             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16156         }
16157         
16158         var v = this.inputEl().getValue();
16159         
16160         return v;
16161     },
16162
16163     /**
16164      * Clears any text/value currently set in the field
16165      */
16166     clearValue : function(){
16167         
16168         if(this.hiddenField){
16169             this.hiddenField.dom.value = '';
16170         }
16171         this.value = '';
16172         this.setRawValue('');
16173         this.lastSelectionText = '';
16174         this.lastData = false;
16175         
16176         var close = this.closeTriggerEl();
16177         
16178         if(close){
16179             close.hide();
16180         }
16181         
16182         this.validate();
16183         
16184     },
16185
16186     /**
16187      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16188      * will be displayed in the field.  If the value does not match the data value of an existing item,
16189      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16190      * Otherwise the field will be blank (although the value will still be set).
16191      * @param {String} value The value to match
16192      */
16193     setValue : function(v)
16194     {
16195         if(Roo.isIOS && this.useNativeIOS){
16196             this.setIOSValue(v);
16197             return;
16198         }
16199         
16200         if(this.multiple){
16201             this.syncValue();
16202             return;
16203         }
16204         
16205         var text = v;
16206         if(this.valueField){
16207             var r = this.findRecord(this.valueField, v);
16208             if(r){
16209                 text = r.data[this.displayField];
16210             }else if(this.valueNotFoundText !== undefined){
16211                 text = this.valueNotFoundText;
16212             }
16213         }
16214         this.lastSelectionText = text;
16215         if(this.hiddenField){
16216             this.hiddenField.dom.value = v;
16217         }
16218         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16219         this.value = v;
16220         
16221         var close = this.closeTriggerEl();
16222         
16223         if(close){
16224             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16225         }
16226         
16227         this.validate();
16228     },
16229     /**
16230      * @property {Object} the last set data for the element
16231      */
16232     
16233     lastData : false,
16234     /**
16235      * Sets the value of the field based on a object which is related to the record format for the store.
16236      * @param {Object} value the value to set as. or false on reset?
16237      */
16238     setFromData : function(o){
16239         
16240         if(this.multiple){
16241             this.addItem(o);
16242             return;
16243         }
16244             
16245         var dv = ''; // display value
16246         var vv = ''; // value value..
16247         this.lastData = o;
16248         if (this.displayField) {
16249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16250         } else {
16251             // this is an error condition!!!
16252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16253         }
16254         
16255         if(this.valueField){
16256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16257         }
16258         
16259         var close = this.closeTriggerEl();
16260         
16261         if(close){
16262             if(dv.length || vv * 1 > 0){
16263                 close.show() ;
16264                 this.blockFocus=true;
16265             } else {
16266                 close.hide();
16267             }             
16268         }
16269         
16270         if(this.hiddenField){
16271             this.hiddenField.dom.value = vv;
16272             
16273             this.lastSelectionText = dv;
16274             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16275             this.value = vv;
16276             return;
16277         }
16278         // no hidden field.. - we store the value in 'value', but still display
16279         // display field!!!!
16280         this.lastSelectionText = dv;
16281         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16282         this.value = vv;
16283         
16284         
16285         
16286     },
16287     // private
16288     reset : function(){
16289         // overridden so that last data is reset..
16290         
16291         if(this.multiple){
16292             this.clearItem();
16293             return;
16294         }
16295         
16296         this.setValue(this.originalValue);
16297         //this.clearInvalid();
16298         this.lastData = false;
16299         if (this.view) {
16300             this.view.clearSelections();
16301         }
16302         
16303         this.validate();
16304     },
16305     // private
16306     findRecord : function(prop, value){
16307         var record;
16308         if(this.store.getCount() > 0){
16309             this.store.each(function(r){
16310                 if(r.data[prop] == value){
16311                     record = r;
16312                     return false;
16313                 }
16314                 return true;
16315             });
16316         }
16317         return record;
16318     },
16319     
16320     getName: function()
16321     {
16322         // returns hidden if it's set..
16323         if (!this.rendered) {return ''};
16324         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16325         
16326     },
16327     // private
16328     onViewMove : function(e, t){
16329         this.inKeyMode = false;
16330     },
16331
16332     // private
16333     onViewOver : function(e, t){
16334         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16335             return;
16336         }
16337         var item = this.view.findItemFromChild(t);
16338         
16339         if(item){
16340             var index = this.view.indexOf(item);
16341             this.select(index, false);
16342         }
16343     },
16344
16345     // private
16346     onViewClick : function(view, doFocus, el, e)
16347     {
16348         var index = this.view.getSelectedIndexes()[0];
16349         
16350         var r = this.store.getAt(index);
16351         
16352         if(this.tickable){
16353             
16354             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16355                 return;
16356             }
16357             
16358             var rm = false;
16359             var _this = this;
16360             
16361             Roo.each(this.tickItems, function(v,k){
16362                 
16363                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16364                     Roo.log(v);
16365                     _this.tickItems.splice(k, 1);
16366                     
16367                     if(typeof(e) == 'undefined' && view == false){
16368                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16369                     }
16370                     
16371                     rm = true;
16372                     return;
16373                 }
16374             });
16375             
16376             if(rm){
16377                 return;
16378             }
16379             
16380             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16381                 this.tickItems.push(r.data);
16382             }
16383             
16384             if(typeof(e) == 'undefined' && view == false){
16385                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16386             }
16387                     
16388             return;
16389         }
16390         
16391         if(r){
16392             this.onSelect(r, index);
16393         }
16394         if(doFocus !== false && !this.blockFocus){
16395             this.inputEl().focus();
16396         }
16397     },
16398
16399     // private
16400     restrictHeight : function(){
16401         //this.innerList.dom.style.height = '';
16402         //var inner = this.innerList.dom;
16403         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16404         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16405         //this.list.beginUpdate();
16406         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16407         this.list.alignTo(this.inputEl(), this.listAlign);
16408         this.list.alignTo(this.inputEl(), this.listAlign);
16409         //this.list.endUpdate();
16410     },
16411
16412     // private
16413     onEmptyResults : function(){
16414         
16415         if(this.tickable && this.editable){
16416             this.hasFocus = false;
16417             this.restrictHeight();
16418             return;
16419         }
16420         
16421         this.collapse();
16422     },
16423
16424     /**
16425      * Returns true if the dropdown list is expanded, else false.
16426      */
16427     isExpanded : function(){
16428         return this.list.isVisible();
16429     },
16430
16431     /**
16432      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16434      * @param {String} value The data value of the item to select
16435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16436      * selected item if it is not currently in view (defaults to true)
16437      * @return {Boolean} True if the value matched an item in the list, else false
16438      */
16439     selectByValue : function(v, scrollIntoView){
16440         if(v !== undefined && v !== null){
16441             var r = this.findRecord(this.valueField || this.displayField, v);
16442             if(r){
16443                 this.select(this.store.indexOf(r), scrollIntoView);
16444                 return true;
16445             }
16446         }
16447         return false;
16448     },
16449
16450     /**
16451      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16452      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16453      * @param {Number} index The zero-based index of the list item to select
16454      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16455      * selected item if it is not currently in view (defaults to true)
16456      */
16457     select : function(index, scrollIntoView){
16458         this.selectedIndex = index;
16459         this.view.select(index);
16460         if(scrollIntoView !== false){
16461             var el = this.view.getNode(index);
16462             /*
16463              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16464              */
16465             if(el){
16466                 this.list.scrollChildIntoView(el, false);
16467             }
16468         }
16469     },
16470
16471     // private
16472     selectNext : function(){
16473         var ct = this.store.getCount();
16474         if(ct > 0){
16475             if(this.selectedIndex == -1){
16476                 this.select(0);
16477             }else if(this.selectedIndex < ct-1){
16478                 this.select(this.selectedIndex+1);
16479             }
16480         }
16481     },
16482
16483     // private
16484     selectPrev : function(){
16485         var ct = this.store.getCount();
16486         if(ct > 0){
16487             if(this.selectedIndex == -1){
16488                 this.select(0);
16489             }else if(this.selectedIndex != 0){
16490                 this.select(this.selectedIndex-1);
16491             }
16492         }
16493     },
16494
16495     // private
16496     onKeyUp : function(e){
16497         if(this.editable !== false && !e.isSpecialKey()){
16498             this.lastKey = e.getKey();
16499             this.dqTask.delay(this.queryDelay);
16500         }
16501     },
16502
16503     // private
16504     validateBlur : function(){
16505         return !this.list || !this.list.isVisible();   
16506     },
16507
16508     // private
16509     initQuery : function(){
16510         
16511         var v = this.getRawValue();
16512         
16513         if(this.tickable && this.editable){
16514             v = this.tickableInputEl().getValue();
16515         }
16516         
16517         this.doQuery(v);
16518     },
16519
16520     // private
16521     doForce : function(){
16522         if(this.inputEl().dom.value.length > 0){
16523             this.inputEl().dom.value =
16524                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16525              
16526         }
16527     },
16528
16529     /**
16530      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16531      * query allowing the query action to be canceled if needed.
16532      * @param {String} query The SQL query to execute
16533      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16534      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16535      * saved in the current store (defaults to false)
16536      */
16537     doQuery : function(q, forceAll){
16538         
16539         if(q === undefined || q === null){
16540             q = '';
16541         }
16542         var qe = {
16543             query: q,
16544             forceAll: forceAll,
16545             combo: this,
16546             cancel:false
16547         };
16548         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16549             return false;
16550         }
16551         q = qe.query;
16552         
16553         forceAll = qe.forceAll;
16554         if(forceAll === true || (q.length >= this.minChars)){
16555             
16556             this.hasQuery = true;
16557             
16558             if(this.lastQuery != q || this.alwaysQuery){
16559                 this.lastQuery = q;
16560                 if(this.mode == 'local'){
16561                     this.selectedIndex = -1;
16562                     if(forceAll){
16563                         this.store.clearFilter();
16564                     }else{
16565                         
16566                         if(this.specialFilter){
16567                             this.fireEvent('specialfilter', this);
16568                             this.onLoad();
16569                             return;
16570                         }
16571                         
16572                         this.store.filter(this.displayField, q);
16573                     }
16574                     
16575                     this.store.fireEvent("datachanged", this.store);
16576                     
16577                     this.onLoad();
16578                     
16579                     
16580                 }else{
16581                     
16582                     this.store.baseParams[this.queryParam] = q;
16583                     
16584                     var options = {params : this.getParams(q)};
16585                     
16586                     if(this.loadNext){
16587                         options.add = true;
16588                         options.params.start = this.page * this.pageSize;
16589                     }
16590                     
16591                     this.store.load(options);
16592                     
16593                     /*
16594                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16595                      *  we should expand the list on onLoad
16596                      *  so command out it
16597                      */
16598 //                    this.expand();
16599                 }
16600             }else{
16601                 this.selectedIndex = -1;
16602                 this.onLoad();   
16603             }
16604         }
16605         
16606         this.loadNext = false;
16607     },
16608     
16609     // private
16610     getParams : function(q){
16611         var p = {};
16612         //p[this.queryParam] = q;
16613         
16614         if(this.pageSize){
16615             p.start = 0;
16616             p.limit = this.pageSize;
16617         }
16618         return p;
16619     },
16620
16621     /**
16622      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16623      */
16624     collapse : function(){
16625         if(!this.isExpanded()){
16626             return;
16627         }
16628         
16629         this.list.hide();
16630         
16631         this.hasFocus = false;
16632         
16633         if(this.tickable){
16634             this.okBtn.hide();
16635             this.cancelBtn.hide();
16636             this.trigger.show();
16637             
16638             if(this.editable){
16639                 this.tickableInputEl().dom.value = '';
16640                 this.tickableInputEl().blur();
16641             }
16642             
16643         }
16644         
16645         Roo.get(document).un('mousedown', this.collapseIf, this);
16646         Roo.get(document).un('mousewheel', this.collapseIf, this);
16647         if (!this.editable) {
16648             Roo.get(document).un('keydown', this.listKeyPress, this);
16649         }
16650         this.fireEvent('collapse', this);
16651         
16652         this.validate();
16653     },
16654
16655     // private
16656     collapseIf : function(e){
16657         var in_combo  = e.within(this.el);
16658         var in_list =  e.within(this.list);
16659         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16660         
16661         if (in_combo || in_list || is_list) {
16662             //e.stopPropagation();
16663             return;
16664         }
16665         
16666         if(this.tickable){
16667             this.onTickableFooterButtonClick(e, false, false);
16668         }
16669
16670         this.collapse();
16671         
16672     },
16673
16674     /**
16675      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16676      */
16677     expand : function(){
16678        
16679         if(this.isExpanded() || !this.hasFocus){
16680             return;
16681         }
16682         
16683         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16684         this.list.setWidth(lw);
16685         
16686         Roo.log('expand');
16687         
16688         this.list.show();
16689         
16690         this.restrictHeight();
16691         
16692         if(this.tickable){
16693             
16694             this.tickItems = Roo.apply([], this.item);
16695             
16696             this.okBtn.show();
16697             this.cancelBtn.show();
16698             this.trigger.hide();
16699             
16700             if(this.editable){
16701                 this.tickableInputEl().focus();
16702             }
16703             
16704         }
16705         
16706         Roo.get(document).on('mousedown', this.collapseIf, this);
16707         Roo.get(document).on('mousewheel', this.collapseIf, this);
16708         if (!this.editable) {
16709             Roo.get(document).on('keydown', this.listKeyPress, this);
16710         }
16711         
16712         this.fireEvent('expand', this);
16713     },
16714
16715     // private
16716     // Implements the default empty TriggerField.onTriggerClick function
16717     onTriggerClick : function(e)
16718     {
16719         Roo.log('trigger click');
16720         
16721         if(this.disabled || !this.triggerList){
16722             return;
16723         }
16724         
16725         this.page = 0;
16726         this.loadNext = false;
16727         
16728         if(this.isExpanded()){
16729             this.collapse();
16730             if (!this.blockFocus) {
16731                 this.inputEl().focus();
16732             }
16733             
16734         }else {
16735             this.hasFocus = true;
16736             if(this.triggerAction == 'all') {
16737                 this.doQuery(this.allQuery, true);
16738             } else {
16739                 this.doQuery(this.getRawValue());
16740             }
16741             if (!this.blockFocus) {
16742                 this.inputEl().focus();
16743             }
16744         }
16745     },
16746     
16747     onTickableTriggerClick : function(e)
16748     {
16749         if(this.disabled){
16750             return;
16751         }
16752         
16753         this.page = 0;
16754         this.loadNext = false;
16755         this.hasFocus = true;
16756         
16757         if(this.triggerAction == 'all') {
16758             this.doQuery(this.allQuery, true);
16759         } else {
16760             this.doQuery(this.getRawValue());
16761         }
16762     },
16763     
16764     onSearchFieldClick : function(e)
16765     {
16766         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16767             this.onTickableFooterButtonClick(e, false, false);
16768             return;
16769         }
16770         
16771         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16772             return;
16773         }
16774         
16775         this.page = 0;
16776         this.loadNext = false;
16777         this.hasFocus = true;
16778         
16779         if(this.triggerAction == 'all') {
16780             this.doQuery(this.allQuery, true);
16781         } else {
16782             this.doQuery(this.getRawValue());
16783         }
16784     },
16785     
16786     listKeyPress : function(e)
16787     {
16788         //Roo.log('listkeypress');
16789         // scroll to first matching element based on key pres..
16790         if (e.isSpecialKey()) {
16791             return false;
16792         }
16793         var k = String.fromCharCode(e.getKey()).toUpperCase();
16794         //Roo.log(k);
16795         var match  = false;
16796         var csel = this.view.getSelectedNodes();
16797         var cselitem = false;
16798         if (csel.length) {
16799             var ix = this.view.indexOf(csel[0]);
16800             cselitem  = this.store.getAt(ix);
16801             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16802                 cselitem = false;
16803             }
16804             
16805         }
16806         
16807         this.store.each(function(v) { 
16808             if (cselitem) {
16809                 // start at existing selection.
16810                 if (cselitem.id == v.id) {
16811                     cselitem = false;
16812                 }
16813                 return true;
16814             }
16815                 
16816             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16817                 match = this.store.indexOf(v);
16818                 return false;
16819             }
16820             return true;
16821         }, this);
16822         
16823         if (match === false) {
16824             return true; // no more action?
16825         }
16826         // scroll to?
16827         this.view.select(match);
16828         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16829         sn.scrollIntoView(sn.dom.parentNode, false);
16830     },
16831     
16832     onViewScroll : function(e, t){
16833         
16834         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
16835             return;
16836         }
16837         
16838         this.hasQuery = true;
16839         
16840         this.loading = this.list.select('.loading', true).first();
16841         
16842         if(this.loading === null){
16843             this.list.createChild({
16844                 tag: 'div',
16845                 cls: 'loading roo-select2-more-results roo-select2-active',
16846                 html: 'Loading more results...'
16847             });
16848             
16849             this.loading = this.list.select('.loading', true).first();
16850             
16851             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16852             
16853             this.loading.hide();
16854         }
16855         
16856         this.loading.show();
16857         
16858         var _combo = this;
16859         
16860         this.page++;
16861         this.loadNext = true;
16862         
16863         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16864         
16865         return;
16866     },
16867     
16868     addItem : function(o)
16869     {   
16870         var dv = ''; // display value
16871         
16872         if (this.displayField) {
16873             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16874         } else {
16875             // this is an error condition!!!
16876             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16877         }
16878         
16879         if(!dv.length){
16880             return;
16881         }
16882         
16883         var choice = this.choices.createChild({
16884             tag: 'li',
16885             cls: 'roo-select2-search-choice',
16886             cn: [
16887                 {
16888                     tag: 'div',
16889                     html: dv
16890                 },
16891                 {
16892                     tag: 'a',
16893                     href: '#',
16894                     cls: 'roo-select2-search-choice-close fa fa-times',
16895                     tabindex: '-1'
16896                 }
16897             ]
16898             
16899         }, this.searchField);
16900         
16901         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16902         
16903         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16904         
16905         this.item.push(o);
16906         
16907         this.lastData = o;
16908         
16909         this.syncValue();
16910         
16911         this.inputEl().dom.value = '';
16912         
16913         this.validate();
16914     },
16915     
16916     onRemoveItem : function(e, _self, o)
16917     {
16918         e.preventDefault();
16919         
16920         this.lastItem = Roo.apply([], this.item);
16921         
16922         var index = this.item.indexOf(o.data) * 1;
16923         
16924         if( index < 0){
16925             Roo.log('not this item?!');
16926             return;
16927         }
16928         
16929         this.item.splice(index, 1);
16930         o.item.remove();
16931         
16932         this.syncValue();
16933         
16934         this.fireEvent('remove', this, e);
16935         
16936         this.validate();
16937         
16938     },
16939     
16940     syncValue : function()
16941     {
16942         if(!this.item.length){
16943             this.clearValue();
16944             return;
16945         }
16946             
16947         var value = [];
16948         var _this = this;
16949         Roo.each(this.item, function(i){
16950             if(_this.valueField){
16951                 value.push(i[_this.valueField]);
16952                 return;
16953             }
16954
16955             value.push(i);
16956         });
16957
16958         this.value = value.join(',');
16959
16960         if(this.hiddenField){
16961             this.hiddenField.dom.value = this.value;
16962         }
16963         
16964         this.store.fireEvent("datachanged", this.store);
16965         
16966         this.validate();
16967     },
16968     
16969     clearItem : function()
16970     {
16971         if(!this.multiple){
16972             return;
16973         }
16974         
16975         this.item = [];
16976         
16977         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16978            c.remove();
16979         });
16980         
16981         this.syncValue();
16982         
16983         this.validate();
16984         
16985         if(this.tickable && !Roo.isTouch){
16986             this.view.refresh();
16987         }
16988     },
16989     
16990     inputEl: function ()
16991     {
16992         if(Roo.isIOS && this.useNativeIOS){
16993             return this.el.select('select.roo-ios-select', true).first();
16994         }
16995         
16996         if(Roo.isTouch && this.mobileTouchView){
16997             return this.el.select('input.form-control',true).first();
16998         }
16999         
17000         if(this.tickable){
17001             return this.searchField;
17002         }
17003         
17004         return this.el.select('input.form-control',true).first();
17005     },
17006     
17007     onTickableFooterButtonClick : function(e, btn, el)
17008     {
17009         e.preventDefault();
17010         
17011         this.lastItem = Roo.apply([], this.item);
17012         
17013         if(btn && btn.name == 'cancel'){
17014             this.tickItems = Roo.apply([], this.item);
17015             this.collapse();
17016             return;
17017         }
17018         
17019         this.clearItem();
17020         
17021         var _this = this;
17022         
17023         Roo.each(this.tickItems, function(o){
17024             _this.addItem(o);
17025         });
17026         
17027         this.collapse();
17028         
17029     },
17030     
17031     validate : function()
17032     {
17033         if(this.getVisibilityEl().hasClass('hidden')){
17034             return true;
17035         }
17036         
17037         var v = this.getRawValue();
17038         
17039         if(this.multiple){
17040             v = this.getValue();
17041         }
17042         
17043         if(this.disabled || this.allowBlank || v.length){
17044             this.markValid();
17045             return true;
17046         }
17047         
17048         this.markInvalid();
17049         return false;
17050     },
17051     
17052     tickableInputEl : function()
17053     {
17054         if(!this.tickable || !this.editable){
17055             return this.inputEl();
17056         }
17057         
17058         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17059     },
17060     
17061     
17062     getAutoCreateTouchView : function()
17063     {
17064         var id = Roo.id();
17065         
17066         var cfg = {
17067             cls: 'form-group' //input-group
17068         };
17069         
17070         var input =  {
17071             tag: 'input',
17072             id : id,
17073             type : this.inputType,
17074             cls : 'form-control x-combo-noedit',
17075             autocomplete: 'new-password',
17076             placeholder : this.placeholder || '',
17077             readonly : true
17078         };
17079         
17080         if (this.name) {
17081             input.name = this.name;
17082         }
17083         
17084         if (this.size) {
17085             input.cls += ' input-' + this.size;
17086         }
17087         
17088         if (this.disabled) {
17089             input.disabled = true;
17090         }
17091         
17092         var inputblock = {
17093             cls : 'roo-combobox-wrap',
17094             cn : [
17095                 input
17096             ]
17097         };
17098         
17099         if(this.before){
17100             inputblock.cls += ' input-group';
17101             
17102             inputblock.cn.unshift({
17103                 tag :'span',
17104                 cls : 'input-group-addon input-group-prepend input-group-text',
17105                 html : this.before
17106             });
17107         }
17108         
17109         if(this.removable && !this.multiple){
17110             inputblock.cls += ' roo-removable';
17111             
17112             inputblock.cn.push({
17113                 tag: 'button',
17114                 html : 'x',
17115                 cls : 'roo-combo-removable-btn close'
17116             });
17117         }
17118
17119         if(this.hasFeedback && !this.allowBlank){
17120             
17121             inputblock.cls += ' has-feedback';
17122             
17123             inputblock.cn.push({
17124                 tag: 'span',
17125                 cls: 'glyphicon form-control-feedback'
17126             });
17127             
17128         }
17129         
17130         if (this.after) {
17131             
17132             inputblock.cls += (this.before) ? '' : ' input-group';
17133             
17134             inputblock.cn.push({
17135                 tag :'span',
17136                 cls : 'input-group-addon input-group-append input-group-text',
17137                 html : this.after
17138             });
17139         }
17140
17141         
17142         var ibwrap = inputblock;
17143         
17144         if(this.multiple){
17145             ibwrap = {
17146                 tag: 'ul',
17147                 cls: 'roo-select2-choices',
17148                 cn:[
17149                     {
17150                         tag: 'li',
17151                         cls: 'roo-select2-search-field',
17152                         cn: [
17153
17154                             inputblock
17155                         ]
17156                     }
17157                 ]
17158             };
17159         
17160             
17161         }
17162         
17163         var combobox = {
17164             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17165             cn: [
17166                 {
17167                     tag: 'input',
17168                     type : 'hidden',
17169                     cls: 'form-hidden-field'
17170                 },
17171                 ibwrap
17172             ]
17173         };
17174         
17175         if(!this.multiple && this.showToggleBtn){
17176             
17177             var caret = {
17178                 cls: 'caret'
17179             };
17180             
17181             if (this.caret != false) {
17182                 caret = {
17183                      tag: 'i',
17184                      cls: 'fa fa-' + this.caret
17185                 };
17186                 
17187             }
17188             
17189             combobox.cn.push({
17190                 tag :'span',
17191                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17192                 cn : [
17193                     Roo.bootstrap.version == 3 ? caret : '',
17194                     {
17195                         tag: 'span',
17196                         cls: 'combobox-clear',
17197                         cn  : [
17198                             {
17199                                 tag : 'i',
17200                                 cls: 'icon-remove'
17201                             }
17202                         ]
17203                     }
17204                 ]
17205
17206             })
17207         }
17208         
17209         if(this.multiple){
17210             combobox.cls += ' roo-select2-container-multi';
17211         }
17212         
17213         var align = this.labelAlign || this.parentLabelAlign();
17214         
17215         if (align ==='left' && this.fieldLabel.length) {
17216
17217             cfg.cn = [
17218                 {
17219                    tag : 'i',
17220                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17221                    tooltip : 'This field is required'
17222                 },
17223                 {
17224                     tag: 'label',
17225                     cls : 'control-label col-form-label',
17226                     html : this.fieldLabel
17227
17228                 },
17229                 {
17230                     cls : 'roo-combobox-wrap ', 
17231                     cn: [
17232                         combobox
17233                     ]
17234                 }
17235             ];
17236             
17237             var labelCfg = cfg.cn[1];
17238             var contentCfg = cfg.cn[2];
17239             
17240
17241             if(this.indicatorpos == 'right'){
17242                 cfg.cn = [
17243                     {
17244                         tag: 'label',
17245                         'for' :  id,
17246                         cls : 'control-label col-form-label',
17247                         cn : [
17248                             {
17249                                 tag : 'span',
17250                                 html : this.fieldLabel
17251                             },
17252                             {
17253                                 tag : 'i',
17254                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17255                                 tooltip : 'This field is required'
17256                             }
17257                         ]
17258                     },
17259                     {
17260                         cls : "roo-combobox-wrap ",
17261                         cn: [
17262                             combobox
17263                         ]
17264                     }
17265
17266                 ];
17267                 
17268                 labelCfg = cfg.cn[0];
17269                 contentCfg = cfg.cn[1];
17270             }
17271             
17272            
17273             
17274             if(this.labelWidth > 12){
17275                 labelCfg.style = "width: " + this.labelWidth + 'px';
17276             }
17277            
17278             if(this.labelWidth < 13 && this.labelmd == 0){
17279                 this.labelmd = this.labelWidth;
17280             }
17281             
17282             if(this.labellg > 0){
17283                 labelCfg.cls += ' col-lg-' + this.labellg;
17284                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17285             }
17286             
17287             if(this.labelmd > 0){
17288                 labelCfg.cls += ' col-md-' + this.labelmd;
17289                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17290             }
17291             
17292             if(this.labelsm > 0){
17293                 labelCfg.cls += ' col-sm-' + this.labelsm;
17294                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17295             }
17296             
17297             if(this.labelxs > 0){
17298                 labelCfg.cls += ' col-xs-' + this.labelxs;
17299                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17300             }
17301                 
17302                 
17303         } else if ( this.fieldLabel.length) {
17304             cfg.cn = [
17305                 {
17306                    tag : 'i',
17307                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17308                    tooltip : 'This field is required'
17309                 },
17310                 {
17311                     tag: 'label',
17312                     cls : 'control-label',
17313                     html : this.fieldLabel
17314
17315                 },
17316                 {
17317                     cls : '', 
17318                     cn: [
17319                         combobox
17320                     ]
17321                 }
17322             ];
17323             
17324             if(this.indicatorpos == 'right'){
17325                 cfg.cn = [
17326                     {
17327                         tag: 'label',
17328                         cls : 'control-label',
17329                         html : this.fieldLabel,
17330                         cn : [
17331                             {
17332                                tag : 'i',
17333                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17334                                tooltip : 'This field is required'
17335                             }
17336                         ]
17337                     },
17338                     {
17339                         cls : '', 
17340                         cn: [
17341                             combobox
17342                         ]
17343                     }
17344                 ];
17345             }
17346         } else {
17347             cfg.cn = combobox;    
17348         }
17349         
17350         
17351         var settings = this;
17352         
17353         ['xs','sm','md','lg'].map(function(size){
17354             if (settings[size]) {
17355                 cfg.cls += ' col-' + size + '-' + settings[size];
17356             }
17357         });
17358         
17359         return cfg;
17360     },
17361     
17362     initTouchView : function()
17363     {
17364         this.renderTouchView();
17365         
17366         this.touchViewEl.on('scroll', function(){
17367             this.el.dom.scrollTop = 0;
17368         }, this);
17369         
17370         this.originalValue = this.getValue();
17371         
17372         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17373         
17374         this.inputEl().on("click", this.showTouchView, this);
17375         if (this.triggerEl) {
17376             this.triggerEl.on("click", this.showTouchView, this);
17377         }
17378         
17379         
17380         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17381         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17382         
17383         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17384         
17385         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17386         this.store.on('load', this.onTouchViewLoad, this);
17387         this.store.on('loadexception', this.onTouchViewLoadException, this);
17388         
17389         if(this.hiddenName){
17390             
17391             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17392             
17393             this.hiddenField.dom.value =
17394                 this.hiddenValue !== undefined ? this.hiddenValue :
17395                 this.value !== undefined ? this.value : '';
17396         
17397             this.el.dom.removeAttribute('name');
17398             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17399         }
17400         
17401         if(this.multiple){
17402             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17403             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17404         }
17405         
17406         if(this.removable && !this.multiple){
17407             var close = this.closeTriggerEl();
17408             if(close){
17409                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17410                 close.on('click', this.removeBtnClick, this, close);
17411             }
17412         }
17413         /*
17414          * fix the bug in Safari iOS8
17415          */
17416         this.inputEl().on("focus", function(e){
17417             document.activeElement.blur();
17418         }, this);
17419         
17420         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17421         
17422         return;
17423         
17424         
17425     },
17426     
17427     renderTouchView : function()
17428     {
17429         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17430         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17431         
17432         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17433         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434         
17435         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17436         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437         this.touchViewBodyEl.setStyle('overflow', 'auto');
17438         
17439         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17440         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17441         
17442         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17443         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17444         
17445     },
17446     
17447     showTouchView : function()
17448     {
17449         if(this.disabled){
17450             return;
17451         }
17452         
17453         this.touchViewHeaderEl.hide();
17454
17455         if(this.modalTitle.length){
17456             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17457             this.touchViewHeaderEl.show();
17458         }
17459
17460         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17461         this.touchViewEl.show();
17462
17463         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17464         
17465         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17466         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17467
17468         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17469
17470         if(this.modalTitle.length){
17471             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17472         }
17473         
17474         this.touchViewBodyEl.setHeight(bodyHeight);
17475
17476         if(this.animate){
17477             var _this = this;
17478             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17479         }else{
17480             this.touchViewEl.addClass(['in','show']);
17481         }
17482         
17483         if(this._touchViewMask){
17484             Roo.get(document.body).addClass("x-body-masked");
17485             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17486             this._touchViewMask.setStyle('z-index', 10000);
17487             this._touchViewMask.addClass('show');
17488         }
17489         
17490         this.doTouchViewQuery();
17491         
17492     },
17493     
17494     hideTouchView : function()
17495     {
17496         this.touchViewEl.removeClass(['in','show']);
17497
17498         if(this.animate){
17499             var _this = this;
17500             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17501         }else{
17502             this.touchViewEl.setStyle('display', 'none');
17503         }
17504         
17505         if(this._touchViewMask){
17506             this._touchViewMask.removeClass('show');
17507             Roo.get(document.body).removeClass("x-body-masked");
17508         }
17509     },
17510     
17511     setTouchViewValue : function()
17512     {
17513         if(this.multiple){
17514             this.clearItem();
17515         
17516             var _this = this;
17517
17518             Roo.each(this.tickItems, function(o){
17519                 this.addItem(o);
17520             }, this);
17521         }
17522         
17523         this.hideTouchView();
17524     },
17525     
17526     doTouchViewQuery : function()
17527     {
17528         var qe = {
17529             query: '',
17530             forceAll: true,
17531             combo: this,
17532             cancel:false
17533         };
17534         
17535         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17536             return false;
17537         }
17538         
17539         if(!this.alwaysQuery || this.mode == 'local'){
17540             this.onTouchViewLoad();
17541             return;
17542         }
17543         
17544         this.store.load();
17545     },
17546     
17547     onTouchViewBeforeLoad : function(combo,opts)
17548     {
17549         return;
17550     },
17551
17552     // private
17553     onTouchViewLoad : function()
17554     {
17555         if(this.store.getCount() < 1){
17556             this.onTouchViewEmptyResults();
17557             return;
17558         }
17559         
17560         this.clearTouchView();
17561         
17562         var rawValue = this.getRawValue();
17563         
17564         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17565         
17566         this.tickItems = [];
17567         
17568         this.store.data.each(function(d, rowIndex){
17569             var row = this.touchViewListGroup.createChild(template);
17570             
17571             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17572                 row.addClass(d.data.cls);
17573             }
17574             
17575             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17576                 var cfg = {
17577                     data : d.data,
17578                     html : d.data[this.displayField]
17579                 };
17580                 
17581                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17582                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17583                 }
17584             }
17585             row.removeClass('selected');
17586             if(!this.multiple && this.valueField &&
17587                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17588             {
17589                 // radio buttons..
17590                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17591                 row.addClass('selected');
17592             }
17593             
17594             if(this.multiple && this.valueField &&
17595                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17596             {
17597                 
17598                 // checkboxes...
17599                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17600                 this.tickItems.push(d.data);
17601             }
17602             
17603             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17604             
17605         }, this);
17606         
17607         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17608         
17609         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17610
17611         if(this.modalTitle.length){
17612             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17613         }
17614
17615         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17616         
17617         if(this.mobile_restrict_height && listHeight < bodyHeight){
17618             this.touchViewBodyEl.setHeight(listHeight);
17619         }
17620         
17621         var _this = this;
17622         
17623         if(firstChecked && listHeight > bodyHeight){
17624             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17625         }
17626         
17627     },
17628     
17629     onTouchViewLoadException : function()
17630     {
17631         this.hideTouchView();
17632     },
17633     
17634     onTouchViewEmptyResults : function()
17635     {
17636         this.clearTouchView();
17637         
17638         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17639         
17640         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17641         
17642     },
17643     
17644     clearTouchView : function()
17645     {
17646         this.touchViewListGroup.dom.innerHTML = '';
17647     },
17648     
17649     onTouchViewClick : function(e, el, o)
17650     {
17651         e.preventDefault();
17652         
17653         var row = o.row;
17654         var rowIndex = o.rowIndex;
17655         
17656         var r = this.store.getAt(rowIndex);
17657         
17658         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17659             
17660             if(!this.multiple){
17661                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17662                     c.dom.removeAttribute('checked');
17663                 }, this);
17664
17665                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17666
17667                 this.setFromData(r.data);
17668
17669                 var close = this.closeTriggerEl();
17670
17671                 if(close){
17672                     close.show();
17673                 }
17674
17675                 this.hideTouchView();
17676
17677                 this.fireEvent('select', this, r, rowIndex);
17678
17679                 return;
17680             }
17681
17682             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17683                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17684                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17685                 return;
17686             }
17687
17688             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689             this.addItem(r.data);
17690             this.tickItems.push(r.data);
17691         }
17692     },
17693     
17694     getAutoCreateNativeIOS : function()
17695     {
17696         var cfg = {
17697             cls: 'form-group' //input-group,
17698         };
17699         
17700         var combobox =  {
17701             tag: 'select',
17702             cls : 'roo-ios-select'
17703         };
17704         
17705         if (this.name) {
17706             combobox.name = this.name;
17707         }
17708         
17709         if (this.disabled) {
17710             combobox.disabled = true;
17711         }
17712         
17713         var settings = this;
17714         
17715         ['xs','sm','md','lg'].map(function(size){
17716             if (settings[size]) {
17717                 cfg.cls += ' col-' + size + '-' + settings[size];
17718             }
17719         });
17720         
17721         cfg.cn = combobox;
17722         
17723         return cfg;
17724         
17725     },
17726     
17727     initIOSView : function()
17728     {
17729         this.store.on('load', this.onIOSViewLoad, this);
17730         
17731         return;
17732     },
17733     
17734     onIOSViewLoad : function()
17735     {
17736         if(this.store.getCount() < 1){
17737             return;
17738         }
17739         
17740         this.clearIOSView();
17741         
17742         if(this.allowBlank) {
17743             
17744             var default_text = '-- SELECT --';
17745             
17746             if(this.placeholder.length){
17747                 default_text = this.placeholder;
17748             }
17749             
17750             if(this.emptyTitle.length){
17751                 default_text += ' - ' + this.emptyTitle + ' -';
17752             }
17753             
17754             var opt = this.inputEl().createChild({
17755                 tag: 'option',
17756                 value : 0,
17757                 html : default_text
17758             });
17759             
17760             var o = {};
17761             o[this.valueField] = 0;
17762             o[this.displayField] = default_text;
17763             
17764             this.ios_options.push({
17765                 data : o,
17766                 el : opt
17767             });
17768             
17769         }
17770         
17771         this.store.data.each(function(d, rowIndex){
17772             
17773             var html = '';
17774             
17775             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17776                 html = d.data[this.displayField];
17777             }
17778             
17779             var value = '';
17780             
17781             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17782                 value = d.data[this.valueField];
17783             }
17784             
17785             var option = {
17786                 tag: 'option',
17787                 value : value,
17788                 html : html
17789             };
17790             
17791             if(this.value == d.data[this.valueField]){
17792                 option['selected'] = true;
17793             }
17794             
17795             var opt = this.inputEl().createChild(option);
17796             
17797             this.ios_options.push({
17798                 data : d.data,
17799                 el : opt
17800             });
17801             
17802         }, this);
17803         
17804         this.inputEl().on('change', function(){
17805            this.fireEvent('select', this);
17806         }, this);
17807         
17808     },
17809     
17810     clearIOSView: function()
17811     {
17812         this.inputEl().dom.innerHTML = '';
17813         
17814         this.ios_options = [];
17815     },
17816     
17817     setIOSValue: function(v)
17818     {
17819         this.value = v;
17820         
17821         if(!this.ios_options){
17822             return;
17823         }
17824         
17825         Roo.each(this.ios_options, function(opts){
17826            
17827            opts.el.dom.removeAttribute('selected');
17828            
17829            if(opts.data[this.valueField] != v){
17830                return;
17831            }
17832            
17833            opts.el.dom.setAttribute('selected', true);
17834            
17835         }, this);
17836     }
17837
17838     /** 
17839     * @cfg {Boolean} grow 
17840     * @hide 
17841     */
17842     /** 
17843     * @cfg {Number} growMin 
17844     * @hide 
17845     */
17846     /** 
17847     * @cfg {Number} growMax 
17848     * @hide 
17849     */
17850     /**
17851      * @hide
17852      * @method autoSize
17853      */
17854 });
17855
17856 Roo.apply(Roo.bootstrap.ComboBox,  {
17857     
17858     header : {
17859         tag: 'div',
17860         cls: 'modal-header',
17861         cn: [
17862             {
17863                 tag: 'h4',
17864                 cls: 'modal-title'
17865             }
17866         ]
17867     },
17868     
17869     body : {
17870         tag: 'div',
17871         cls: 'modal-body',
17872         cn: [
17873             {
17874                 tag: 'ul',
17875                 cls: 'list-group'
17876             }
17877         ]
17878     },
17879     
17880     listItemRadio : {
17881         tag: 'li',
17882         cls: 'list-group-item',
17883         cn: [
17884             {
17885                 tag: 'span',
17886                 cls: 'roo-combobox-list-group-item-value'
17887             },
17888             {
17889                 tag: 'div',
17890                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17891                 cn: [
17892                     {
17893                         tag: 'input',
17894                         type: 'radio'
17895                     },
17896                     {
17897                         tag: 'label'
17898                     }
17899                 ]
17900             }
17901         ]
17902     },
17903     
17904     listItemCheckbox : {
17905         tag: 'li',
17906         cls: 'list-group-item',
17907         cn: [
17908             {
17909                 tag: 'span',
17910                 cls: 'roo-combobox-list-group-item-value'
17911             },
17912             {
17913                 tag: 'div',
17914                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17915                 cn: [
17916                     {
17917                         tag: 'input',
17918                         type: 'checkbox'
17919                     },
17920                     {
17921                         tag: 'label'
17922                     }
17923                 ]
17924             }
17925         ]
17926     },
17927     
17928     emptyResult : {
17929         tag: 'div',
17930         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17931     },
17932     
17933     footer : {
17934         tag: 'div',
17935         cls: 'modal-footer',
17936         cn: [
17937             {
17938                 tag: 'div',
17939                 cls: 'row',
17940                 cn: [
17941                     {
17942                         tag: 'div',
17943                         cls: 'col-xs-6 text-left',
17944                         cn: {
17945                             tag: 'button',
17946                             cls: 'btn btn-danger roo-touch-view-cancel',
17947                             html: 'Cancel'
17948                         }
17949                     },
17950                     {
17951                         tag: 'div',
17952                         cls: 'col-xs-6 text-right',
17953                         cn: {
17954                             tag: 'button',
17955                             cls: 'btn btn-success roo-touch-view-ok',
17956                             html: 'OK'
17957                         }
17958                     }
17959                 ]
17960             }
17961         ]
17962         
17963     }
17964 });
17965
17966 Roo.apply(Roo.bootstrap.ComboBox,  {
17967     
17968     touchViewTemplate : {
17969         tag: 'div',
17970         cls: 'modal fade roo-combobox-touch-view',
17971         cn: [
17972             {
17973                 tag: 'div',
17974                 cls: 'modal-dialog',
17975                 style : 'position:fixed', // we have to fix position....
17976                 cn: [
17977                     {
17978                         tag: 'div',
17979                         cls: 'modal-content',
17980                         cn: [
17981                             Roo.bootstrap.ComboBox.header,
17982                             Roo.bootstrap.ComboBox.body,
17983                             Roo.bootstrap.ComboBox.footer
17984                         ]
17985                     }
17986                 ]
17987             }
17988         ]
17989     }
17990 });/*
17991  * Based on:
17992  * Ext JS Library 1.1.1
17993  * Copyright(c) 2006-2007, Ext JS, LLC.
17994  *
17995  * Originally Released Under LGPL - original licence link has changed is not relivant.
17996  *
17997  * Fork - LGPL
17998  * <script type="text/javascript">
17999  */
18000
18001 /**
18002  * @class Roo.View
18003  * @extends Roo.util.Observable
18004  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18005  * This class also supports single and multi selection modes. <br>
18006  * Create a data model bound view:
18007  <pre><code>
18008  var store = new Roo.data.Store(...);
18009
18010  var view = new Roo.View({
18011     el : "my-element",
18012     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18013  
18014     singleSelect: true,
18015     selectedClass: "ydataview-selected",
18016     store: store
18017  });
18018
18019  // listen for node click?
18020  view.on("click", function(vw, index, node, e){
18021  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18022  });
18023
18024  // load XML data
18025  dataModel.load("foobar.xml");
18026  </code></pre>
18027  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18028  * <br><br>
18029  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18030  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18031  * 
18032  * Note: old style constructor is still suported (container, template, config)
18033  * 
18034  * @constructor
18035  * Create a new View
18036  * @param {Object} config The config object
18037  * 
18038  */
18039 Roo.View = function(config, depreciated_tpl, depreciated_config){
18040     
18041     this.parent = false;
18042     
18043     if (typeof(depreciated_tpl) == 'undefined') {
18044         // new way.. - universal constructor.
18045         Roo.apply(this, config);
18046         this.el  = Roo.get(this.el);
18047     } else {
18048         // old format..
18049         this.el  = Roo.get(config);
18050         this.tpl = depreciated_tpl;
18051         Roo.apply(this, depreciated_config);
18052     }
18053     this.wrapEl  = this.el.wrap().wrap();
18054     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18055     
18056     
18057     if(typeof(this.tpl) == "string"){
18058         this.tpl = new Roo.Template(this.tpl);
18059     } else {
18060         // support xtype ctors..
18061         this.tpl = new Roo.factory(this.tpl, Roo);
18062     }
18063     
18064     
18065     this.tpl.compile();
18066     
18067     /** @private */
18068     this.addEvents({
18069         /**
18070          * @event beforeclick
18071          * Fires before a click is processed. Returns false to cancel the default action.
18072          * @param {Roo.View} this
18073          * @param {Number} index The index of the target node
18074          * @param {HTMLElement} node The target node
18075          * @param {Roo.EventObject} e The raw event object
18076          */
18077             "beforeclick" : true,
18078         /**
18079          * @event click
18080          * Fires when a template node is clicked.
18081          * @param {Roo.View} this
18082          * @param {Number} index The index of the target node
18083          * @param {HTMLElement} node The target node
18084          * @param {Roo.EventObject} e The raw event object
18085          */
18086             "click" : true,
18087         /**
18088          * @event dblclick
18089          * Fires when a template node is double clicked.
18090          * @param {Roo.View} this
18091          * @param {Number} index The index of the target node
18092          * @param {HTMLElement} node The target node
18093          * @param {Roo.EventObject} e The raw event object
18094          */
18095             "dblclick" : true,
18096         /**
18097          * @event contextmenu
18098          * Fires when a template node is right clicked.
18099          * @param {Roo.View} this
18100          * @param {Number} index The index of the target node
18101          * @param {HTMLElement} node The target node
18102          * @param {Roo.EventObject} e The raw event object
18103          */
18104             "contextmenu" : true,
18105         /**
18106          * @event selectionchange
18107          * Fires when the selected nodes change.
18108          * @param {Roo.View} this
18109          * @param {Array} selections Array of the selected nodes
18110          */
18111             "selectionchange" : true,
18112     
18113         /**
18114          * @event beforeselect
18115          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18116          * @param {Roo.View} this
18117          * @param {HTMLElement} node The node to be selected
18118          * @param {Array} selections Array of currently selected nodes
18119          */
18120             "beforeselect" : true,
18121         /**
18122          * @event preparedata
18123          * Fires on every row to render, to allow you to change the data.
18124          * @param {Roo.View} this
18125          * @param {Object} data to be rendered (change this)
18126          */
18127           "preparedata" : true
18128           
18129           
18130         });
18131
18132
18133
18134     this.el.on({
18135         "click": this.onClick,
18136         "dblclick": this.onDblClick,
18137         "contextmenu": this.onContextMenu,
18138         scope:this
18139     });
18140
18141     this.selections = [];
18142     this.nodes = [];
18143     this.cmp = new Roo.CompositeElementLite([]);
18144     if(this.store){
18145         this.store = Roo.factory(this.store, Roo.data);
18146         this.setStore(this.store, true);
18147     }
18148     
18149     if ( this.footer && this.footer.xtype) {
18150            
18151          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18152         
18153         this.footer.dataSource = this.store;
18154         this.footer.container = fctr;
18155         this.footer = Roo.factory(this.footer, Roo);
18156         fctr.insertFirst(this.el);
18157         
18158         // this is a bit insane - as the paging toolbar seems to detach the el..
18159 //        dom.parentNode.parentNode.parentNode
18160          // they get detached?
18161     }
18162     
18163     
18164     Roo.View.superclass.constructor.call(this);
18165     
18166     
18167 };
18168
18169 Roo.extend(Roo.View, Roo.util.Observable, {
18170     
18171      /**
18172      * @cfg {Roo.data.Store} store Data store to load data from.
18173      */
18174     store : false,
18175     
18176     /**
18177      * @cfg {String|Roo.Element} el The container element.
18178      */
18179     el : '',
18180     
18181     /**
18182      * @cfg {String|Roo.Template} tpl The template used by this View 
18183      */
18184     tpl : false,
18185     /**
18186      * @cfg {String} dataName the named area of the template to use as the data area
18187      *                          Works with domtemplates roo-name="name"
18188      */
18189     dataName: false,
18190     /**
18191      * @cfg {String} selectedClass The css class to add to selected nodes
18192      */
18193     selectedClass : "x-view-selected",
18194      /**
18195      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18196      */
18197     emptyText : "",
18198     
18199     /**
18200      * @cfg {String} text to display on mask (default Loading)
18201      */
18202     mask : false,
18203     /**
18204      * @cfg {Boolean} multiSelect Allow multiple selection
18205      */
18206     multiSelect : false,
18207     /**
18208      * @cfg {Boolean} singleSelect Allow single selection
18209      */
18210     singleSelect:  false,
18211     
18212     /**
18213      * @cfg {Boolean} toggleSelect - selecting 
18214      */
18215     toggleSelect : false,
18216     
18217     /**
18218      * @cfg {Boolean} tickable - selecting 
18219      */
18220     tickable : false,
18221     
18222     /**
18223      * Returns the element this view is bound to.
18224      * @return {Roo.Element}
18225      */
18226     getEl : function(){
18227         return this.wrapEl;
18228     },
18229     
18230     
18231
18232     /**
18233      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18234      */
18235     refresh : function(){
18236         //Roo.log('refresh');
18237         var t = this.tpl;
18238         
18239         // if we are using something like 'domtemplate', then
18240         // the what gets used is:
18241         // t.applySubtemplate(NAME, data, wrapping data..)
18242         // the outer template then get' applied with
18243         //     the store 'extra data'
18244         // and the body get's added to the
18245         //      roo-name="data" node?
18246         //      <span class='roo-tpl-{name}'></span> ?????
18247         
18248         
18249         
18250         this.clearSelections();
18251         this.el.update("");
18252         var html = [];
18253         var records = this.store.getRange();
18254         if(records.length < 1) {
18255             
18256             // is this valid??  = should it render a template??
18257             
18258             this.el.update(this.emptyText);
18259             return;
18260         }
18261         var el = this.el;
18262         if (this.dataName) {
18263             this.el.update(t.apply(this.store.meta)); //????
18264             el = this.el.child('.roo-tpl-' + this.dataName);
18265         }
18266         
18267         for(var i = 0, len = records.length; i < len; i++){
18268             var data = this.prepareData(records[i].data, i, records[i]);
18269             this.fireEvent("preparedata", this, data, i, records[i]);
18270             
18271             var d = Roo.apply({}, data);
18272             
18273             if(this.tickable){
18274                 Roo.apply(d, {'roo-id' : Roo.id()});
18275                 
18276                 var _this = this;
18277             
18278                 Roo.each(this.parent.item, function(item){
18279                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18280                         return;
18281                     }
18282                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18283                 });
18284             }
18285             
18286             html[html.length] = Roo.util.Format.trim(
18287                 this.dataName ?
18288                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18289                     t.apply(d)
18290             );
18291         }
18292         
18293         
18294         
18295         el.update(html.join(""));
18296         this.nodes = el.dom.childNodes;
18297         this.updateIndexes(0);
18298     },
18299     
18300
18301     /**
18302      * Function to override to reformat the data that is sent to
18303      * the template for each node.
18304      * DEPRICATED - use the preparedata event handler.
18305      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18306      * a JSON object for an UpdateManager bound view).
18307      */
18308     prepareData : function(data, index, record)
18309     {
18310         this.fireEvent("preparedata", this, data, index, record);
18311         return data;
18312     },
18313
18314     onUpdate : function(ds, record){
18315         // Roo.log('on update');   
18316         this.clearSelections();
18317         var index = this.store.indexOf(record);
18318         var n = this.nodes[index];
18319         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18320         n.parentNode.removeChild(n);
18321         this.updateIndexes(index, index);
18322     },
18323
18324     
18325     
18326 // --------- FIXME     
18327     onAdd : function(ds, records, index)
18328     {
18329         //Roo.log(['on Add', ds, records, index] );        
18330         this.clearSelections();
18331         if(this.nodes.length == 0){
18332             this.refresh();
18333             return;
18334         }
18335         var n = this.nodes[index];
18336         for(var i = 0, len = records.length; i < len; i++){
18337             var d = this.prepareData(records[i].data, i, records[i]);
18338             if(n){
18339                 this.tpl.insertBefore(n, d);
18340             }else{
18341                 
18342                 this.tpl.append(this.el, d);
18343             }
18344         }
18345         this.updateIndexes(index);
18346     },
18347
18348     onRemove : function(ds, record, index){
18349        // Roo.log('onRemove');
18350         this.clearSelections();
18351         var el = this.dataName  ?
18352             this.el.child('.roo-tpl-' + this.dataName) :
18353             this.el; 
18354         
18355         el.dom.removeChild(this.nodes[index]);
18356         this.updateIndexes(index);
18357     },
18358
18359     /**
18360      * Refresh an individual node.
18361      * @param {Number} index
18362      */
18363     refreshNode : function(index){
18364         this.onUpdate(this.store, this.store.getAt(index));
18365     },
18366
18367     updateIndexes : function(startIndex, endIndex){
18368         var ns = this.nodes;
18369         startIndex = startIndex || 0;
18370         endIndex = endIndex || ns.length - 1;
18371         for(var i = startIndex; i <= endIndex; i++){
18372             ns[i].nodeIndex = i;
18373         }
18374     },
18375
18376     /**
18377      * Changes the data store this view uses and refresh the view.
18378      * @param {Store} store
18379      */
18380     setStore : function(store, initial){
18381         if(!initial && this.store){
18382             this.store.un("datachanged", this.refresh);
18383             this.store.un("add", this.onAdd);
18384             this.store.un("remove", this.onRemove);
18385             this.store.un("update", this.onUpdate);
18386             this.store.un("clear", this.refresh);
18387             this.store.un("beforeload", this.onBeforeLoad);
18388             this.store.un("load", this.onLoad);
18389             this.store.un("loadexception", this.onLoad);
18390         }
18391         if(store){
18392           
18393             store.on("datachanged", this.refresh, this);
18394             store.on("add", this.onAdd, this);
18395             store.on("remove", this.onRemove, this);
18396             store.on("update", this.onUpdate, this);
18397             store.on("clear", this.refresh, this);
18398             store.on("beforeload", this.onBeforeLoad, this);
18399             store.on("load", this.onLoad, this);
18400             store.on("loadexception", this.onLoad, this);
18401         }
18402         
18403         if(store){
18404             this.refresh();
18405         }
18406     },
18407     /**
18408      * onbeforeLoad - masks the loading area.
18409      *
18410      */
18411     onBeforeLoad : function(store,opts)
18412     {
18413          //Roo.log('onBeforeLoad');   
18414         if (!opts.add) {
18415             this.el.update("");
18416         }
18417         this.el.mask(this.mask ? this.mask : "Loading" ); 
18418     },
18419     onLoad : function ()
18420     {
18421         this.el.unmask();
18422     },
18423     
18424
18425     /**
18426      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18427      * @param {HTMLElement} node
18428      * @return {HTMLElement} The template node
18429      */
18430     findItemFromChild : function(node){
18431         var el = this.dataName  ?
18432             this.el.child('.roo-tpl-' + this.dataName,true) :
18433             this.el.dom; 
18434         
18435         if(!node || node.parentNode == el){
18436                     return node;
18437             }
18438             var p = node.parentNode;
18439             while(p && p != el){
18440             if(p.parentNode == el){
18441                 return p;
18442             }
18443             p = p.parentNode;
18444         }
18445             return null;
18446     },
18447
18448     /** @ignore */
18449     onClick : function(e){
18450         var item = this.findItemFromChild(e.getTarget());
18451         if(item){
18452             var index = this.indexOf(item);
18453             if(this.onItemClick(item, index, e) !== false){
18454                 this.fireEvent("click", this, index, item, e);
18455             }
18456         }else{
18457             this.clearSelections();
18458         }
18459     },
18460
18461     /** @ignore */
18462     onContextMenu : function(e){
18463         var item = this.findItemFromChild(e.getTarget());
18464         if(item){
18465             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18466         }
18467     },
18468
18469     /** @ignore */
18470     onDblClick : function(e){
18471         var item = this.findItemFromChild(e.getTarget());
18472         if(item){
18473             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18474         }
18475     },
18476
18477     onItemClick : function(item, index, e)
18478     {
18479         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18480             return false;
18481         }
18482         if (this.toggleSelect) {
18483             var m = this.isSelected(item) ? 'unselect' : 'select';
18484             //Roo.log(m);
18485             var _t = this;
18486             _t[m](item, true, false);
18487             return true;
18488         }
18489         if(this.multiSelect || this.singleSelect){
18490             if(this.multiSelect && e.shiftKey && this.lastSelection){
18491                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18492             }else{
18493                 this.select(item, this.multiSelect && e.ctrlKey);
18494                 this.lastSelection = item;
18495             }
18496             
18497             if(!this.tickable){
18498                 e.preventDefault();
18499             }
18500             
18501         }
18502         return true;
18503     },
18504
18505     /**
18506      * Get the number of selected nodes.
18507      * @return {Number}
18508      */
18509     getSelectionCount : function(){
18510         return this.selections.length;
18511     },
18512
18513     /**
18514      * Get the currently selected nodes.
18515      * @return {Array} An array of HTMLElements
18516      */
18517     getSelectedNodes : function(){
18518         return this.selections;
18519     },
18520
18521     /**
18522      * Get the indexes of the selected nodes.
18523      * @return {Array}
18524      */
18525     getSelectedIndexes : function(){
18526         var indexes = [], s = this.selections;
18527         for(var i = 0, len = s.length; i < len; i++){
18528             indexes.push(s[i].nodeIndex);
18529         }
18530         return indexes;
18531     },
18532
18533     /**
18534      * Clear all selections
18535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18536      */
18537     clearSelections : function(suppressEvent){
18538         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18539             this.cmp.elements = this.selections;
18540             this.cmp.removeClass(this.selectedClass);
18541             this.selections = [];
18542             if(!suppressEvent){
18543                 this.fireEvent("selectionchange", this, this.selections);
18544             }
18545         }
18546     },
18547
18548     /**
18549      * Returns true if the passed node is selected
18550      * @param {HTMLElement/Number} node The node or node index
18551      * @return {Boolean}
18552      */
18553     isSelected : function(node){
18554         var s = this.selections;
18555         if(s.length < 1){
18556             return false;
18557         }
18558         node = this.getNode(node);
18559         return s.indexOf(node) !== -1;
18560     },
18561
18562     /**
18563      * Selects nodes.
18564      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18565      * @param {Boolean} keepExisting (optional) true to keep existing selections
18566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18567      */
18568     select : function(nodeInfo, keepExisting, suppressEvent){
18569         if(nodeInfo instanceof Array){
18570             if(!keepExisting){
18571                 this.clearSelections(true);
18572             }
18573             for(var i = 0, len = nodeInfo.length; i < len; i++){
18574                 this.select(nodeInfo[i], true, true);
18575             }
18576             return;
18577         } 
18578         var node = this.getNode(nodeInfo);
18579         if(!node || this.isSelected(node)){
18580             return; // already selected.
18581         }
18582         if(!keepExisting){
18583             this.clearSelections(true);
18584         }
18585         
18586         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18587             Roo.fly(node).addClass(this.selectedClass);
18588             this.selections.push(node);
18589             if(!suppressEvent){
18590                 this.fireEvent("selectionchange", this, this.selections);
18591             }
18592         }
18593         
18594         
18595     },
18596       /**
18597      * Unselects nodes.
18598      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18599      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18600      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18601      */
18602     unselect : function(nodeInfo, keepExisting, suppressEvent)
18603     {
18604         if(nodeInfo instanceof Array){
18605             Roo.each(this.selections, function(s) {
18606                 this.unselect(s, nodeInfo);
18607             }, this);
18608             return;
18609         }
18610         var node = this.getNode(nodeInfo);
18611         if(!node || !this.isSelected(node)){
18612             //Roo.log("not selected");
18613             return; // not selected.
18614         }
18615         // fireevent???
18616         var ns = [];
18617         Roo.each(this.selections, function(s) {
18618             if (s == node ) {
18619                 Roo.fly(node).removeClass(this.selectedClass);
18620
18621                 return;
18622             }
18623             ns.push(s);
18624         },this);
18625         
18626         this.selections= ns;
18627         this.fireEvent("selectionchange", this, this.selections);
18628     },
18629
18630     /**
18631      * Gets a template node.
18632      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18633      * @return {HTMLElement} The node or null if it wasn't found
18634      */
18635     getNode : function(nodeInfo){
18636         if(typeof nodeInfo == "string"){
18637             return document.getElementById(nodeInfo);
18638         }else if(typeof nodeInfo == "number"){
18639             return this.nodes[nodeInfo];
18640         }
18641         return nodeInfo;
18642     },
18643
18644     /**
18645      * Gets a range template nodes.
18646      * @param {Number} startIndex
18647      * @param {Number} endIndex
18648      * @return {Array} An array of nodes
18649      */
18650     getNodes : function(start, end){
18651         var ns = this.nodes;
18652         start = start || 0;
18653         end = typeof end == "undefined" ? ns.length - 1 : end;
18654         var nodes = [];
18655         if(start <= end){
18656             for(var i = start; i <= end; i++){
18657                 nodes.push(ns[i]);
18658             }
18659         } else{
18660             for(var i = start; i >= end; i--){
18661                 nodes.push(ns[i]);
18662             }
18663         }
18664         return nodes;
18665     },
18666
18667     /**
18668      * Finds the index of the passed node
18669      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18670      * @return {Number} The index of the node or -1
18671      */
18672     indexOf : function(node){
18673         node = this.getNode(node);
18674         if(typeof node.nodeIndex == "number"){
18675             return node.nodeIndex;
18676         }
18677         var ns = this.nodes;
18678         for(var i = 0, len = ns.length; i < len; i++){
18679             if(ns[i] == node){
18680                 return i;
18681             }
18682         }
18683         return -1;
18684     }
18685 });
18686 /*
18687  * - LGPL
18688  *
18689  * based on jquery fullcalendar
18690  * 
18691  */
18692
18693 Roo.bootstrap = Roo.bootstrap || {};
18694 /**
18695  * @class Roo.bootstrap.Calendar
18696  * @extends Roo.bootstrap.Component
18697  * Bootstrap Calendar class
18698  * @cfg {Boolean} loadMask (true|false) default false
18699  * @cfg {Object} header generate the user specific header of the calendar, default false
18700
18701  * @constructor
18702  * Create a new Container
18703  * @param {Object} config The config object
18704  */
18705
18706
18707
18708 Roo.bootstrap.Calendar = function(config){
18709     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18710      this.addEvents({
18711         /**
18712              * @event select
18713              * Fires when a date is selected
18714              * @param {DatePicker} this
18715              * @param {Date} date The selected date
18716              */
18717         'select': true,
18718         /**
18719              * @event monthchange
18720              * Fires when the displayed month changes 
18721              * @param {DatePicker} this
18722              * @param {Date} date The selected month
18723              */
18724         'monthchange': true,
18725         /**
18726              * @event evententer
18727              * Fires when mouse over an event
18728              * @param {Calendar} this
18729              * @param {event} Event
18730              */
18731         'evententer': true,
18732         /**
18733              * @event eventleave
18734              * Fires when the mouse leaves an
18735              * @param {Calendar} this
18736              * @param {event}
18737              */
18738         'eventleave': true,
18739         /**
18740              * @event eventclick
18741              * Fires when the mouse click an
18742              * @param {Calendar} this
18743              * @param {event}
18744              */
18745         'eventclick': true
18746         
18747     });
18748
18749 };
18750
18751 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18752     
18753      /**
18754      * @cfg {Number} startDay
18755      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18756      */
18757     startDay : 0,
18758     
18759     loadMask : false,
18760     
18761     header : false,
18762       
18763     getAutoCreate : function(){
18764         
18765         
18766         var fc_button = function(name, corner, style, content ) {
18767             return Roo.apply({},{
18768                 tag : 'span',
18769                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18770                          (corner.length ?
18771                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18772                             ''
18773                         ),
18774                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18775                 unselectable: 'on'
18776             });
18777         };
18778         
18779         var header = {};
18780         
18781         if(!this.header){
18782             header = {
18783                 tag : 'table',
18784                 cls : 'fc-header',
18785                 style : 'width:100%',
18786                 cn : [
18787                     {
18788                         tag: 'tr',
18789                         cn : [
18790                             {
18791                                 tag : 'td',
18792                                 cls : 'fc-header-left',
18793                                 cn : [
18794                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18795                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18796                                     { tag: 'span', cls: 'fc-header-space' },
18797                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18798
18799
18800                                 ]
18801                             },
18802
18803                             {
18804                                 tag : 'td',
18805                                 cls : 'fc-header-center',
18806                                 cn : [
18807                                     {
18808                                         tag: 'span',
18809                                         cls: 'fc-header-title',
18810                                         cn : {
18811                                             tag: 'H2',
18812                                             html : 'month / year'
18813                                         }
18814                                     }
18815
18816                                 ]
18817                             },
18818                             {
18819                                 tag : 'td',
18820                                 cls : 'fc-header-right',
18821                                 cn : [
18822                               /*      fc_button('month', 'left', '', 'month' ),
18823                                     fc_button('week', '', '', 'week' ),
18824                                     fc_button('day', 'right', '', 'day' )
18825                                 */    
18826
18827                                 ]
18828                             }
18829
18830                         ]
18831                     }
18832                 ]
18833             };
18834         }
18835         
18836         header = this.header;
18837         
18838        
18839         var cal_heads = function() {
18840             var ret = [];
18841             // fixme - handle this.
18842             
18843             for (var i =0; i < Date.dayNames.length; i++) {
18844                 var d = Date.dayNames[i];
18845                 ret.push({
18846                     tag: 'th',
18847                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18848                     html : d.substring(0,3)
18849                 });
18850                 
18851             }
18852             ret[0].cls += ' fc-first';
18853             ret[6].cls += ' fc-last';
18854             return ret;
18855         };
18856         var cal_cell = function(n) {
18857             return  {
18858                 tag: 'td',
18859                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18860                 cn : [
18861                     {
18862                         cn : [
18863                             {
18864                                 cls: 'fc-day-number',
18865                                 html: 'D'
18866                             },
18867                             {
18868                                 cls: 'fc-day-content',
18869                              
18870                                 cn : [
18871                                      {
18872                                         style: 'position: relative;' // height: 17px;
18873                                     }
18874                                 ]
18875                             }
18876                             
18877                             
18878                         ]
18879                     }
18880                 ]
18881                 
18882             }
18883         };
18884         var cal_rows = function() {
18885             
18886             var ret = [];
18887             for (var r = 0; r < 6; r++) {
18888                 var row= {
18889                     tag : 'tr',
18890                     cls : 'fc-week',
18891                     cn : []
18892                 };
18893                 
18894                 for (var i =0; i < Date.dayNames.length; i++) {
18895                     var d = Date.dayNames[i];
18896                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18897
18898                 }
18899                 row.cn[0].cls+=' fc-first';
18900                 row.cn[0].cn[0].style = 'min-height:90px';
18901                 row.cn[6].cls+=' fc-last';
18902                 ret.push(row);
18903                 
18904             }
18905             ret[0].cls += ' fc-first';
18906             ret[4].cls += ' fc-prev-last';
18907             ret[5].cls += ' fc-last';
18908             return ret;
18909             
18910         };
18911         
18912         var cal_table = {
18913             tag: 'table',
18914             cls: 'fc-border-separate',
18915             style : 'width:100%',
18916             cellspacing  : 0,
18917             cn : [
18918                 { 
18919                     tag: 'thead',
18920                     cn : [
18921                         { 
18922                             tag: 'tr',
18923                             cls : 'fc-first fc-last',
18924                             cn : cal_heads()
18925                         }
18926                     ]
18927                 },
18928                 { 
18929                     tag: 'tbody',
18930                     cn : cal_rows()
18931                 }
18932                   
18933             ]
18934         };
18935          
18936          var cfg = {
18937             cls : 'fc fc-ltr',
18938             cn : [
18939                 header,
18940                 {
18941                     cls : 'fc-content',
18942                     style : "position: relative;",
18943                     cn : [
18944                         {
18945                             cls : 'fc-view fc-view-month fc-grid',
18946                             style : 'position: relative',
18947                             unselectable : 'on',
18948                             cn : [
18949                                 {
18950                                     cls : 'fc-event-container',
18951                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18952                                 },
18953                                 cal_table
18954                             ]
18955                         }
18956                     ]
18957     
18958                 }
18959            ] 
18960             
18961         };
18962         
18963          
18964         
18965         return cfg;
18966     },
18967     
18968     
18969     initEvents : function()
18970     {
18971         if(!this.store){
18972             throw "can not find store for calendar";
18973         }
18974         
18975         var mark = {
18976             tag: "div",
18977             cls:"x-dlg-mask",
18978             style: "text-align:center",
18979             cn: [
18980                 {
18981                     tag: "div",
18982                     style: "background-color:white;width:50%;margin:250 auto",
18983                     cn: [
18984                         {
18985                             tag: "img",
18986                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18987                         },
18988                         {
18989                             tag: "span",
18990                             html: "Loading"
18991                         }
18992                         
18993                     ]
18994                 }
18995             ]
18996         };
18997         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18998         
18999         var size = this.el.select('.fc-content', true).first().getSize();
19000         this.maskEl.setSize(size.width, size.height);
19001         this.maskEl.enableDisplayMode("block");
19002         if(!this.loadMask){
19003             this.maskEl.hide();
19004         }
19005         
19006         this.store = Roo.factory(this.store, Roo.data);
19007         this.store.on('load', this.onLoad, this);
19008         this.store.on('beforeload', this.onBeforeLoad, this);
19009         
19010         this.resize();
19011         
19012         this.cells = this.el.select('.fc-day',true);
19013         //Roo.log(this.cells);
19014         this.textNodes = this.el.query('.fc-day-number');
19015         this.cells.addClassOnOver('fc-state-hover');
19016         
19017         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19018         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19019         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19020         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19021         
19022         this.on('monthchange', this.onMonthChange, this);
19023         
19024         this.update(new Date().clearTime());
19025     },
19026     
19027     resize : function() {
19028         var sz  = this.el.getSize();
19029         
19030         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19031         this.el.select('.fc-day-content div',true).setHeight(34);
19032     },
19033     
19034     
19035     // private
19036     showPrevMonth : function(e){
19037         this.update(this.activeDate.add("mo", -1));
19038     },
19039     showToday : function(e){
19040         this.update(new Date().clearTime());
19041     },
19042     // private
19043     showNextMonth : function(e){
19044         this.update(this.activeDate.add("mo", 1));
19045     },
19046
19047     // private
19048     showPrevYear : function(){
19049         this.update(this.activeDate.add("y", -1));
19050     },
19051
19052     // private
19053     showNextYear : function(){
19054         this.update(this.activeDate.add("y", 1));
19055     },
19056
19057     
19058    // private
19059     update : function(date)
19060     {
19061         var vd = this.activeDate;
19062         this.activeDate = date;
19063 //        if(vd && this.el){
19064 //            var t = date.getTime();
19065 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19066 //                Roo.log('using add remove');
19067 //                
19068 //                this.fireEvent('monthchange', this, date);
19069 //                
19070 //                this.cells.removeClass("fc-state-highlight");
19071 //                this.cells.each(function(c){
19072 //                   if(c.dateValue == t){
19073 //                       c.addClass("fc-state-highlight");
19074 //                       setTimeout(function(){
19075 //                            try{c.dom.firstChild.focus();}catch(e){}
19076 //                       }, 50);
19077 //                       return false;
19078 //                   }
19079 //                   return true;
19080 //                });
19081 //                return;
19082 //            }
19083 //        }
19084         
19085         var days = date.getDaysInMonth();
19086         
19087         var firstOfMonth = date.getFirstDateOfMonth();
19088         var startingPos = firstOfMonth.getDay()-this.startDay;
19089         
19090         if(startingPos < this.startDay){
19091             startingPos += 7;
19092         }
19093         
19094         var pm = date.add(Date.MONTH, -1);
19095         var prevStart = pm.getDaysInMonth()-startingPos;
19096 //        
19097         this.cells = this.el.select('.fc-day',true);
19098         this.textNodes = this.el.query('.fc-day-number');
19099         this.cells.addClassOnOver('fc-state-hover');
19100         
19101         var cells = this.cells.elements;
19102         var textEls = this.textNodes;
19103         
19104         Roo.each(cells, function(cell){
19105             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19106         });
19107         
19108         days += startingPos;
19109
19110         // convert everything to numbers so it's fast
19111         var day = 86400000;
19112         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19113         //Roo.log(d);
19114         //Roo.log(pm);
19115         //Roo.log(prevStart);
19116         
19117         var today = new Date().clearTime().getTime();
19118         var sel = date.clearTime().getTime();
19119         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19120         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19121         var ddMatch = this.disabledDatesRE;
19122         var ddText = this.disabledDatesText;
19123         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19124         var ddaysText = this.disabledDaysText;
19125         var format = this.format;
19126         
19127         var setCellClass = function(cal, cell){
19128             cell.row = 0;
19129             cell.events = [];
19130             cell.more = [];
19131             //Roo.log('set Cell Class');
19132             cell.title = "";
19133             var t = d.getTime();
19134             
19135             //Roo.log(d);
19136             
19137             cell.dateValue = t;
19138             if(t == today){
19139                 cell.className += " fc-today";
19140                 cell.className += " fc-state-highlight";
19141                 cell.title = cal.todayText;
19142             }
19143             if(t == sel){
19144                 // disable highlight in other month..
19145                 //cell.className += " fc-state-highlight";
19146                 
19147             }
19148             // disabling
19149             if(t < min) {
19150                 cell.className = " fc-state-disabled";
19151                 cell.title = cal.minText;
19152                 return;
19153             }
19154             if(t > max) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.maxText;
19157                 return;
19158             }
19159             if(ddays){
19160                 if(ddays.indexOf(d.getDay()) != -1){
19161                     cell.title = ddaysText;
19162                     cell.className = " fc-state-disabled";
19163                 }
19164             }
19165             if(ddMatch && format){
19166                 var fvalue = d.dateFormat(format);
19167                 if(ddMatch.test(fvalue)){
19168                     cell.title = ddText.replace("%0", fvalue);
19169                     cell.className = " fc-state-disabled";
19170                 }
19171             }
19172             
19173             if (!cell.initialClassName) {
19174                 cell.initialClassName = cell.dom.className;
19175             }
19176             
19177             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19178         };
19179
19180         var i = 0;
19181         
19182         for(; i < startingPos; i++) {
19183             textEls[i].innerHTML = (++prevStart);
19184             d.setDate(d.getDate()+1);
19185             
19186             cells[i].className = "fc-past fc-other-month";
19187             setCellClass(this, cells[i]);
19188         }
19189         
19190         var intDay = 0;
19191         
19192         for(; i < days; i++){
19193             intDay = i - startingPos + 1;
19194             textEls[i].innerHTML = (intDay);
19195             d.setDate(d.getDate()+1);
19196             
19197             cells[i].className = ''; // "x-date-active";
19198             setCellClass(this, cells[i]);
19199         }
19200         var extraDays = 0;
19201         
19202         for(; i < 42; i++) {
19203             textEls[i].innerHTML = (++extraDays);
19204             d.setDate(d.getDate()+1);
19205             
19206             cells[i].className = "fc-future fc-other-month";
19207             setCellClass(this, cells[i]);
19208         }
19209         
19210         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19211         
19212         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19213         
19214         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19215         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19216         
19217         if(totalRows != 6){
19218             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19219             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19220         }
19221         
19222         this.fireEvent('monthchange', this, date);
19223         
19224         
19225         /*
19226         if(!this.internalRender){
19227             var main = this.el.dom.firstChild;
19228             var w = main.offsetWidth;
19229             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19230             Roo.fly(main).setWidth(w);
19231             this.internalRender = true;
19232             // opera does not respect the auto grow header center column
19233             // then, after it gets a width opera refuses to recalculate
19234             // without a second pass
19235             if(Roo.isOpera && !this.secondPass){
19236                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19237                 this.secondPass = true;
19238                 this.update.defer(10, this, [date]);
19239             }
19240         }
19241         */
19242         
19243     },
19244     
19245     findCell : function(dt) {
19246         dt = dt.clearTime().getTime();
19247         var ret = false;
19248         this.cells.each(function(c){
19249             //Roo.log("check " +c.dateValue + '?=' + dt);
19250             if(c.dateValue == dt){
19251                 ret = c;
19252                 return false;
19253             }
19254             return true;
19255         });
19256         
19257         return ret;
19258     },
19259     
19260     findCells : function(ev) {
19261         var s = ev.start.clone().clearTime().getTime();
19262        // Roo.log(s);
19263         var e= ev.end.clone().clearTime().getTime();
19264        // Roo.log(e);
19265         var ret = [];
19266         this.cells.each(function(c){
19267              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19268             
19269             if(c.dateValue > e){
19270                 return ;
19271             }
19272             if(c.dateValue < s){
19273                 return ;
19274             }
19275             ret.push(c);
19276         });
19277         
19278         return ret;    
19279     },
19280     
19281 //    findBestRow: function(cells)
19282 //    {
19283 //        var ret = 0;
19284 //        
19285 //        for (var i =0 ; i < cells.length;i++) {
19286 //            ret  = Math.max(cells[i].rows || 0,ret);
19287 //        }
19288 //        return ret;
19289 //        
19290 //    },
19291     
19292     
19293     addItem : function(ev)
19294     {
19295         // look for vertical location slot in
19296         var cells = this.findCells(ev);
19297         
19298 //        ev.row = this.findBestRow(cells);
19299         
19300         // work out the location.
19301         
19302         var crow = false;
19303         var rows = [];
19304         for(var i =0; i < cells.length; i++) {
19305             
19306             cells[i].row = cells[0].row;
19307             
19308             if(i == 0){
19309                 cells[i].row = cells[i].row + 1;
19310             }
19311             
19312             if (!crow) {
19313                 crow = {
19314                     start : cells[i],
19315                     end :  cells[i]
19316                 };
19317                 continue;
19318             }
19319             if (crow.start.getY() == cells[i].getY()) {
19320                 // on same row.
19321                 crow.end = cells[i];
19322                 continue;
19323             }
19324             // different row.
19325             rows.push(crow);
19326             crow = {
19327                 start: cells[i],
19328                 end : cells[i]
19329             };
19330             
19331         }
19332         
19333         rows.push(crow);
19334         ev.els = [];
19335         ev.rows = rows;
19336         ev.cells = cells;
19337         
19338         cells[0].events.push(ev);
19339         
19340         this.calevents.push(ev);
19341     },
19342     
19343     clearEvents: function() {
19344         
19345         if(!this.calevents){
19346             return;
19347         }
19348         
19349         Roo.each(this.cells.elements, function(c){
19350             c.row = 0;
19351             c.events = [];
19352             c.more = [];
19353         });
19354         
19355         Roo.each(this.calevents, function(e) {
19356             Roo.each(e.els, function(el) {
19357                 el.un('mouseenter' ,this.onEventEnter, this);
19358                 el.un('mouseleave' ,this.onEventLeave, this);
19359                 el.remove();
19360             },this);
19361         },this);
19362         
19363         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19364             e.remove();
19365         });
19366         
19367     },
19368     
19369     renderEvents: function()
19370     {   
19371         var _this = this;
19372         
19373         this.cells.each(function(c) {
19374             
19375             if(c.row < 5){
19376                 return;
19377             }
19378             
19379             var ev = c.events;
19380             
19381             var r = 4;
19382             if(c.row != c.events.length){
19383                 r = 4 - (4 - (c.row - c.events.length));
19384             }
19385             
19386             c.events = ev.slice(0, r);
19387             c.more = ev.slice(r);
19388             
19389             if(c.more.length && c.more.length == 1){
19390                 c.events.push(c.more.pop());
19391             }
19392             
19393             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19394             
19395         });
19396             
19397         this.cells.each(function(c) {
19398             
19399             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19400             
19401             
19402             for (var e = 0; e < c.events.length; e++){
19403                 var ev = c.events[e];
19404                 var rows = ev.rows;
19405                 
19406                 for(var i = 0; i < rows.length; i++) {
19407                 
19408                     // how many rows should it span..
19409
19410                     var  cfg = {
19411                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19412                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19413
19414                         unselectable : "on",
19415                         cn : [
19416                             {
19417                                 cls: 'fc-event-inner',
19418                                 cn : [
19419     //                                {
19420     //                                  tag:'span',
19421     //                                  cls: 'fc-event-time',
19422     //                                  html : cells.length > 1 ? '' : ev.time
19423     //                                },
19424                                     {
19425                                       tag:'span',
19426                                       cls: 'fc-event-title',
19427                                       html : String.format('{0}', ev.title)
19428                                     }
19429
19430
19431                                 ]
19432                             },
19433                             {
19434                                 cls: 'ui-resizable-handle ui-resizable-e',
19435                                 html : '&nbsp;&nbsp;&nbsp'
19436                             }
19437
19438                         ]
19439                     };
19440
19441                     if (i == 0) {
19442                         cfg.cls += ' fc-event-start';
19443                     }
19444                     if ((i+1) == rows.length) {
19445                         cfg.cls += ' fc-event-end';
19446                     }
19447
19448                     var ctr = _this.el.select('.fc-event-container',true).first();
19449                     var cg = ctr.createChild(cfg);
19450
19451                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19452                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19453
19454                     var r = (c.more.length) ? 1 : 0;
19455                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19456                     cg.setWidth(ebox.right - sbox.x -2);
19457
19458                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19459                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19460                     cg.on('click', _this.onEventClick, _this, ev);
19461
19462                     ev.els.push(cg);
19463                     
19464                 }
19465                 
19466             }
19467             
19468             
19469             if(c.more.length){
19470                 var  cfg = {
19471                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19472                     style : 'position: absolute',
19473                     unselectable : "on",
19474                     cn : [
19475                         {
19476                             cls: 'fc-event-inner',
19477                             cn : [
19478                                 {
19479                                   tag:'span',
19480                                   cls: 'fc-event-title',
19481                                   html : 'More'
19482                                 }
19483
19484
19485                             ]
19486                         },
19487                         {
19488                             cls: 'ui-resizable-handle ui-resizable-e',
19489                             html : '&nbsp;&nbsp;&nbsp'
19490                         }
19491
19492                     ]
19493                 };
19494
19495                 var ctr = _this.el.select('.fc-event-container',true).first();
19496                 var cg = ctr.createChild(cfg);
19497
19498                 var sbox = c.select('.fc-day-content',true).first().getBox();
19499                 var ebox = c.select('.fc-day-content',true).first().getBox();
19500                 //Roo.log(cg);
19501                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19502                 cg.setWidth(ebox.right - sbox.x -2);
19503
19504                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19505                 
19506             }
19507             
19508         });
19509         
19510         
19511         
19512     },
19513     
19514     onEventEnter: function (e, el,event,d) {
19515         this.fireEvent('evententer', this, el, event);
19516     },
19517     
19518     onEventLeave: function (e, el,event,d) {
19519         this.fireEvent('eventleave', this, el, event);
19520     },
19521     
19522     onEventClick: function (e, el,event,d) {
19523         this.fireEvent('eventclick', this, el, event);
19524     },
19525     
19526     onMonthChange: function () {
19527         this.store.load();
19528     },
19529     
19530     onMoreEventClick: function(e, el, more)
19531     {
19532         var _this = this;
19533         
19534         this.calpopover.placement = 'right';
19535         this.calpopover.setTitle('More');
19536         
19537         this.calpopover.setContent('');
19538         
19539         var ctr = this.calpopover.el.select('.popover-content', true).first();
19540         
19541         Roo.each(more, function(m){
19542             var cfg = {
19543                 cls : 'fc-event-hori fc-event-draggable',
19544                 html : m.title
19545             };
19546             var cg = ctr.createChild(cfg);
19547             
19548             cg.on('click', _this.onEventClick, _this, m);
19549         });
19550         
19551         this.calpopover.show(el);
19552         
19553         
19554     },
19555     
19556     onLoad: function () 
19557     {   
19558         this.calevents = [];
19559         var cal = this;
19560         
19561         if(this.store.getCount() > 0){
19562             this.store.data.each(function(d){
19563                cal.addItem({
19564                     id : d.data.id,
19565                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19566                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19567                     time : d.data.start_time,
19568                     title : d.data.title,
19569                     description : d.data.description,
19570                     venue : d.data.venue
19571                 });
19572             });
19573         }
19574         
19575         this.renderEvents();
19576         
19577         if(this.calevents.length && this.loadMask){
19578             this.maskEl.hide();
19579         }
19580     },
19581     
19582     onBeforeLoad: function()
19583     {
19584         this.clearEvents();
19585         if(this.loadMask){
19586             this.maskEl.show();
19587         }
19588     }
19589 });
19590
19591  
19592  /*
19593  * - LGPL
19594  *
19595  * element
19596  * 
19597  */
19598
19599 /**
19600  * @class Roo.bootstrap.Popover
19601  * @extends Roo.bootstrap.Component
19602  * Bootstrap Popover class
19603  * @cfg {String} html contents of the popover   (or false to use children..)
19604  * @cfg {String} title of popover (or false to hide)
19605  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19606  * @cfg {String} trigger click || hover (or false to trigger manually)
19607  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19608  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19609  *      - if false and it has a 'parent' then it will be automatically added to that element
19610  *      - if string - Roo.get  will be called 
19611  * @cfg {Number} delay - delay before showing
19612  
19613  * @constructor
19614  * Create a new Popover
19615  * @param {Object} config The config object
19616  */
19617
19618 Roo.bootstrap.Popover = function(config){
19619     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19620     
19621     this.addEvents({
19622         // raw events
19623          /**
19624          * @event show
19625          * After the popover show
19626          * 
19627          * @param {Roo.bootstrap.Popover} this
19628          */
19629         "show" : true,
19630         /**
19631          * @event hide
19632          * After the popover hide
19633          * 
19634          * @param {Roo.bootstrap.Popover} this
19635          */
19636         "hide" : true
19637     });
19638 };
19639
19640 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19641     
19642     title: false,
19643     html: false,
19644     
19645     placement : 'right',
19646     trigger : 'hover', // hover
19647     modal : false,
19648     delay : 0,
19649     
19650     over: false,
19651     
19652     can_build_overlaid : false,
19653     
19654     maskEl : false, // the mask element
19655     
19656     getChildContainer : function()
19657     {
19658         return this.el.select('.popover-content',true).first();
19659     },
19660     
19661     getAutoCreate : function(){
19662          
19663         var cfg = {
19664            cls : 'popover roo-dynamic shadow roo-popover',
19665            style: 'display:block',
19666            cn : [
19667                 {
19668                     cls : 'arrow'
19669                 },
19670                 {
19671                     cls : 'popover-inner ',
19672                     cn : [
19673                         {
19674                             tag: 'h3',
19675                             cls: 'popover-title popover-header',
19676                             html : this.title || ''
19677                         },
19678                         {
19679                             cls : 'popover-content popover-body'  + this.cls,
19680                             html : this.html || ''
19681                         }
19682                     ]
19683                     
19684                 }
19685            ]
19686         };
19687         
19688         return cfg;
19689     },
19690     /**
19691      * @param {string} the title
19692      */
19693     setTitle: function(str)
19694     {
19695         this.title = str;
19696         if (this.el) {
19697             this.el.select('.popover-title',true).first().dom.innerHTML = str;
19698         }
19699         
19700     },
19701     /**
19702      * @param {string} the body content
19703      */
19704     setContent: function(str)
19705     {
19706         this.html = str;
19707         if (this.el) {
19708             this.el.select('.popover-content',true).first().dom.innerHTML = str;
19709         }
19710         
19711     },
19712     // as it get's added to the bottom of the page.
19713     onRender : function(ct, position)
19714     {
19715         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19716         if(!this.el){
19717             var cfg = Roo.apply({},  this.getAutoCreate());
19718             cfg.id = Roo.id();
19719             
19720             if (this.cls) {
19721                 cfg.cls += ' ' + this.cls;
19722             }
19723             if (this.style) {
19724                 cfg.style = this.style;
19725             }
19726             //Roo.log("adding to ");
19727             this.el = Roo.get(document.body).createChild(cfg, position);
19728 //            Roo.log(this.el);
19729         }
19730         
19731         var nitems = [];
19732         if(typeof(this.items) != 'undefined'){
19733             var items = this.items;
19734             delete this.items;
19735
19736             for(var i =0;i < items.length;i++) {
19737                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19738             }
19739         }
19740
19741         this.items = nitems;
19742         
19743         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19744         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19745         
19746         
19747         this.initEvents();
19748     },
19749     
19750     resizeMask : function()
19751     {
19752         
19753     }
19754     
19755     initEvents : function()
19756     {
19757         
19758         Roo.bootstrap.Popover.register(this);
19759         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19760         this.el.enableDisplayMode('block');
19761         this.el.hide();
19762         if (this.over === false && !this.parent()) {
19763             return; 
19764         }
19765         if (this.triggers === false) {
19766             return;
19767         }
19768          
19769         // support parent
19770         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19771         var triggers = this.trigger ? this.trigger.split(' ') : [];
19772         Roo.each(triggers, function(trigger) {
19773         
19774             if (trigger == 'click') {
19775                 on_el.on('click', this.toggle, this);
19776             } else if (trigger != 'manual') {
19777                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19778                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19779       
19780                 on_el.on(eventIn  ,this.enter, this);
19781                 on_el.on(eventOut, this.leave, this);
19782             }
19783         }, this);
19784         
19785     },
19786     
19787     
19788     // private
19789     timeout : null,
19790     hoverState : null,
19791     
19792     toggle : function () {
19793         this.hoverState == 'in' ? this.leave() : this.enter();
19794     },
19795     
19796     enter : function () {
19797         
19798         clearTimeout(this.timeout);
19799     
19800         this.hoverState = 'in';
19801     
19802         if (!this.delay || !this.delay.show) {
19803             this.show();
19804             return;
19805         }
19806         var _t = this;
19807         this.timeout = setTimeout(function () {
19808             if (_t.hoverState == 'in') {
19809                 _t.show();
19810             }
19811         }, this.delay.show)
19812     },
19813     
19814     leave : function() {
19815         clearTimeout(this.timeout);
19816     
19817         this.hoverState = 'out';
19818     
19819         if (!this.delay || !this.delay.hide) {
19820             this.hide();
19821             return;
19822         }
19823         var _t = this;
19824         this.timeout = setTimeout(function () {
19825             if (_t.hoverState == 'out') {
19826                 _t.hide();
19827             }
19828         }, this.delay.hide)
19829     },
19830     /**
19831      * Show the popover
19832      * @param {Roo.Element|string|false} - element to align and point to.
19833      */
19834     show : function (on_el)
19835     {
19836         
19837         on_el = on_el || false; // default to false
19838         if (!on_el) {
19839             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19840                 on_el = this.parent().el;
19841             } else if (this.over) {
19842                 Roo.get(this.over);
19843             }
19844             
19845         }
19846         
19847         if (!this.el) {
19848             this.render(document.body);
19849         }
19850         
19851         
19852         this.el.removeClass([
19853             'fade','top','bottom', 'left', 'right','in',
19854             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19855         ]);
19856         
19857         if (!this.title.length) {
19858             this.el.select('.popover-title',true).hide();
19859         }
19860         
19861         
19862         var placement = typeof this.placement == 'function' ?
19863             this.placement.call(this, this.el, on_el) :
19864             this.placement;
19865             
19866         /*
19867         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19868         
19869         // I think  'auto right' - but 
19870         
19871         var autoPlace = autoToken.test(placement);
19872         if (autoPlace) {
19873             placement = placement.replace(autoToken, '') || 'top';
19874         }
19875         */
19876         
19877         
19878         this.el.show();
19879         this.el.dom.style.display='block';
19880         
19881         //this.el.appendTo(on_el);
19882         
19883         var p = this.getPosition();
19884         var box = this.el.getBox();
19885         
19886         
19887         var align = Roo.bootstrap.Popover.alignment[placement];
19888         this.el.addClass(align[2]);
19889
19890 //        Roo.log(align);
19891
19892         if (on_el) {
19893             this.el.alignTo(on_el, align[0],align[1]);
19894         } else {
19895             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19896             var es = this.el.getSize();
19897             var x = Roo.lib.Dom.getViewWidth()/2;
19898             var y = Roo.lib.Dom.getViewHeight()/2;
19899             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19900             
19901         }
19902
19903         
19904         //var arrow = this.el.select('.arrow',true).first();
19905         //arrow.set(align[2], 
19906         
19907         this.el.addClass('in');
19908         
19909         
19910         if (this.el.hasClass('fade')) {
19911             // fade it?
19912         }
19913         
19914         this.hoverState = 'in';
19915         
19916         this.fireEvent('show', this);
19917         
19918     },
19919     hide : function()
19920     {
19921         this.el.setXY([0,0]);
19922         this.el.removeClass('in');
19923         this.el.hide();
19924         this.hoverState = null;
19925         
19926         this.fireEvent('hide', this);
19927     }
19928     
19929 });
19930
19931
19932 Roo.apply(Roo.bootstrap.Popover, {
19933
19934     alignment : {
19935         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19936         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19937         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19938         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19939     },
19940
19941     clickHander : false,
19942     
19943
19944     onMouseDown : function(e)
19945     {
19946         if (!e.getTarget(".roo-popover")) {
19947             this.hideAll();
19948         }
19949          
19950     },
19951     
19952     popups : [],
19953     
19954     register : function(popup)
19955     {
19956         if (!Roo.bootstrap.Popover.clickHandler) {
19957             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19958         }
19959         // hide other popups.
19960         this.hideAll();
19961         this.popups.push(popup);
19962     },
19963     hideAll : function()
19964     {
19965         this.popups.forEach(function(p) {
19966             p.hide();
19967         });
19968     }
19969
19970 });/*
19971  * - LGPL
19972  *
19973  * Progress
19974  * 
19975  */
19976
19977 /**
19978  * @class Roo.bootstrap.Progress
19979  * @extends Roo.bootstrap.Component
19980  * Bootstrap Progress class
19981  * @cfg {Boolean} striped striped of the progress bar
19982  * @cfg {Boolean} active animated of the progress bar
19983  * 
19984  * 
19985  * @constructor
19986  * Create a new Progress
19987  * @param {Object} config The config object
19988  */
19989
19990 Roo.bootstrap.Progress = function(config){
19991     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19992 };
19993
19994 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19995     
19996     striped : false,
19997     active: false,
19998     
19999     getAutoCreate : function(){
20000         var cfg = {
20001             tag: 'div',
20002             cls: 'progress'
20003         };
20004         
20005         
20006         if(this.striped){
20007             cfg.cls += ' progress-striped';
20008         }
20009       
20010         if(this.active){
20011             cfg.cls += ' active';
20012         }
20013         
20014         
20015         return cfg;
20016     }
20017    
20018 });
20019
20020  
20021
20022  /*
20023  * - LGPL
20024  *
20025  * ProgressBar
20026  * 
20027  */
20028
20029 /**
20030  * @class Roo.bootstrap.ProgressBar
20031  * @extends Roo.bootstrap.Component
20032  * Bootstrap ProgressBar class
20033  * @cfg {Number} aria_valuenow aria-value now
20034  * @cfg {Number} aria_valuemin aria-value min
20035  * @cfg {Number} aria_valuemax aria-value max
20036  * @cfg {String} label label for the progress bar
20037  * @cfg {String} panel (success | info | warning | danger )
20038  * @cfg {String} role role of the progress bar
20039  * @cfg {String} sr_only text
20040  * 
20041  * 
20042  * @constructor
20043  * Create a new ProgressBar
20044  * @param {Object} config The config object
20045  */
20046
20047 Roo.bootstrap.ProgressBar = function(config){
20048     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20049 };
20050
20051 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20052     
20053     aria_valuenow : 0,
20054     aria_valuemin : 0,
20055     aria_valuemax : 100,
20056     label : false,
20057     panel : false,
20058     role : false,
20059     sr_only: false,
20060     
20061     getAutoCreate : function()
20062     {
20063         
20064         var cfg = {
20065             tag: 'div',
20066             cls: 'progress-bar',
20067             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20068         };
20069         
20070         if(this.sr_only){
20071             cfg.cn = {
20072                 tag: 'span',
20073                 cls: 'sr-only',
20074                 html: this.sr_only
20075             }
20076         }
20077         
20078         if(this.role){
20079             cfg.role = this.role;
20080         }
20081         
20082         if(this.aria_valuenow){
20083             cfg['aria-valuenow'] = this.aria_valuenow;
20084         }
20085         
20086         if(this.aria_valuemin){
20087             cfg['aria-valuemin'] = this.aria_valuemin;
20088         }
20089         
20090         if(this.aria_valuemax){
20091             cfg['aria-valuemax'] = this.aria_valuemax;
20092         }
20093         
20094         if(this.label && !this.sr_only){
20095             cfg.html = this.label;
20096         }
20097         
20098         if(this.panel){
20099             cfg.cls += ' progress-bar-' + this.panel;
20100         }
20101         
20102         return cfg;
20103     },
20104     
20105     update : function(aria_valuenow)
20106     {
20107         this.aria_valuenow = aria_valuenow;
20108         
20109         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20110     }
20111    
20112 });
20113
20114  
20115
20116  /*
20117  * - LGPL
20118  *
20119  * column
20120  * 
20121  */
20122
20123 /**
20124  * @class Roo.bootstrap.TabGroup
20125  * @extends Roo.bootstrap.Column
20126  * Bootstrap Column class
20127  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20128  * @cfg {Boolean} carousel true to make the group behave like a carousel
20129  * @cfg {Boolean} bullets show bullets for the panels
20130  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20131  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20132  * @cfg {Boolean} showarrow (true|false) show arrow default true
20133  * 
20134  * @constructor
20135  * Create a new TabGroup
20136  * @param {Object} config The config object
20137  */
20138
20139 Roo.bootstrap.TabGroup = function(config){
20140     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20141     if (!this.navId) {
20142         this.navId = Roo.id();
20143     }
20144     this.tabs = [];
20145     Roo.bootstrap.TabGroup.register(this);
20146     
20147 };
20148
20149 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20150     
20151     carousel : false,
20152     transition : false,
20153     bullets : 0,
20154     timer : 0,
20155     autoslide : false,
20156     slideFn : false,
20157     slideOnTouch : false,
20158     showarrow : true,
20159     
20160     getAutoCreate : function()
20161     {
20162         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20163         
20164         cfg.cls += ' tab-content';
20165         
20166         if (this.carousel) {
20167             cfg.cls += ' carousel slide';
20168             
20169             cfg.cn = [{
20170                cls : 'carousel-inner',
20171                cn : []
20172             }];
20173         
20174             if(this.bullets  && !Roo.isTouch){
20175                 
20176                 var bullets = {
20177                     cls : 'carousel-bullets',
20178                     cn : []
20179                 };
20180                
20181                 if(this.bullets_cls){
20182                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20183                 }
20184                 
20185                 bullets.cn.push({
20186                     cls : 'clear'
20187                 });
20188                 
20189                 cfg.cn[0].cn.push(bullets);
20190             }
20191             
20192             if(this.showarrow){
20193                 cfg.cn[0].cn.push({
20194                     tag : 'div',
20195                     class : 'carousel-arrow',
20196                     cn : [
20197                         {
20198                             tag : 'div',
20199                             class : 'carousel-prev',
20200                             cn : [
20201                                 {
20202                                     tag : 'i',
20203                                     class : 'fa fa-chevron-left'
20204                                 }
20205                             ]
20206                         },
20207                         {
20208                             tag : 'div',
20209                             class : 'carousel-next',
20210                             cn : [
20211                                 {
20212                                     tag : 'i',
20213                                     class : 'fa fa-chevron-right'
20214                                 }
20215                             ]
20216                         }
20217                     ]
20218                 });
20219             }
20220             
20221         }
20222         
20223         return cfg;
20224     },
20225     
20226     initEvents:  function()
20227     {
20228 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20229 //            this.el.on("touchstart", this.onTouchStart, this);
20230 //        }
20231         
20232         if(this.autoslide){
20233             var _this = this;
20234             
20235             this.slideFn = window.setInterval(function() {
20236                 _this.showPanelNext();
20237             }, this.timer);
20238         }
20239         
20240         if(this.showarrow){
20241             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20242             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20243         }
20244         
20245         
20246     },
20247     
20248 //    onTouchStart : function(e, el, o)
20249 //    {
20250 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20251 //            return;
20252 //        }
20253 //        
20254 //        this.showPanelNext();
20255 //    },
20256     
20257     
20258     getChildContainer : function()
20259     {
20260         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20261     },
20262     
20263     /**
20264     * register a Navigation item
20265     * @param {Roo.bootstrap.NavItem} the navitem to add
20266     */
20267     register : function(item)
20268     {
20269         this.tabs.push( item);
20270         item.navId = this.navId; // not really needed..
20271         this.addBullet();
20272     
20273     },
20274     
20275     getActivePanel : function()
20276     {
20277         var r = false;
20278         Roo.each(this.tabs, function(t) {
20279             if (t.active) {
20280                 r = t;
20281                 return false;
20282             }
20283             return null;
20284         });
20285         return r;
20286         
20287     },
20288     getPanelByName : function(n)
20289     {
20290         var r = false;
20291         Roo.each(this.tabs, function(t) {
20292             if (t.tabId == n) {
20293                 r = t;
20294                 return false;
20295             }
20296             return null;
20297         });
20298         return r;
20299     },
20300     indexOfPanel : function(p)
20301     {
20302         var r = false;
20303         Roo.each(this.tabs, function(t,i) {
20304             if (t.tabId == p.tabId) {
20305                 r = i;
20306                 return false;
20307             }
20308             return null;
20309         });
20310         return r;
20311     },
20312     /**
20313      * show a specific panel
20314      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20315      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20316      */
20317     showPanel : function (pan)
20318     {
20319         if(this.transition || typeof(pan) == 'undefined'){
20320             Roo.log("waiting for the transitionend");
20321             return false;
20322         }
20323         
20324         if (typeof(pan) == 'number') {
20325             pan = this.tabs[pan];
20326         }
20327         
20328         if (typeof(pan) == 'string') {
20329             pan = this.getPanelByName(pan);
20330         }
20331         
20332         var cur = this.getActivePanel();
20333         
20334         if(!pan || !cur){
20335             Roo.log('pan or acitve pan is undefined');
20336             return false;
20337         }
20338         
20339         if (pan.tabId == this.getActivePanel().tabId) {
20340             return true;
20341         }
20342         
20343         if (false === cur.fireEvent('beforedeactivate')) {
20344             return false;
20345         }
20346         
20347         if(this.bullets > 0 && !Roo.isTouch){
20348             this.setActiveBullet(this.indexOfPanel(pan));
20349         }
20350         
20351         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20352             
20353             //class="carousel-item carousel-item-next carousel-item-left"
20354             
20355             this.transition = true;
20356             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20357             var lr = dir == 'next' ? 'left' : 'right';
20358             pan.el.addClass(dir); // or prev
20359             pan.el.addClass('carousel-item-' + dir); // or prev
20360             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20361             cur.el.addClass(lr); // or right
20362             pan.el.addClass(lr);
20363             cur.el.addClass('carousel-item-' +lr); // or right
20364             pan.el.addClass('carousel-item-' +lr);
20365             
20366             
20367             var _this = this;
20368             cur.el.on('transitionend', function() {
20369                 Roo.log("trans end?");
20370                 
20371                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20372                 pan.setActive(true);
20373                 
20374                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20375                 cur.setActive(false);
20376                 
20377                 _this.transition = false;
20378                 
20379             }, this, { single:  true } );
20380             
20381             return true;
20382         }
20383         
20384         cur.setActive(false);
20385         pan.setActive(true);
20386         
20387         return true;
20388         
20389     },
20390     showPanelNext : function()
20391     {
20392         var i = this.indexOfPanel(this.getActivePanel());
20393         
20394         if (i >= this.tabs.length - 1 && !this.autoslide) {
20395             return;
20396         }
20397         
20398         if (i >= this.tabs.length - 1 && this.autoslide) {
20399             i = -1;
20400         }
20401         
20402         this.showPanel(this.tabs[i+1]);
20403     },
20404     
20405     showPanelPrev : function()
20406     {
20407         var i = this.indexOfPanel(this.getActivePanel());
20408         
20409         if (i  < 1 && !this.autoslide) {
20410             return;
20411         }
20412         
20413         if (i < 1 && this.autoslide) {
20414             i = this.tabs.length;
20415         }
20416         
20417         this.showPanel(this.tabs[i-1]);
20418     },
20419     
20420     
20421     addBullet: function()
20422     {
20423         if(!this.bullets || Roo.isTouch){
20424             return;
20425         }
20426         var ctr = this.el.select('.carousel-bullets',true).first();
20427         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20428         var bullet = ctr.createChild({
20429             cls : 'bullet bullet-' + i
20430         },ctr.dom.lastChild);
20431         
20432         
20433         var _this = this;
20434         
20435         bullet.on('click', (function(e, el, o, ii, t){
20436
20437             e.preventDefault();
20438
20439             this.showPanel(ii);
20440
20441             if(this.autoslide && this.slideFn){
20442                 clearInterval(this.slideFn);
20443                 this.slideFn = window.setInterval(function() {
20444                     _this.showPanelNext();
20445                 }, this.timer);
20446             }
20447
20448         }).createDelegate(this, [i, bullet], true));
20449                 
20450         
20451     },
20452      
20453     setActiveBullet : function(i)
20454     {
20455         if(Roo.isTouch){
20456             return;
20457         }
20458         
20459         Roo.each(this.el.select('.bullet', true).elements, function(el){
20460             el.removeClass('selected');
20461         });
20462
20463         var bullet = this.el.select('.bullet-' + i, true).first();
20464         
20465         if(!bullet){
20466             return;
20467         }
20468         
20469         bullet.addClass('selected');
20470     }
20471     
20472     
20473   
20474 });
20475
20476  
20477
20478  
20479  
20480 Roo.apply(Roo.bootstrap.TabGroup, {
20481     
20482     groups: {},
20483      /**
20484     * register a Navigation Group
20485     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20486     */
20487     register : function(navgrp)
20488     {
20489         this.groups[navgrp.navId] = navgrp;
20490         
20491     },
20492     /**
20493     * fetch a Navigation Group based on the navigation ID
20494     * if one does not exist , it will get created.
20495     * @param {string} the navgroup to add
20496     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20497     */
20498     get: function(navId) {
20499         if (typeof(this.groups[navId]) == 'undefined') {
20500             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20501         }
20502         return this.groups[navId] ;
20503     }
20504     
20505     
20506     
20507 });
20508
20509  /*
20510  * - LGPL
20511  *
20512  * TabPanel
20513  * 
20514  */
20515
20516 /**
20517  * @class Roo.bootstrap.TabPanel
20518  * @extends Roo.bootstrap.Component
20519  * Bootstrap TabPanel class
20520  * @cfg {Boolean} active panel active
20521  * @cfg {String} html panel content
20522  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20523  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20524  * @cfg {String} href click to link..
20525  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20526  * 
20527  * 
20528  * @constructor
20529  * Create a new TabPanel
20530  * @param {Object} config The config object
20531  */
20532
20533 Roo.bootstrap.TabPanel = function(config){
20534     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20535     this.addEvents({
20536         /**
20537              * @event changed
20538              * Fires when the active status changes
20539              * @param {Roo.bootstrap.TabPanel} this
20540              * @param {Boolean} state the new state
20541             
20542          */
20543         'changed': true,
20544         /**
20545              * @event beforedeactivate
20546              * Fires before a tab is de-activated - can be used to do validation on a form.
20547              * @param {Roo.bootstrap.TabPanel} this
20548              * @return {Boolean} false if there is an error
20549             
20550          */
20551         'beforedeactivate': true
20552      });
20553     
20554     this.tabId = this.tabId || Roo.id();
20555   
20556 };
20557
20558 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20559     
20560     active: false,
20561     html: false,
20562     tabId: false,
20563     navId : false,
20564     href : '',
20565     touchSlide : false,
20566     getAutoCreate : function(){
20567         
20568         
20569         var cfg = {
20570             tag: 'div',
20571             // item is needed for carousel - not sure if it has any effect otherwise
20572             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20573             html: this.html || ''
20574         };
20575         
20576         if(this.active){
20577             cfg.cls += ' active';
20578         }
20579         
20580         if(this.tabId){
20581             cfg.tabId = this.tabId;
20582         }
20583         
20584         
20585         
20586         return cfg;
20587     },
20588     
20589     initEvents:  function()
20590     {
20591         var p = this.parent();
20592         
20593         this.navId = this.navId || p.navId;
20594         
20595         if (typeof(this.navId) != 'undefined') {
20596             // not really needed.. but just in case.. parent should be a NavGroup.
20597             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20598             
20599             tg.register(this);
20600             
20601             var i = tg.tabs.length - 1;
20602             
20603             if(this.active && tg.bullets > 0 && i < tg.bullets){
20604                 tg.setActiveBullet(i);
20605             }
20606         }
20607         
20608         this.el.on('click', this.onClick, this);
20609         
20610         if(Roo.isTouch && this.touchSlide){
20611             this.el.on("touchstart", this.onTouchStart, this);
20612             this.el.on("touchmove", this.onTouchMove, this);
20613             this.el.on("touchend", this.onTouchEnd, this);
20614         }
20615         
20616     },
20617     
20618     onRender : function(ct, position)
20619     {
20620         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20621     },
20622     
20623     setActive : function(state)
20624     {
20625         Roo.log("panel - set active " + this.tabId + "=" + state);
20626         
20627         this.active = state;
20628         if (!state) {
20629             this.el.removeClass('active');
20630             
20631         } else  if (!this.el.hasClass('active')) {
20632             this.el.addClass('active');
20633         }
20634         
20635         this.fireEvent('changed', this, state);
20636     },
20637     
20638     onClick : function(e)
20639     {
20640         e.preventDefault();
20641         
20642         if(!this.href.length){
20643             return;
20644         }
20645         
20646         window.location.href = this.href;
20647     },
20648     
20649     startX : 0,
20650     startY : 0,
20651     endX : 0,
20652     endY : 0,
20653     swiping : false,
20654     
20655     onTouchStart : function(e)
20656     {
20657         this.swiping = false;
20658         
20659         this.startX = e.browserEvent.touches[0].clientX;
20660         this.startY = e.browserEvent.touches[0].clientY;
20661     },
20662     
20663     onTouchMove : function(e)
20664     {
20665         this.swiping = true;
20666         
20667         this.endX = e.browserEvent.touches[0].clientX;
20668         this.endY = e.browserEvent.touches[0].clientY;
20669     },
20670     
20671     onTouchEnd : function(e)
20672     {
20673         if(!this.swiping){
20674             this.onClick(e);
20675             return;
20676         }
20677         
20678         var tabGroup = this.parent();
20679         
20680         if(this.endX > this.startX){ // swiping right
20681             tabGroup.showPanelPrev();
20682             return;
20683         }
20684         
20685         if(this.startX > this.endX){ // swiping left
20686             tabGroup.showPanelNext();
20687             return;
20688         }
20689     }
20690     
20691     
20692 });
20693  
20694
20695  
20696
20697  /*
20698  * - LGPL
20699  *
20700  * DateField
20701  * 
20702  */
20703
20704 /**
20705  * @class Roo.bootstrap.DateField
20706  * @extends Roo.bootstrap.Input
20707  * Bootstrap DateField class
20708  * @cfg {Number} weekStart default 0
20709  * @cfg {String} viewMode default empty, (months|years)
20710  * @cfg {String} minViewMode default empty, (months|years)
20711  * @cfg {Number} startDate default -Infinity
20712  * @cfg {Number} endDate default Infinity
20713  * @cfg {Boolean} todayHighlight default false
20714  * @cfg {Boolean} todayBtn default false
20715  * @cfg {Boolean} calendarWeeks default false
20716  * @cfg {Object} daysOfWeekDisabled default empty
20717  * @cfg {Boolean} singleMode default false (true | false)
20718  * 
20719  * @cfg {Boolean} keyboardNavigation default true
20720  * @cfg {String} language default en
20721  * 
20722  * @constructor
20723  * Create a new DateField
20724  * @param {Object} config The config object
20725  */
20726
20727 Roo.bootstrap.DateField = function(config){
20728     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20729      this.addEvents({
20730             /**
20731              * @event show
20732              * Fires when this field show.
20733              * @param {Roo.bootstrap.DateField} this
20734              * @param {Mixed} date The date value
20735              */
20736             show : true,
20737             /**
20738              * @event show
20739              * Fires when this field hide.
20740              * @param {Roo.bootstrap.DateField} this
20741              * @param {Mixed} date The date value
20742              */
20743             hide : true,
20744             /**
20745              * @event select
20746              * Fires when select a date.
20747              * @param {Roo.bootstrap.DateField} this
20748              * @param {Mixed} date The date value
20749              */
20750             select : true,
20751             /**
20752              * @event beforeselect
20753              * Fires when before select a date.
20754              * @param {Roo.bootstrap.DateField} this
20755              * @param {Mixed} date The date value
20756              */
20757             beforeselect : true
20758         });
20759 };
20760
20761 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20762     
20763     /**
20764      * @cfg {String} format
20765      * The default date format string which can be overriden for localization support.  The format must be
20766      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20767      */
20768     format : "m/d/y",
20769     /**
20770      * @cfg {String} altFormats
20771      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20772      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20773      */
20774     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20775     
20776     weekStart : 0,
20777     
20778     viewMode : '',
20779     
20780     minViewMode : '',
20781     
20782     todayHighlight : false,
20783     
20784     todayBtn: false,
20785     
20786     language: 'en',
20787     
20788     keyboardNavigation: true,
20789     
20790     calendarWeeks: false,
20791     
20792     startDate: -Infinity,
20793     
20794     endDate: Infinity,
20795     
20796     daysOfWeekDisabled: [],
20797     
20798     _events: [],
20799     
20800     singleMode : false,
20801     
20802     UTCDate: function()
20803     {
20804         return new Date(Date.UTC.apply(Date, arguments));
20805     },
20806     
20807     UTCToday: function()
20808     {
20809         var today = new Date();
20810         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20811     },
20812     
20813     getDate: function() {
20814             var d = this.getUTCDate();
20815             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20816     },
20817     
20818     getUTCDate: function() {
20819             return this.date;
20820     },
20821     
20822     setDate: function(d) {
20823             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20824     },
20825     
20826     setUTCDate: function(d) {
20827             this.date = d;
20828             this.setValue(this.formatDate(this.date));
20829     },
20830         
20831     onRender: function(ct, position)
20832     {
20833         
20834         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20835         
20836         this.language = this.language || 'en';
20837         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20838         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20839         
20840         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20841         this.format = this.format || 'm/d/y';
20842         this.isInline = false;
20843         this.isInput = true;
20844         this.component = this.el.select('.add-on', true).first() || false;
20845         this.component = (this.component && this.component.length === 0) ? false : this.component;
20846         this.hasInput = this.component && this.inputEl().length;
20847         
20848         if (typeof(this.minViewMode === 'string')) {
20849             switch (this.minViewMode) {
20850                 case 'months':
20851                     this.minViewMode = 1;
20852                     break;
20853                 case 'years':
20854                     this.minViewMode = 2;
20855                     break;
20856                 default:
20857                     this.minViewMode = 0;
20858                     break;
20859             }
20860         }
20861         
20862         if (typeof(this.viewMode === 'string')) {
20863             switch (this.viewMode) {
20864                 case 'months':
20865                     this.viewMode = 1;
20866                     break;
20867                 case 'years':
20868                     this.viewMode = 2;
20869                     break;
20870                 default:
20871                     this.viewMode = 0;
20872                     break;
20873             }
20874         }
20875                 
20876         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20877         
20878 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20879         
20880         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20881         
20882         this.picker().on('mousedown', this.onMousedown, this);
20883         this.picker().on('click', this.onClick, this);
20884         
20885         this.picker().addClass('datepicker-dropdown');
20886         
20887         this.startViewMode = this.viewMode;
20888         
20889         if(this.singleMode){
20890             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20891                 v.setVisibilityMode(Roo.Element.DISPLAY);
20892                 v.hide();
20893             });
20894             
20895             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20896                 v.setStyle('width', '189px');
20897             });
20898         }
20899         
20900         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20901             if(!this.calendarWeeks){
20902                 v.remove();
20903                 return;
20904             }
20905             
20906             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20907             v.attr('colspan', function(i, val){
20908                 return parseInt(val) + 1;
20909             });
20910         });
20911                         
20912         
20913         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20914         
20915         this.setStartDate(this.startDate);
20916         this.setEndDate(this.endDate);
20917         
20918         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20919         
20920         this.fillDow();
20921         this.fillMonths();
20922         this.update();
20923         this.showMode();
20924         
20925         if(this.isInline) {
20926             this.showPopup();
20927         }
20928     },
20929     
20930     picker : function()
20931     {
20932         return this.pickerEl;
20933 //        return this.el.select('.datepicker', true).first();
20934     },
20935     
20936     fillDow: function()
20937     {
20938         var dowCnt = this.weekStart;
20939         
20940         var dow = {
20941             tag: 'tr',
20942             cn: [
20943                 
20944             ]
20945         };
20946         
20947         if(this.calendarWeeks){
20948             dow.cn.push({
20949                 tag: 'th',
20950                 cls: 'cw',
20951                 html: '&nbsp;'
20952             })
20953         }
20954         
20955         while (dowCnt < this.weekStart + 7) {
20956             dow.cn.push({
20957                 tag: 'th',
20958                 cls: 'dow',
20959                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20960             });
20961         }
20962         
20963         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20964     },
20965     
20966     fillMonths: function()
20967     {    
20968         var i = 0;
20969         var months = this.picker().select('>.datepicker-months td', true).first();
20970         
20971         months.dom.innerHTML = '';
20972         
20973         while (i < 12) {
20974             var month = {
20975                 tag: 'span',
20976                 cls: 'month',
20977                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20978             };
20979             
20980             months.createChild(month);
20981         }
20982         
20983     },
20984     
20985     update: function()
20986     {
20987         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;
20988         
20989         if (this.date < this.startDate) {
20990             this.viewDate = new Date(this.startDate);
20991         } else if (this.date > this.endDate) {
20992             this.viewDate = new Date(this.endDate);
20993         } else {
20994             this.viewDate = new Date(this.date);
20995         }
20996         
20997         this.fill();
20998     },
20999     
21000     fill: function() 
21001     {
21002         var d = new Date(this.viewDate),
21003                 year = d.getUTCFullYear(),
21004                 month = d.getUTCMonth(),
21005                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21006                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21007                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21008                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21009                 currentDate = this.date && this.date.valueOf(),
21010                 today = this.UTCToday();
21011         
21012         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21013         
21014 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21015         
21016 //        this.picker.select('>tfoot th.today').
21017 //                                              .text(dates[this.language].today)
21018 //                                              .toggle(this.todayBtn !== false);
21019     
21020         this.updateNavArrows();
21021         this.fillMonths();
21022                                                 
21023         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21024         
21025         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21026          
21027         prevMonth.setUTCDate(day);
21028         
21029         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21030         
21031         var nextMonth = new Date(prevMonth);
21032         
21033         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21034         
21035         nextMonth = nextMonth.valueOf();
21036         
21037         var fillMonths = false;
21038         
21039         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21040         
21041         while(prevMonth.valueOf() <= nextMonth) {
21042             var clsName = '';
21043             
21044             if (prevMonth.getUTCDay() === this.weekStart) {
21045                 if(fillMonths){
21046                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21047                 }
21048                     
21049                 fillMonths = {
21050                     tag: 'tr',
21051                     cn: []
21052                 };
21053                 
21054                 if(this.calendarWeeks){
21055                     // ISO 8601: First week contains first thursday.
21056                     // ISO also states week starts on Monday, but we can be more abstract here.
21057                     var
21058                     // Start of current week: based on weekstart/current date
21059                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21060                     // Thursday of this week
21061                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21062                     // First Thursday of year, year from thursday
21063                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21064                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21065                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21066                     
21067                     fillMonths.cn.push({
21068                         tag: 'td',
21069                         cls: 'cw',
21070                         html: calWeek
21071                     });
21072                 }
21073             }
21074             
21075             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21076                 clsName += ' old';
21077             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21078                 clsName += ' new';
21079             }
21080             if (this.todayHighlight &&
21081                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21082                 prevMonth.getUTCMonth() == today.getMonth() &&
21083                 prevMonth.getUTCDate() == today.getDate()) {
21084                 clsName += ' today';
21085             }
21086             
21087             if (currentDate && prevMonth.valueOf() === currentDate) {
21088                 clsName += ' active';
21089             }
21090             
21091             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21092                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21093                     clsName += ' disabled';
21094             }
21095             
21096             fillMonths.cn.push({
21097                 tag: 'td',
21098                 cls: 'day ' + clsName,
21099                 html: prevMonth.getDate()
21100             });
21101             
21102             prevMonth.setDate(prevMonth.getDate()+1);
21103         }
21104           
21105         var currentYear = this.date && this.date.getUTCFullYear();
21106         var currentMonth = this.date && this.date.getUTCMonth();
21107         
21108         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21109         
21110         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21111             v.removeClass('active');
21112             
21113             if(currentYear === year && k === currentMonth){
21114                 v.addClass('active');
21115             }
21116             
21117             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21118                 v.addClass('disabled');
21119             }
21120             
21121         });
21122         
21123         
21124         year = parseInt(year/10, 10) * 10;
21125         
21126         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21127         
21128         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21129         
21130         year -= 1;
21131         for (var i = -1; i < 11; i++) {
21132             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21133                 tag: 'span',
21134                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21135                 html: year
21136             });
21137             
21138             year += 1;
21139         }
21140     },
21141     
21142     showMode: function(dir) 
21143     {
21144         if (dir) {
21145             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21146         }
21147         
21148         Roo.each(this.picker().select('>div',true).elements, function(v){
21149             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21150             v.hide();
21151         });
21152         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21153     },
21154     
21155     place: function()
21156     {
21157         if(this.isInline) {
21158             return;
21159         }
21160         
21161         this.picker().removeClass(['bottom', 'top']);
21162         
21163         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21164             /*
21165              * place to the top of element!
21166              *
21167              */
21168             
21169             this.picker().addClass('top');
21170             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21171             
21172             return;
21173         }
21174         
21175         this.picker().addClass('bottom');
21176         
21177         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21178     },
21179     
21180     parseDate : function(value)
21181     {
21182         if(!value || value instanceof Date){
21183             return value;
21184         }
21185         var v = Date.parseDate(value, this.format);
21186         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21187             v = Date.parseDate(value, 'Y-m-d');
21188         }
21189         if(!v && this.altFormats){
21190             if(!this.altFormatsArray){
21191                 this.altFormatsArray = this.altFormats.split("|");
21192             }
21193             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21194                 v = Date.parseDate(value, this.altFormatsArray[i]);
21195             }
21196         }
21197         return v;
21198     },
21199     
21200     formatDate : function(date, fmt)
21201     {   
21202         return (!date || !(date instanceof Date)) ?
21203         date : date.dateFormat(fmt || this.format);
21204     },
21205     
21206     onFocus : function()
21207     {
21208         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21209         this.showPopup();
21210     },
21211     
21212     onBlur : function()
21213     {
21214         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21215         
21216         var d = this.inputEl().getValue();
21217         
21218         this.setValue(d);
21219                 
21220         this.hidePopup();
21221     },
21222     
21223     showPopup : function()
21224     {
21225         this.picker().show();
21226         this.update();
21227         this.place();
21228         
21229         this.fireEvent('showpopup', this, this.date);
21230     },
21231     
21232     hidePopup : function()
21233     {
21234         if(this.isInline) {
21235             return;
21236         }
21237         this.picker().hide();
21238         this.viewMode = this.startViewMode;
21239         this.showMode();
21240         
21241         this.fireEvent('hidepopup', this, this.date);
21242         
21243     },
21244     
21245     onMousedown: function(e)
21246     {
21247         e.stopPropagation();
21248         e.preventDefault();
21249     },
21250     
21251     keyup: function(e)
21252     {
21253         Roo.bootstrap.DateField.superclass.keyup.call(this);
21254         this.update();
21255     },
21256
21257     setValue: function(v)
21258     {
21259         if(this.fireEvent('beforeselect', this, v) !== false){
21260             var d = new Date(this.parseDate(v) ).clearTime();
21261         
21262             if(isNaN(d.getTime())){
21263                 this.date = this.viewDate = '';
21264                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21265                 return;
21266             }
21267
21268             v = this.formatDate(d);
21269
21270             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21271
21272             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21273
21274             this.update();
21275
21276             this.fireEvent('select', this, this.date);
21277         }
21278     },
21279     
21280     getValue: function()
21281     {
21282         return this.formatDate(this.date);
21283     },
21284     
21285     fireKey: function(e)
21286     {
21287         if (!this.picker().isVisible()){
21288             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21289                 this.showPopup();
21290             }
21291             return;
21292         }
21293         
21294         var dateChanged = false,
21295         dir, day, month,
21296         newDate, newViewDate;
21297         
21298         switch(e.keyCode){
21299             case 27: // escape
21300                 this.hidePopup();
21301                 e.preventDefault();
21302                 break;
21303             case 37: // left
21304             case 39: // right
21305                 if (!this.keyboardNavigation) {
21306                     break;
21307                 }
21308                 dir = e.keyCode == 37 ? -1 : 1;
21309                 
21310                 if (e.ctrlKey){
21311                     newDate = this.moveYear(this.date, dir);
21312                     newViewDate = this.moveYear(this.viewDate, dir);
21313                 } else if (e.shiftKey){
21314                     newDate = this.moveMonth(this.date, dir);
21315                     newViewDate = this.moveMonth(this.viewDate, dir);
21316                 } else {
21317                     newDate = new Date(this.date);
21318                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21319                     newViewDate = new Date(this.viewDate);
21320                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21321                 }
21322                 if (this.dateWithinRange(newDate)){
21323                     this.date = newDate;
21324                     this.viewDate = newViewDate;
21325                     this.setValue(this.formatDate(this.date));
21326 //                    this.update();
21327                     e.preventDefault();
21328                     dateChanged = true;
21329                 }
21330                 break;
21331             case 38: // up
21332             case 40: // down
21333                 if (!this.keyboardNavigation) {
21334                     break;
21335                 }
21336                 dir = e.keyCode == 38 ? -1 : 1;
21337                 if (e.ctrlKey){
21338                     newDate = this.moveYear(this.date, dir);
21339                     newViewDate = this.moveYear(this.viewDate, dir);
21340                 } else if (e.shiftKey){
21341                     newDate = this.moveMonth(this.date, dir);
21342                     newViewDate = this.moveMonth(this.viewDate, dir);
21343                 } else {
21344                     newDate = new Date(this.date);
21345                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21346                     newViewDate = new Date(this.viewDate);
21347                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21348                 }
21349                 if (this.dateWithinRange(newDate)){
21350                     this.date = newDate;
21351                     this.viewDate = newViewDate;
21352                     this.setValue(this.formatDate(this.date));
21353 //                    this.update();
21354                     e.preventDefault();
21355                     dateChanged = true;
21356                 }
21357                 break;
21358             case 13: // enter
21359                 this.setValue(this.formatDate(this.date));
21360                 this.hidePopup();
21361                 e.preventDefault();
21362                 break;
21363             case 9: // tab
21364                 this.setValue(this.formatDate(this.date));
21365                 this.hidePopup();
21366                 break;
21367             case 16: // shift
21368             case 17: // ctrl
21369             case 18: // alt
21370                 break;
21371             default :
21372                 this.hidePopup();
21373                 
21374         }
21375     },
21376     
21377     
21378     onClick: function(e) 
21379     {
21380         e.stopPropagation();
21381         e.preventDefault();
21382         
21383         var target = e.getTarget();
21384         
21385         if(target.nodeName.toLowerCase() === 'i'){
21386             target = Roo.get(target).dom.parentNode;
21387         }
21388         
21389         var nodeName = target.nodeName;
21390         var className = target.className;
21391         var html = target.innerHTML;
21392         //Roo.log(nodeName);
21393         
21394         switch(nodeName.toLowerCase()) {
21395             case 'th':
21396                 switch(className) {
21397                     case 'switch':
21398                         this.showMode(1);
21399                         break;
21400                     case 'prev':
21401                     case 'next':
21402                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21403                         switch(this.viewMode){
21404                                 case 0:
21405                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21406                                         break;
21407                                 case 1:
21408                                 case 2:
21409                                         this.viewDate = this.moveYear(this.viewDate, dir);
21410                                         break;
21411                         }
21412                         this.fill();
21413                         break;
21414                     case 'today':
21415                         var date = new Date();
21416                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21417 //                        this.fill()
21418                         this.setValue(this.formatDate(this.date));
21419                         
21420                         this.hidePopup();
21421                         break;
21422                 }
21423                 break;
21424             case 'span':
21425                 if (className.indexOf('disabled') < 0) {
21426                     this.viewDate.setUTCDate(1);
21427                     if (className.indexOf('month') > -1) {
21428                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21429                     } else {
21430                         var year = parseInt(html, 10) || 0;
21431                         this.viewDate.setUTCFullYear(year);
21432                         
21433                     }
21434                     
21435                     if(this.singleMode){
21436                         this.setValue(this.formatDate(this.viewDate));
21437                         this.hidePopup();
21438                         return;
21439                     }
21440                     
21441                     this.showMode(-1);
21442                     this.fill();
21443                 }
21444                 break;
21445                 
21446             case 'td':
21447                 //Roo.log(className);
21448                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21449                     var day = parseInt(html, 10) || 1;
21450                     var year = this.viewDate.getUTCFullYear(),
21451                         month = this.viewDate.getUTCMonth();
21452
21453                     if (className.indexOf('old') > -1) {
21454                         if(month === 0 ){
21455                             month = 11;
21456                             year -= 1;
21457                         }else{
21458                             month -= 1;
21459                         }
21460                     } else if (className.indexOf('new') > -1) {
21461                         if (month == 11) {
21462                             month = 0;
21463                             year += 1;
21464                         } else {
21465                             month += 1;
21466                         }
21467                     }
21468                     //Roo.log([year,month,day]);
21469                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21470                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21471 //                    this.fill();
21472                     //Roo.log(this.formatDate(this.date));
21473                     this.setValue(this.formatDate(this.date));
21474                     this.hidePopup();
21475                 }
21476                 break;
21477         }
21478     },
21479     
21480     setStartDate: function(startDate)
21481     {
21482         this.startDate = startDate || -Infinity;
21483         if (this.startDate !== -Infinity) {
21484             this.startDate = this.parseDate(this.startDate);
21485         }
21486         this.update();
21487         this.updateNavArrows();
21488     },
21489
21490     setEndDate: function(endDate)
21491     {
21492         this.endDate = endDate || Infinity;
21493         if (this.endDate !== Infinity) {
21494             this.endDate = this.parseDate(this.endDate);
21495         }
21496         this.update();
21497         this.updateNavArrows();
21498     },
21499     
21500     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21501     {
21502         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21503         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21504             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21505         }
21506         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21507             return parseInt(d, 10);
21508         });
21509         this.update();
21510         this.updateNavArrows();
21511     },
21512     
21513     updateNavArrows: function() 
21514     {
21515         if(this.singleMode){
21516             return;
21517         }
21518         
21519         var d = new Date(this.viewDate),
21520         year = d.getUTCFullYear(),
21521         month = d.getUTCMonth();
21522         
21523         Roo.each(this.picker().select('.prev', true).elements, function(v){
21524             v.show();
21525             switch (this.viewMode) {
21526                 case 0:
21527
21528                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21529                         v.hide();
21530                     }
21531                     break;
21532                 case 1:
21533                 case 2:
21534                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21535                         v.hide();
21536                     }
21537                     break;
21538             }
21539         });
21540         
21541         Roo.each(this.picker().select('.next', true).elements, function(v){
21542             v.show();
21543             switch (this.viewMode) {
21544                 case 0:
21545
21546                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21547                         v.hide();
21548                     }
21549                     break;
21550                 case 1:
21551                 case 2:
21552                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21553                         v.hide();
21554                     }
21555                     break;
21556             }
21557         })
21558     },
21559     
21560     moveMonth: function(date, dir)
21561     {
21562         if (!dir) {
21563             return date;
21564         }
21565         var new_date = new Date(date.valueOf()),
21566         day = new_date.getUTCDate(),
21567         month = new_date.getUTCMonth(),
21568         mag = Math.abs(dir),
21569         new_month, test;
21570         dir = dir > 0 ? 1 : -1;
21571         if (mag == 1){
21572             test = dir == -1
21573             // If going back one month, make sure month is not current month
21574             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21575             ? function(){
21576                 return new_date.getUTCMonth() == month;
21577             }
21578             // If going forward one month, make sure month is as expected
21579             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21580             : function(){
21581                 return new_date.getUTCMonth() != new_month;
21582             };
21583             new_month = month + dir;
21584             new_date.setUTCMonth(new_month);
21585             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21586             if (new_month < 0 || new_month > 11) {
21587                 new_month = (new_month + 12) % 12;
21588             }
21589         } else {
21590             // For magnitudes >1, move one month at a time...
21591             for (var i=0; i<mag; i++) {
21592                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21593                 new_date = this.moveMonth(new_date, dir);
21594             }
21595             // ...then reset the day, keeping it in the new month
21596             new_month = new_date.getUTCMonth();
21597             new_date.setUTCDate(day);
21598             test = function(){
21599                 return new_month != new_date.getUTCMonth();
21600             };
21601         }
21602         // Common date-resetting loop -- if date is beyond end of month, make it
21603         // end of month
21604         while (test()){
21605             new_date.setUTCDate(--day);
21606             new_date.setUTCMonth(new_month);
21607         }
21608         return new_date;
21609     },
21610
21611     moveYear: function(date, dir)
21612     {
21613         return this.moveMonth(date, dir*12);
21614     },
21615
21616     dateWithinRange: function(date)
21617     {
21618         return date >= this.startDate && date <= this.endDate;
21619     },
21620
21621     
21622     remove: function() 
21623     {
21624         this.picker().remove();
21625     },
21626     
21627     validateValue : function(value)
21628     {
21629         if(this.getVisibilityEl().hasClass('hidden')){
21630             return true;
21631         }
21632         
21633         if(value.length < 1)  {
21634             if(this.allowBlank){
21635                 return true;
21636             }
21637             return false;
21638         }
21639         
21640         if(value.length < this.minLength){
21641             return false;
21642         }
21643         if(value.length > this.maxLength){
21644             return false;
21645         }
21646         if(this.vtype){
21647             var vt = Roo.form.VTypes;
21648             if(!vt[this.vtype](value, this)){
21649                 return false;
21650             }
21651         }
21652         if(typeof this.validator == "function"){
21653             var msg = this.validator(value);
21654             if(msg !== true){
21655                 return false;
21656             }
21657         }
21658         
21659         if(this.regex && !this.regex.test(value)){
21660             return false;
21661         }
21662         
21663         if(typeof(this.parseDate(value)) == 'undefined'){
21664             return false;
21665         }
21666         
21667         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21668             return false;
21669         }      
21670         
21671         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21672             return false;
21673         } 
21674         
21675         
21676         return true;
21677     },
21678     
21679     reset : function()
21680     {
21681         this.date = this.viewDate = '';
21682         
21683         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21684     }
21685    
21686 });
21687
21688 Roo.apply(Roo.bootstrap.DateField,  {
21689     
21690     head : {
21691         tag: 'thead',
21692         cn: [
21693         {
21694             tag: 'tr',
21695             cn: [
21696             {
21697                 tag: 'th',
21698                 cls: 'prev',
21699                 html: '<i class="fa fa-arrow-left"/>'
21700             },
21701             {
21702                 tag: 'th',
21703                 cls: 'switch',
21704                 colspan: '5'
21705             },
21706             {
21707                 tag: 'th',
21708                 cls: 'next',
21709                 html: '<i class="fa fa-arrow-right"/>'
21710             }
21711
21712             ]
21713         }
21714         ]
21715     },
21716     
21717     content : {
21718         tag: 'tbody',
21719         cn: [
21720         {
21721             tag: 'tr',
21722             cn: [
21723             {
21724                 tag: 'td',
21725                 colspan: '7'
21726             }
21727             ]
21728         }
21729         ]
21730     },
21731     
21732     footer : {
21733         tag: 'tfoot',
21734         cn: [
21735         {
21736             tag: 'tr',
21737             cn: [
21738             {
21739                 tag: 'th',
21740                 colspan: '7',
21741                 cls: 'today'
21742             }
21743                     
21744             ]
21745         }
21746         ]
21747     },
21748     
21749     dates:{
21750         en: {
21751             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21752             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21753             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21754             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21755             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21756             today: "Today"
21757         }
21758     },
21759     
21760     modes: [
21761     {
21762         clsName: 'days',
21763         navFnc: 'Month',
21764         navStep: 1
21765     },
21766     {
21767         clsName: 'months',
21768         navFnc: 'FullYear',
21769         navStep: 1
21770     },
21771     {
21772         clsName: 'years',
21773         navFnc: 'FullYear',
21774         navStep: 10
21775     }]
21776 });
21777
21778 Roo.apply(Roo.bootstrap.DateField,  {
21779   
21780     template : {
21781         tag: 'div',
21782         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21783         cn: [
21784         {
21785             tag: 'div',
21786             cls: 'datepicker-days',
21787             cn: [
21788             {
21789                 tag: 'table',
21790                 cls: 'table-condensed',
21791                 cn:[
21792                 Roo.bootstrap.DateField.head,
21793                 {
21794                     tag: 'tbody'
21795                 },
21796                 Roo.bootstrap.DateField.footer
21797                 ]
21798             }
21799             ]
21800         },
21801         {
21802             tag: 'div',
21803             cls: 'datepicker-months',
21804             cn: [
21805             {
21806                 tag: 'table',
21807                 cls: 'table-condensed',
21808                 cn:[
21809                 Roo.bootstrap.DateField.head,
21810                 Roo.bootstrap.DateField.content,
21811                 Roo.bootstrap.DateField.footer
21812                 ]
21813             }
21814             ]
21815         },
21816         {
21817             tag: 'div',
21818             cls: 'datepicker-years',
21819             cn: [
21820             {
21821                 tag: 'table',
21822                 cls: 'table-condensed',
21823                 cn:[
21824                 Roo.bootstrap.DateField.head,
21825                 Roo.bootstrap.DateField.content,
21826                 Roo.bootstrap.DateField.footer
21827                 ]
21828             }
21829             ]
21830         }
21831         ]
21832     }
21833 });
21834
21835  
21836
21837  /*
21838  * - LGPL
21839  *
21840  * TimeField
21841  * 
21842  */
21843
21844 /**
21845  * @class Roo.bootstrap.TimeField
21846  * @extends Roo.bootstrap.Input
21847  * Bootstrap DateField class
21848  * 
21849  * 
21850  * @constructor
21851  * Create a new TimeField
21852  * @param {Object} config The config object
21853  */
21854
21855 Roo.bootstrap.TimeField = function(config){
21856     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21857     this.addEvents({
21858             /**
21859              * @event show
21860              * Fires when this field show.
21861              * @param {Roo.bootstrap.DateField} thisthis
21862              * @param {Mixed} date The date value
21863              */
21864             show : true,
21865             /**
21866              * @event show
21867              * Fires when this field hide.
21868              * @param {Roo.bootstrap.DateField} this
21869              * @param {Mixed} date The date value
21870              */
21871             hide : true,
21872             /**
21873              * @event select
21874              * Fires when select a date.
21875              * @param {Roo.bootstrap.DateField} this
21876              * @param {Mixed} date The date value
21877              */
21878             select : true
21879         });
21880 };
21881
21882 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21883     
21884     /**
21885      * @cfg {String} format
21886      * The default time format string which can be overriden for localization support.  The format must be
21887      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21888      */
21889     format : "H:i",
21890        
21891     onRender: function(ct, position)
21892     {
21893         
21894         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21895                 
21896         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21897         
21898         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21899         
21900         this.pop = this.picker().select('>.datepicker-time',true).first();
21901         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21902         
21903         this.picker().on('mousedown', this.onMousedown, this);
21904         this.picker().on('click', this.onClick, this);
21905         
21906         this.picker().addClass('datepicker-dropdown');
21907     
21908         this.fillTime();
21909         this.update();
21910             
21911         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21912         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21913         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21914         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21915         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21916         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21917
21918     },
21919     
21920     fireKey: function(e){
21921         if (!this.picker().isVisible()){
21922             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21923                 this.show();
21924             }
21925             return;
21926         }
21927
21928         e.preventDefault();
21929         
21930         switch(e.keyCode){
21931             case 27: // escape
21932                 this.hide();
21933                 break;
21934             case 37: // left
21935             case 39: // right
21936                 this.onTogglePeriod();
21937                 break;
21938             case 38: // up
21939                 this.onIncrementMinutes();
21940                 break;
21941             case 40: // down
21942                 this.onDecrementMinutes();
21943                 break;
21944             case 13: // enter
21945             case 9: // tab
21946                 this.setTime();
21947                 break;
21948         }
21949     },
21950     
21951     onClick: function(e) {
21952         e.stopPropagation();
21953         e.preventDefault();
21954     },
21955     
21956     picker : function()
21957     {
21958         return this.el.select('.datepicker', true).first();
21959     },
21960     
21961     fillTime: function()
21962     {    
21963         var time = this.pop.select('tbody', true).first();
21964         
21965         time.dom.innerHTML = '';
21966         
21967         time.createChild({
21968             tag: 'tr',
21969             cn: [
21970                 {
21971                     tag: 'td',
21972                     cn: [
21973                         {
21974                             tag: 'a',
21975                             href: '#',
21976                             cls: 'btn',
21977                             cn: [
21978                                 {
21979                                     tag: 'span',
21980                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21981                                 }
21982                             ]
21983                         } 
21984                     ]
21985                 },
21986                 {
21987                     tag: 'td',
21988                     cls: 'separator'
21989                 },
21990                 {
21991                     tag: 'td',
21992                     cn: [
21993                         {
21994                             tag: 'a',
21995                             href: '#',
21996                             cls: 'btn',
21997                             cn: [
21998                                 {
21999                                     tag: 'span',
22000                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22001                                 }
22002                             ]
22003                         }
22004                     ]
22005                 },
22006                 {
22007                     tag: 'td',
22008                     cls: 'separator'
22009                 }
22010             ]
22011         });
22012         
22013         time.createChild({
22014             tag: 'tr',
22015             cn: [
22016                 {
22017                     tag: 'td',
22018                     cn: [
22019                         {
22020                             tag: 'span',
22021                             cls: 'timepicker-hour',
22022                             html: '00'
22023                         }  
22024                     ]
22025                 },
22026                 {
22027                     tag: 'td',
22028                     cls: 'separator',
22029                     html: ':'
22030                 },
22031                 {
22032                     tag: 'td',
22033                     cn: [
22034                         {
22035                             tag: 'span',
22036                             cls: 'timepicker-minute',
22037                             html: '00'
22038                         }  
22039                     ]
22040                 },
22041                 {
22042                     tag: 'td',
22043                     cls: 'separator'
22044                 },
22045                 {
22046                     tag: 'td',
22047                     cn: [
22048                         {
22049                             tag: 'button',
22050                             type: 'button',
22051                             cls: 'btn btn-primary period',
22052                             html: 'AM'
22053                             
22054                         }
22055                     ]
22056                 }
22057             ]
22058         });
22059         
22060         time.createChild({
22061             tag: 'tr',
22062             cn: [
22063                 {
22064                     tag: 'td',
22065                     cn: [
22066                         {
22067                             tag: 'a',
22068                             href: '#',
22069                             cls: 'btn',
22070                             cn: [
22071                                 {
22072                                     tag: 'span',
22073                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22074                                 }
22075                             ]
22076                         }
22077                     ]
22078                 },
22079                 {
22080                     tag: 'td',
22081                     cls: 'separator'
22082                 },
22083                 {
22084                     tag: 'td',
22085                     cn: [
22086                         {
22087                             tag: 'a',
22088                             href: '#',
22089                             cls: 'btn',
22090                             cn: [
22091                                 {
22092                                     tag: 'span',
22093                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22094                                 }
22095                             ]
22096                         }
22097                     ]
22098                 },
22099                 {
22100                     tag: 'td',
22101                     cls: 'separator'
22102                 }
22103             ]
22104         });
22105         
22106     },
22107     
22108     update: function()
22109     {
22110         
22111         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22112         
22113         this.fill();
22114     },
22115     
22116     fill: function() 
22117     {
22118         var hours = this.time.getHours();
22119         var minutes = this.time.getMinutes();
22120         var period = 'AM';
22121         
22122         if(hours > 11){
22123             period = 'PM';
22124         }
22125         
22126         if(hours == 0){
22127             hours = 12;
22128         }
22129         
22130         
22131         if(hours > 12){
22132             hours = hours - 12;
22133         }
22134         
22135         if(hours < 10){
22136             hours = '0' + hours;
22137         }
22138         
22139         if(minutes < 10){
22140             minutes = '0' + minutes;
22141         }
22142         
22143         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22144         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22145         this.pop.select('button', true).first().dom.innerHTML = period;
22146         
22147     },
22148     
22149     place: function()
22150     {   
22151         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22152         
22153         var cls = ['bottom'];
22154         
22155         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22156             cls.pop();
22157             cls.push('top');
22158         }
22159         
22160         cls.push('right');
22161         
22162         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22163             cls.pop();
22164             cls.push('left');
22165         }
22166         
22167         this.picker().addClass(cls.join('-'));
22168         
22169         var _this = this;
22170         
22171         Roo.each(cls, function(c){
22172             if(c == 'bottom'){
22173                 _this.picker().setTop(_this.inputEl().getHeight());
22174                 return;
22175             }
22176             if(c == 'top'){
22177                 _this.picker().setTop(0 - _this.picker().getHeight());
22178                 return;
22179             }
22180             
22181             if(c == 'left'){
22182                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22183                 return;
22184             }
22185             if(c == 'right'){
22186                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22187                 return;
22188             }
22189         });
22190         
22191     },
22192   
22193     onFocus : function()
22194     {
22195         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22196         this.show();
22197     },
22198     
22199     onBlur : function()
22200     {
22201         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22202         this.hide();
22203     },
22204     
22205     show : function()
22206     {
22207         this.picker().show();
22208         this.pop.show();
22209         this.update();
22210         this.place();
22211         
22212         this.fireEvent('show', this, this.date);
22213     },
22214     
22215     hide : function()
22216     {
22217         this.picker().hide();
22218         this.pop.hide();
22219         
22220         this.fireEvent('hide', this, this.date);
22221     },
22222     
22223     setTime : function()
22224     {
22225         this.hide();
22226         this.setValue(this.time.format(this.format));
22227         
22228         this.fireEvent('select', this, this.date);
22229         
22230         
22231     },
22232     
22233     onMousedown: function(e){
22234         e.stopPropagation();
22235         e.preventDefault();
22236     },
22237     
22238     onIncrementHours: function()
22239     {
22240         Roo.log('onIncrementHours');
22241         this.time = this.time.add(Date.HOUR, 1);
22242         this.update();
22243         
22244     },
22245     
22246     onDecrementHours: function()
22247     {
22248         Roo.log('onDecrementHours');
22249         this.time = this.time.add(Date.HOUR, -1);
22250         this.update();
22251     },
22252     
22253     onIncrementMinutes: function()
22254     {
22255         Roo.log('onIncrementMinutes');
22256         this.time = this.time.add(Date.MINUTE, 1);
22257         this.update();
22258     },
22259     
22260     onDecrementMinutes: function()
22261     {
22262         Roo.log('onDecrementMinutes');
22263         this.time = this.time.add(Date.MINUTE, -1);
22264         this.update();
22265     },
22266     
22267     onTogglePeriod: function()
22268     {
22269         Roo.log('onTogglePeriod');
22270         this.time = this.time.add(Date.HOUR, 12);
22271         this.update();
22272     }
22273     
22274    
22275 });
22276
22277 Roo.apply(Roo.bootstrap.TimeField,  {
22278     
22279     content : {
22280         tag: 'tbody',
22281         cn: [
22282             {
22283                 tag: 'tr',
22284                 cn: [
22285                 {
22286                     tag: 'td',
22287                     colspan: '7'
22288                 }
22289                 ]
22290             }
22291         ]
22292     },
22293     
22294     footer : {
22295         tag: 'tfoot',
22296         cn: [
22297             {
22298                 tag: 'tr',
22299                 cn: [
22300                 {
22301                     tag: 'th',
22302                     colspan: '7',
22303                     cls: '',
22304                     cn: [
22305                         {
22306                             tag: 'button',
22307                             cls: 'btn btn-info ok',
22308                             html: 'OK'
22309                         }
22310                     ]
22311                 }
22312
22313                 ]
22314             }
22315         ]
22316     }
22317 });
22318
22319 Roo.apply(Roo.bootstrap.TimeField,  {
22320   
22321     template : {
22322         tag: 'div',
22323         cls: 'datepicker dropdown-menu',
22324         cn: [
22325             {
22326                 tag: 'div',
22327                 cls: 'datepicker-time',
22328                 cn: [
22329                 {
22330                     tag: 'table',
22331                     cls: 'table-condensed',
22332                     cn:[
22333                     Roo.bootstrap.TimeField.content,
22334                     Roo.bootstrap.TimeField.footer
22335                     ]
22336                 }
22337                 ]
22338             }
22339         ]
22340     }
22341 });
22342
22343  
22344
22345  /*
22346  * - LGPL
22347  *
22348  * MonthField
22349  * 
22350  */
22351
22352 /**
22353  * @class Roo.bootstrap.MonthField
22354  * @extends Roo.bootstrap.Input
22355  * Bootstrap MonthField class
22356  * 
22357  * @cfg {String} language default en
22358  * 
22359  * @constructor
22360  * Create a new MonthField
22361  * @param {Object} config The config object
22362  */
22363
22364 Roo.bootstrap.MonthField = function(config){
22365     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22366     
22367     this.addEvents({
22368         /**
22369          * @event show
22370          * Fires when this field show.
22371          * @param {Roo.bootstrap.MonthField} this
22372          * @param {Mixed} date The date value
22373          */
22374         show : true,
22375         /**
22376          * @event show
22377          * Fires when this field hide.
22378          * @param {Roo.bootstrap.MonthField} this
22379          * @param {Mixed} date The date value
22380          */
22381         hide : true,
22382         /**
22383          * @event select
22384          * Fires when select a date.
22385          * @param {Roo.bootstrap.MonthField} this
22386          * @param {String} oldvalue The old value
22387          * @param {String} newvalue The new value
22388          */
22389         select : true
22390     });
22391 };
22392
22393 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22394     
22395     onRender: function(ct, position)
22396     {
22397         
22398         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22399         
22400         this.language = this.language || 'en';
22401         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22402         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22403         
22404         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22405         this.isInline = false;
22406         this.isInput = true;
22407         this.component = this.el.select('.add-on', true).first() || false;
22408         this.component = (this.component && this.component.length === 0) ? false : this.component;
22409         this.hasInput = this.component && this.inputEL().length;
22410         
22411         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22412         
22413         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22414         
22415         this.picker().on('mousedown', this.onMousedown, this);
22416         this.picker().on('click', this.onClick, this);
22417         
22418         this.picker().addClass('datepicker-dropdown');
22419         
22420         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22421             v.setStyle('width', '189px');
22422         });
22423         
22424         this.fillMonths();
22425         
22426         this.update();
22427         
22428         if(this.isInline) {
22429             this.show();
22430         }
22431         
22432     },
22433     
22434     setValue: function(v, suppressEvent)
22435     {   
22436         var o = this.getValue();
22437         
22438         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22439         
22440         this.update();
22441
22442         if(suppressEvent !== true){
22443             this.fireEvent('select', this, o, v);
22444         }
22445         
22446     },
22447     
22448     getValue: function()
22449     {
22450         return this.value;
22451     },
22452     
22453     onClick: function(e) 
22454     {
22455         e.stopPropagation();
22456         e.preventDefault();
22457         
22458         var target = e.getTarget();
22459         
22460         if(target.nodeName.toLowerCase() === 'i'){
22461             target = Roo.get(target).dom.parentNode;
22462         }
22463         
22464         var nodeName = target.nodeName;
22465         var className = target.className;
22466         var html = target.innerHTML;
22467         
22468         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22469             return;
22470         }
22471         
22472         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22473         
22474         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22475         
22476         this.hide();
22477                         
22478     },
22479     
22480     picker : function()
22481     {
22482         return this.pickerEl;
22483     },
22484     
22485     fillMonths: function()
22486     {    
22487         var i = 0;
22488         var months = this.picker().select('>.datepicker-months td', true).first();
22489         
22490         months.dom.innerHTML = '';
22491         
22492         while (i < 12) {
22493             var month = {
22494                 tag: 'span',
22495                 cls: 'month',
22496                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22497             };
22498             
22499             months.createChild(month);
22500         }
22501         
22502     },
22503     
22504     update: function()
22505     {
22506         var _this = this;
22507         
22508         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22509             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22510         }
22511         
22512         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22513             e.removeClass('active');
22514             
22515             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22516                 e.addClass('active');
22517             }
22518         })
22519     },
22520     
22521     place: function()
22522     {
22523         if(this.isInline) {
22524             return;
22525         }
22526         
22527         this.picker().removeClass(['bottom', 'top']);
22528         
22529         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22530             /*
22531              * place to the top of element!
22532              *
22533              */
22534             
22535             this.picker().addClass('top');
22536             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22537             
22538             return;
22539         }
22540         
22541         this.picker().addClass('bottom');
22542         
22543         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22544     },
22545     
22546     onFocus : function()
22547     {
22548         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22549         this.show();
22550     },
22551     
22552     onBlur : function()
22553     {
22554         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22555         
22556         var d = this.inputEl().getValue();
22557         
22558         this.setValue(d);
22559                 
22560         this.hide();
22561     },
22562     
22563     show : function()
22564     {
22565         this.picker().show();
22566         this.picker().select('>.datepicker-months', true).first().show();
22567         this.update();
22568         this.place();
22569         
22570         this.fireEvent('show', this, this.date);
22571     },
22572     
22573     hide : function()
22574     {
22575         if(this.isInline) {
22576             return;
22577         }
22578         this.picker().hide();
22579         this.fireEvent('hide', this, this.date);
22580         
22581     },
22582     
22583     onMousedown: function(e)
22584     {
22585         e.stopPropagation();
22586         e.preventDefault();
22587     },
22588     
22589     keyup: function(e)
22590     {
22591         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22592         this.update();
22593     },
22594
22595     fireKey: function(e)
22596     {
22597         if (!this.picker().isVisible()){
22598             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22599                 this.show();
22600             }
22601             return;
22602         }
22603         
22604         var dir;
22605         
22606         switch(e.keyCode){
22607             case 27: // escape
22608                 this.hide();
22609                 e.preventDefault();
22610                 break;
22611             case 37: // left
22612             case 39: // right
22613                 dir = e.keyCode == 37 ? -1 : 1;
22614                 
22615                 this.vIndex = this.vIndex + dir;
22616                 
22617                 if(this.vIndex < 0){
22618                     this.vIndex = 0;
22619                 }
22620                 
22621                 if(this.vIndex > 11){
22622                     this.vIndex = 11;
22623                 }
22624                 
22625                 if(isNaN(this.vIndex)){
22626                     this.vIndex = 0;
22627                 }
22628                 
22629                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22630                 
22631                 break;
22632             case 38: // up
22633             case 40: // down
22634                 
22635                 dir = e.keyCode == 38 ? -1 : 1;
22636                 
22637                 this.vIndex = this.vIndex + dir * 4;
22638                 
22639                 if(this.vIndex < 0){
22640                     this.vIndex = 0;
22641                 }
22642                 
22643                 if(this.vIndex > 11){
22644                     this.vIndex = 11;
22645                 }
22646                 
22647                 if(isNaN(this.vIndex)){
22648                     this.vIndex = 0;
22649                 }
22650                 
22651                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22652                 break;
22653                 
22654             case 13: // enter
22655                 
22656                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22657                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22658                 }
22659                 
22660                 this.hide();
22661                 e.preventDefault();
22662                 break;
22663             case 9: // tab
22664                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22665                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22666                 }
22667                 this.hide();
22668                 break;
22669             case 16: // shift
22670             case 17: // ctrl
22671             case 18: // alt
22672                 break;
22673             default :
22674                 this.hide();
22675                 
22676         }
22677     },
22678     
22679     remove: function() 
22680     {
22681         this.picker().remove();
22682     }
22683    
22684 });
22685
22686 Roo.apply(Roo.bootstrap.MonthField,  {
22687     
22688     content : {
22689         tag: 'tbody',
22690         cn: [
22691         {
22692             tag: 'tr',
22693             cn: [
22694             {
22695                 tag: 'td',
22696                 colspan: '7'
22697             }
22698             ]
22699         }
22700         ]
22701     },
22702     
22703     dates:{
22704         en: {
22705             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22706             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22707         }
22708     }
22709 });
22710
22711 Roo.apply(Roo.bootstrap.MonthField,  {
22712   
22713     template : {
22714         tag: 'div',
22715         cls: 'datepicker dropdown-menu roo-dynamic',
22716         cn: [
22717             {
22718                 tag: 'div',
22719                 cls: 'datepicker-months',
22720                 cn: [
22721                 {
22722                     tag: 'table',
22723                     cls: 'table-condensed',
22724                     cn:[
22725                         Roo.bootstrap.DateField.content
22726                     ]
22727                 }
22728                 ]
22729             }
22730         ]
22731     }
22732 });
22733
22734  
22735
22736  
22737  /*
22738  * - LGPL
22739  *
22740  * CheckBox
22741  * 
22742  */
22743
22744 /**
22745  * @class Roo.bootstrap.CheckBox
22746  * @extends Roo.bootstrap.Input
22747  * Bootstrap CheckBox class
22748  * 
22749  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22750  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22751  * @cfg {String} boxLabel The text that appears beside the checkbox
22752  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22753  * @cfg {Boolean} checked initnal the element
22754  * @cfg {Boolean} inline inline the element (default false)
22755  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22756  * @cfg {String} tooltip label tooltip
22757  * 
22758  * @constructor
22759  * Create a new CheckBox
22760  * @param {Object} config The config object
22761  */
22762
22763 Roo.bootstrap.CheckBox = function(config){
22764     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22765    
22766     this.addEvents({
22767         /**
22768         * @event check
22769         * Fires when the element is checked or unchecked.
22770         * @param {Roo.bootstrap.CheckBox} this This input
22771         * @param {Boolean} checked The new checked value
22772         */
22773        check : true,
22774        /**
22775         * @event click
22776         * Fires when the element is click.
22777         * @param {Roo.bootstrap.CheckBox} this This input
22778         */
22779        click : true
22780     });
22781     
22782 };
22783
22784 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22785   
22786     inputType: 'checkbox',
22787     inputValue: 1,
22788     valueOff: 0,
22789     boxLabel: false,
22790     checked: false,
22791     weight : false,
22792     inline: false,
22793     tooltip : '',
22794     
22795     // checkbox success does not make any sense really.. 
22796     invalidClass : "",
22797     validClass : "",
22798     
22799     
22800     getAutoCreate : function()
22801     {
22802         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22803         
22804         var id = Roo.id();
22805         
22806         var cfg = {};
22807         
22808         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22809         
22810         if(this.inline){
22811             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22812         }
22813         
22814         var input =  {
22815             tag: 'input',
22816             id : id,
22817             type : this.inputType,
22818             value : this.inputValue,
22819             cls : 'roo-' + this.inputType, //'form-box',
22820             placeholder : this.placeholder || ''
22821             
22822         };
22823         
22824         if(this.inputType != 'radio'){
22825             var hidden =  {
22826                 tag: 'input',
22827                 type : 'hidden',
22828                 cls : 'roo-hidden-value',
22829                 value : this.checked ? this.inputValue : this.valueOff
22830             };
22831         }
22832         
22833             
22834         if (this.weight) { // Validity check?
22835             cfg.cls += " " + this.inputType + "-" + this.weight;
22836         }
22837         
22838         if (this.disabled) {
22839             input.disabled=true;
22840         }
22841         
22842         if(this.checked){
22843             input.checked = this.checked;
22844         }
22845         
22846         if (this.name) {
22847             
22848             input.name = this.name;
22849             
22850             if(this.inputType != 'radio'){
22851                 hidden.name = this.name;
22852                 input.name = '_hidden_' + this.name;
22853             }
22854         }
22855         
22856         if (this.size) {
22857             input.cls += ' input-' + this.size;
22858         }
22859         
22860         var settings=this;
22861         
22862         ['xs','sm','md','lg'].map(function(size){
22863             if (settings[size]) {
22864                 cfg.cls += ' col-' + size + '-' + settings[size];
22865             }
22866         });
22867         
22868         var inputblock = input;
22869          
22870         if (this.before || this.after) {
22871             
22872             inputblock = {
22873                 cls : 'input-group',
22874                 cn :  [] 
22875             };
22876             
22877             if (this.before) {
22878                 inputblock.cn.push({
22879                     tag :'span',
22880                     cls : 'input-group-addon',
22881                     html : this.before
22882                 });
22883             }
22884             
22885             inputblock.cn.push(input);
22886             
22887             if(this.inputType != 'radio'){
22888                 inputblock.cn.push(hidden);
22889             }
22890             
22891             if (this.after) {
22892                 inputblock.cn.push({
22893                     tag :'span',
22894                     cls : 'input-group-addon',
22895                     html : this.after
22896                 });
22897             }
22898             
22899         }
22900         var boxLabelCfg = false;
22901         
22902         if(this.boxLabel){
22903            
22904             boxLabelCfg = {
22905                 tag: 'label',
22906                 //'for': id, // box label is handled by onclick - so no for...
22907                 cls: 'box-label',
22908                 html: this.boxLabel
22909             };
22910             if(this.tooltip){
22911                 boxLabelCfg.tooltip = this.tooltip;
22912             }
22913              
22914         }
22915         
22916         
22917         if (align ==='left' && this.fieldLabel.length) {
22918 //                Roo.log("left and has label");
22919             cfg.cn = [
22920                 {
22921                     tag: 'label',
22922                     'for' :  id,
22923                     cls : 'control-label',
22924                     html : this.fieldLabel
22925                 },
22926                 {
22927                     cls : "", 
22928                     cn: [
22929                         inputblock
22930                     ]
22931                 }
22932             ];
22933             
22934             if (boxLabelCfg) {
22935                 cfg.cn[1].cn.push(boxLabelCfg);
22936             }
22937             
22938             if(this.labelWidth > 12){
22939                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22940             }
22941             
22942             if(this.labelWidth < 13 && this.labelmd == 0){
22943                 this.labelmd = this.labelWidth;
22944             }
22945             
22946             if(this.labellg > 0){
22947                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22948                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22949             }
22950             
22951             if(this.labelmd > 0){
22952                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22953                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22954             }
22955             
22956             if(this.labelsm > 0){
22957                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22958                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22959             }
22960             
22961             if(this.labelxs > 0){
22962                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22963                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22964             }
22965             
22966         } else if ( this.fieldLabel.length) {
22967 //                Roo.log(" label");
22968                 cfg.cn = [
22969                    
22970                     {
22971                         tag: this.boxLabel ? 'span' : 'label',
22972                         'for': id,
22973                         cls: 'control-label box-input-label',
22974                         //cls : 'input-group-addon',
22975                         html : this.fieldLabel
22976                     },
22977                     
22978                     inputblock
22979                     
22980                 ];
22981                 if (boxLabelCfg) {
22982                     cfg.cn.push(boxLabelCfg);
22983                 }
22984
22985         } else {
22986             
22987 //                Roo.log(" no label && no align");
22988                 cfg.cn = [  inputblock ] ;
22989                 if (boxLabelCfg) {
22990                     cfg.cn.push(boxLabelCfg);
22991                 }
22992
22993                 
22994         }
22995         
22996        
22997         
22998         if(this.inputType != 'radio'){
22999             cfg.cn.push(hidden);
23000         }
23001         
23002         return cfg;
23003         
23004     },
23005     
23006     /**
23007      * return the real input element.
23008      */
23009     inputEl: function ()
23010     {
23011         return this.el.select('input.roo-' + this.inputType,true).first();
23012     },
23013     hiddenEl: function ()
23014     {
23015         return this.el.select('input.roo-hidden-value',true).first();
23016     },
23017     
23018     labelEl: function()
23019     {
23020         return this.el.select('label.control-label',true).first();
23021     },
23022     /* depricated... */
23023     
23024     label: function()
23025     {
23026         return this.labelEl();
23027     },
23028     
23029     boxLabelEl: function()
23030     {
23031         return this.el.select('label.box-label',true).first();
23032     },
23033     
23034     initEvents : function()
23035     {
23036 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23037         
23038         this.inputEl().on('click', this.onClick,  this);
23039         
23040         if (this.boxLabel) { 
23041             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23042         }
23043         
23044         this.startValue = this.getValue();
23045         
23046         if(this.groupId){
23047             Roo.bootstrap.CheckBox.register(this);
23048         }
23049     },
23050     
23051     onClick : function(e)
23052     {   
23053         if(this.fireEvent('click', this, e) !== false){
23054             this.setChecked(!this.checked);
23055         }
23056         
23057     },
23058     
23059     setChecked : function(state,suppressEvent)
23060     {
23061         this.startValue = this.getValue();
23062
23063         if(this.inputType == 'radio'){
23064             
23065             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23066                 e.dom.checked = false;
23067             });
23068             
23069             this.inputEl().dom.checked = true;
23070             
23071             this.inputEl().dom.value = this.inputValue;
23072             
23073             if(suppressEvent !== true){
23074                 this.fireEvent('check', this, true);
23075             }
23076             
23077             this.validate();
23078             
23079             return;
23080         }
23081         
23082         this.checked = state;
23083         
23084         this.inputEl().dom.checked = state;
23085         
23086         
23087         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23088         
23089         if(suppressEvent !== true){
23090             this.fireEvent('check', this, state);
23091         }
23092         
23093         this.validate();
23094     },
23095     
23096     getValue : function()
23097     {
23098         if(this.inputType == 'radio'){
23099             return this.getGroupValue();
23100         }
23101         
23102         return this.hiddenEl().dom.value;
23103         
23104     },
23105     
23106     getGroupValue : function()
23107     {
23108         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23109             return '';
23110         }
23111         
23112         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23113     },
23114     
23115     setValue : function(v,suppressEvent)
23116     {
23117         if(this.inputType == 'radio'){
23118             this.setGroupValue(v, suppressEvent);
23119             return;
23120         }
23121         
23122         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23123         
23124         this.validate();
23125     },
23126     
23127     setGroupValue : function(v, suppressEvent)
23128     {
23129         this.startValue = this.getValue();
23130         
23131         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23132             e.dom.checked = false;
23133             
23134             if(e.dom.value == v){
23135                 e.dom.checked = true;
23136             }
23137         });
23138         
23139         if(suppressEvent !== true){
23140             this.fireEvent('check', this, true);
23141         }
23142
23143         this.validate();
23144         
23145         return;
23146     },
23147     
23148     validate : function()
23149     {
23150         if(this.getVisibilityEl().hasClass('hidden')){
23151             return true;
23152         }
23153         
23154         if(
23155                 this.disabled || 
23156                 (this.inputType == 'radio' && this.validateRadio()) ||
23157                 (this.inputType == 'checkbox' && this.validateCheckbox())
23158         ){
23159             this.markValid();
23160             return true;
23161         }
23162         
23163         this.markInvalid();
23164         return false;
23165     },
23166     
23167     validateRadio : function()
23168     {
23169         if(this.getVisibilityEl().hasClass('hidden')){
23170             return true;
23171         }
23172         
23173         if(this.allowBlank){
23174             return true;
23175         }
23176         
23177         var valid = false;
23178         
23179         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23180             if(!e.dom.checked){
23181                 return;
23182             }
23183             
23184             valid = true;
23185             
23186             return false;
23187         });
23188         
23189         return valid;
23190     },
23191     
23192     validateCheckbox : function()
23193     {
23194         if(!this.groupId){
23195             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23196             //return (this.getValue() == this.inputValue) ? true : false;
23197         }
23198         
23199         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23200         
23201         if(!group){
23202             return false;
23203         }
23204         
23205         var r = false;
23206         
23207         for(var i in group){
23208             if(group[i].el.isVisible(true)){
23209                 r = false;
23210                 break;
23211             }
23212             
23213             r = true;
23214         }
23215         
23216         for(var i in group){
23217             if(r){
23218                 break;
23219             }
23220             
23221             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23222         }
23223         
23224         return r;
23225     },
23226     
23227     /**
23228      * Mark this field as valid
23229      */
23230     markValid : function()
23231     {
23232         var _this = this;
23233         
23234         this.fireEvent('valid', this);
23235         
23236         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23237         
23238         if(this.groupId){
23239             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23240         }
23241         
23242         if(label){
23243             label.markValid();
23244         }
23245
23246         if(this.inputType == 'radio'){
23247             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23248                 var fg = e.findParent('.form-group', false, true);
23249                 if (Roo.bootstrap.version == 3) {
23250                     fg.removeClass([_this.invalidClass, _this.validClass]);
23251                     fg.addClass(_this.validClass);
23252                 } else {
23253                     fg.removeClass(['is-valid', 'is-invalid']);
23254                     fg.addClass('is-valid');
23255                 }
23256             });
23257             
23258             return;
23259         }
23260
23261         if(!this.groupId){
23262             var fg = this.el.findParent('.form-group', false, true);
23263             if (Roo.bootstrap.version == 3) {
23264                 fg.removeClass([this.invalidClass, this.validClass]);
23265                 fg.addClass(this.validClass);
23266             } else {
23267                 fg.removeClass(['is-valid', 'is-invalid']);
23268                 fg.addClass('is-valid');
23269             }
23270             return;
23271         }
23272         
23273         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23274         
23275         if(!group){
23276             return;
23277         }
23278         
23279         for(var i in group){
23280             var fg = group[i].el.findParent('.form-group', false, true);
23281             if (Roo.bootstrap.version == 3) {
23282                 fg.removeClass([this.invalidClass, this.validClass]);
23283                 fg.addClass(this.validClass);
23284             } else {
23285                 fg.removeClass(['is-valid', 'is-invalid']);
23286                 fg.addClass('is-valid');
23287             }
23288         }
23289     },
23290     
23291      /**
23292      * Mark this field as invalid
23293      * @param {String} msg The validation message
23294      */
23295     markInvalid : function(msg)
23296     {
23297         if(this.allowBlank){
23298             return;
23299         }
23300         
23301         var _this = this;
23302         
23303         this.fireEvent('invalid', this, msg);
23304         
23305         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23306         
23307         if(this.groupId){
23308             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23309         }
23310         
23311         if(label){
23312             label.markInvalid();
23313         }
23314             
23315         if(this.inputType == 'radio'){
23316             
23317             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23318                 var fg = e.findParent('.form-group', false, true);
23319                 if (Roo.bootstrap.version == 3) {
23320                     fg.removeClass([_this.invalidClass, _this.validClass]);
23321                     fg.addClass(_this.invalidClass);
23322                 } else {
23323                     fg.removeClass(['is-invalid', 'is-valid']);
23324                     fg.addClass('is-invalid');
23325                 }
23326             });
23327             
23328             return;
23329         }
23330         
23331         if(!this.groupId){
23332             var fg = this.el.findParent('.form-group', false, true);
23333             if (Roo.bootstrap.version == 3) {
23334                 fg.removeClass([_this.invalidClass, _this.validClass]);
23335                 fg.addClass(_this.invalidClass);
23336             } else {
23337                 fg.removeClass(['is-invalid', 'is-valid']);
23338                 fg.addClass('is-invalid');
23339             }
23340             return;
23341         }
23342         
23343         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23344         
23345         if(!group){
23346             return;
23347         }
23348         
23349         for(var i in group){
23350             var fg = group[i].el.findParent('.form-group', false, true);
23351             if (Roo.bootstrap.version == 3) {
23352                 fg.removeClass([_this.invalidClass, _this.validClass]);
23353                 fg.addClass(_this.invalidClass);
23354             } else {
23355                 fg.removeClass(['is-invalid', 'is-valid']);
23356                 fg.addClass('is-invalid');
23357             }
23358         }
23359         
23360     },
23361     
23362     clearInvalid : function()
23363     {
23364         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23365         
23366         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23367         
23368         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23369         
23370         if (label && label.iconEl) {
23371             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23372             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23373         }
23374     },
23375     
23376     disable : function()
23377     {
23378         if(this.inputType != 'radio'){
23379             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23380             return;
23381         }
23382         
23383         var _this = this;
23384         
23385         if(this.rendered){
23386             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23387                 _this.getActionEl().addClass(this.disabledClass);
23388                 e.dom.disabled = true;
23389             });
23390         }
23391         
23392         this.disabled = true;
23393         this.fireEvent("disable", this);
23394         return this;
23395     },
23396
23397     enable : function()
23398     {
23399         if(this.inputType != 'radio'){
23400             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23401             return;
23402         }
23403         
23404         var _this = this;
23405         
23406         if(this.rendered){
23407             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23408                 _this.getActionEl().removeClass(this.disabledClass);
23409                 e.dom.disabled = false;
23410             });
23411         }
23412         
23413         this.disabled = false;
23414         this.fireEvent("enable", this);
23415         return this;
23416     },
23417     
23418     setBoxLabel : function(v)
23419     {
23420         this.boxLabel = v;
23421         
23422         if(this.rendered){
23423             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23424         }
23425     }
23426
23427 });
23428
23429 Roo.apply(Roo.bootstrap.CheckBox, {
23430     
23431     groups: {},
23432     
23433      /**
23434     * register a CheckBox Group
23435     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23436     */
23437     register : function(checkbox)
23438     {
23439         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23440             this.groups[checkbox.groupId] = {};
23441         }
23442         
23443         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23444             return;
23445         }
23446         
23447         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23448         
23449     },
23450     /**
23451     * fetch a CheckBox Group based on the group ID
23452     * @param {string} the group ID
23453     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23454     */
23455     get: function(groupId) {
23456         if (typeof(this.groups[groupId]) == 'undefined') {
23457             return false;
23458         }
23459         
23460         return this.groups[groupId] ;
23461     }
23462     
23463     
23464 });
23465 /*
23466  * - LGPL
23467  *
23468  * RadioItem
23469  * 
23470  */
23471
23472 /**
23473  * @class Roo.bootstrap.Radio
23474  * @extends Roo.bootstrap.Component
23475  * Bootstrap Radio class
23476  * @cfg {String} boxLabel - the label associated
23477  * @cfg {String} value - the value of radio
23478  * 
23479  * @constructor
23480  * Create a new Radio
23481  * @param {Object} config The config object
23482  */
23483 Roo.bootstrap.Radio = function(config){
23484     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23485     
23486 };
23487
23488 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23489     
23490     boxLabel : '',
23491     
23492     value : '',
23493     
23494     getAutoCreate : function()
23495     {
23496         var cfg = {
23497             tag : 'div',
23498             cls : 'form-group radio',
23499             cn : [
23500                 {
23501                     tag : 'label',
23502                     cls : 'box-label',
23503                     html : this.boxLabel
23504                 }
23505             ]
23506         };
23507         
23508         return cfg;
23509     },
23510     
23511     initEvents : function() 
23512     {
23513         this.parent().register(this);
23514         
23515         this.el.on('click', this.onClick, this);
23516         
23517     },
23518     
23519     onClick : function(e)
23520     {
23521         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23522             this.setChecked(true);
23523         }
23524     },
23525     
23526     setChecked : function(state, suppressEvent)
23527     {
23528         this.parent().setValue(this.value, suppressEvent);
23529         
23530     },
23531     
23532     setBoxLabel : function(v)
23533     {
23534         this.boxLabel = v;
23535         
23536         if(this.rendered){
23537             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23538         }
23539     }
23540     
23541 });
23542  
23543
23544  /*
23545  * - LGPL
23546  *
23547  * Input
23548  * 
23549  */
23550
23551 /**
23552  * @class Roo.bootstrap.SecurePass
23553  * @extends Roo.bootstrap.Input
23554  * Bootstrap SecurePass class
23555  *
23556  * 
23557  * @constructor
23558  * Create a new SecurePass
23559  * @param {Object} config The config object
23560  */
23561  
23562 Roo.bootstrap.SecurePass = function (config) {
23563     // these go here, so the translation tool can replace them..
23564     this.errors = {
23565         PwdEmpty: "Please type a password, and then retype it to confirm.",
23566         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23567         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23568         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23569         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23570         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23571         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23572         TooWeak: "Your password is Too Weak."
23573     },
23574     this.meterLabel = "Password strength:";
23575     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23576     this.meterClass = [
23577         "roo-password-meter-tooweak", 
23578         "roo-password-meter-weak", 
23579         "roo-password-meter-medium", 
23580         "roo-password-meter-strong", 
23581         "roo-password-meter-grey"
23582     ];
23583     
23584     this.errors = {};
23585     
23586     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23587 }
23588
23589 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23590     /**
23591      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23592      * {
23593      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23594      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23595      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23596      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23597      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23598      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23599      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23600      * })
23601      */
23602     // private
23603     
23604     meterWidth: 300,
23605     errorMsg :'',    
23606     errors: false,
23607     imageRoot: '/',
23608     /**
23609      * @cfg {String/Object} Label for the strength meter (defaults to
23610      * 'Password strength:')
23611      */
23612     // private
23613     meterLabel: '',
23614     /**
23615      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23616      * ['Weak', 'Medium', 'Strong'])
23617      */
23618     // private    
23619     pwdStrengths: false,    
23620     // private
23621     strength: 0,
23622     // private
23623     _lastPwd: null,
23624     // private
23625     kCapitalLetter: 0,
23626     kSmallLetter: 1,
23627     kDigit: 2,
23628     kPunctuation: 3,
23629     
23630     insecure: false,
23631     // private
23632     initEvents: function ()
23633     {
23634         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23635
23636         if (this.el.is('input[type=password]') && Roo.isSafari) {
23637             this.el.on('keydown', this.SafariOnKeyDown, this);
23638         }
23639
23640         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23641     },
23642     // private
23643     onRender: function (ct, position)
23644     {
23645         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23646         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23647         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23648
23649         this.trigger.createChild({
23650                    cn: [
23651                     {
23652                     //id: 'PwdMeter',
23653                     tag: 'div',
23654                     cls: 'roo-password-meter-grey col-xs-12',
23655                     style: {
23656                         //width: 0,
23657                         //width: this.meterWidth + 'px'                                                
23658                         }
23659                     },
23660                     {                            
23661                          cls: 'roo-password-meter-text'                          
23662                     }
23663                 ]            
23664         });
23665
23666          
23667         if (this.hideTrigger) {
23668             this.trigger.setDisplayed(false);
23669         }
23670         this.setSize(this.width || '', this.height || '');
23671     },
23672     // private
23673     onDestroy: function ()
23674     {
23675         if (this.trigger) {
23676             this.trigger.removeAllListeners();
23677             this.trigger.remove();
23678         }
23679         if (this.wrap) {
23680             this.wrap.remove();
23681         }
23682         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23683     },
23684     // private
23685     checkStrength: function ()
23686     {
23687         var pwd = this.inputEl().getValue();
23688         if (pwd == this._lastPwd) {
23689             return;
23690         }
23691
23692         var strength;
23693         if (this.ClientSideStrongPassword(pwd)) {
23694             strength = 3;
23695         } else if (this.ClientSideMediumPassword(pwd)) {
23696             strength = 2;
23697         } else if (this.ClientSideWeakPassword(pwd)) {
23698             strength = 1;
23699         } else {
23700             strength = 0;
23701         }
23702         
23703         Roo.log('strength1: ' + strength);
23704         
23705         //var pm = this.trigger.child('div/div/div').dom;
23706         var pm = this.trigger.child('div/div');
23707         pm.removeClass(this.meterClass);
23708         pm.addClass(this.meterClass[strength]);
23709                 
23710         
23711         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23712                 
23713         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23714         
23715         this._lastPwd = pwd;
23716     },
23717     reset: function ()
23718     {
23719         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23720         
23721         this._lastPwd = '';
23722         
23723         var pm = this.trigger.child('div/div');
23724         pm.removeClass(this.meterClass);
23725         pm.addClass('roo-password-meter-grey');        
23726         
23727         
23728         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23729         
23730         pt.innerHTML = '';
23731         this.inputEl().dom.type='password';
23732     },
23733     // private
23734     validateValue: function (value)
23735     {
23736         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23737             return false;
23738         }
23739         if (value.length == 0) {
23740             if (this.allowBlank) {
23741                 this.clearInvalid();
23742                 return true;
23743             }
23744
23745             this.markInvalid(this.errors.PwdEmpty);
23746             this.errorMsg = this.errors.PwdEmpty;
23747             return false;
23748         }
23749         
23750         if(this.insecure){
23751             return true;
23752         }
23753         
23754         if (!value.match(/[\x21-\x7e]+/)) {
23755             this.markInvalid(this.errors.PwdBadChar);
23756             this.errorMsg = this.errors.PwdBadChar;
23757             return false;
23758         }
23759         if (value.length < 6) {
23760             this.markInvalid(this.errors.PwdShort);
23761             this.errorMsg = this.errors.PwdShort;
23762             return false;
23763         }
23764         if (value.length > 16) {
23765             this.markInvalid(this.errors.PwdLong);
23766             this.errorMsg = this.errors.PwdLong;
23767             return false;
23768         }
23769         var strength;
23770         if (this.ClientSideStrongPassword(value)) {
23771             strength = 3;
23772         } else if (this.ClientSideMediumPassword(value)) {
23773             strength = 2;
23774         } else if (this.ClientSideWeakPassword(value)) {
23775             strength = 1;
23776         } else {
23777             strength = 0;
23778         }
23779
23780         
23781         if (strength < 2) {
23782             //this.markInvalid(this.errors.TooWeak);
23783             this.errorMsg = this.errors.TooWeak;
23784             //return false;
23785         }
23786         
23787         
23788         console.log('strength2: ' + strength);
23789         
23790         //var pm = this.trigger.child('div/div/div').dom;
23791         
23792         var pm = this.trigger.child('div/div');
23793         pm.removeClass(this.meterClass);
23794         pm.addClass(this.meterClass[strength]);
23795                 
23796         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23797                 
23798         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23799         
23800         this.errorMsg = ''; 
23801         return true;
23802     },
23803     // private
23804     CharacterSetChecks: function (type)
23805     {
23806         this.type = type;
23807         this.fResult = false;
23808     },
23809     // private
23810     isctype: function (character, type)
23811     {
23812         switch (type) {  
23813             case this.kCapitalLetter:
23814                 if (character >= 'A' && character <= 'Z') {
23815                     return true;
23816                 }
23817                 break;
23818             
23819             case this.kSmallLetter:
23820                 if (character >= 'a' && character <= 'z') {
23821                     return true;
23822                 }
23823                 break;
23824             
23825             case this.kDigit:
23826                 if (character >= '0' && character <= '9') {
23827                     return true;
23828                 }
23829                 break;
23830             
23831             case this.kPunctuation:
23832                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23833                     return true;
23834                 }
23835                 break;
23836             
23837             default:
23838                 return false;
23839         }
23840
23841     },
23842     // private
23843     IsLongEnough: function (pwd, size)
23844     {
23845         return !(pwd == null || isNaN(size) || pwd.length < size);
23846     },
23847     // private
23848     SpansEnoughCharacterSets: function (word, nb)
23849     {
23850         if (!this.IsLongEnough(word, nb))
23851         {
23852             return false;
23853         }
23854
23855         var characterSetChecks = new Array(
23856             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23857             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23858         );
23859         
23860         for (var index = 0; index < word.length; ++index) {
23861             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23862                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23863                     characterSetChecks[nCharSet].fResult = true;
23864                     break;
23865                 }
23866             }
23867         }
23868
23869         var nCharSets = 0;
23870         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23871             if (characterSetChecks[nCharSet].fResult) {
23872                 ++nCharSets;
23873             }
23874         }
23875
23876         if (nCharSets < nb) {
23877             return false;
23878         }
23879         return true;
23880     },
23881     // private
23882     ClientSideStrongPassword: function (pwd)
23883     {
23884         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23885     },
23886     // private
23887     ClientSideMediumPassword: function (pwd)
23888     {
23889         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23890     },
23891     // private
23892     ClientSideWeakPassword: function (pwd)
23893     {
23894         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23895     }
23896           
23897 })//<script type="text/javascript">
23898
23899 /*
23900  * Based  Ext JS Library 1.1.1
23901  * Copyright(c) 2006-2007, Ext JS, LLC.
23902  * LGPL
23903  *
23904  */
23905  
23906 /**
23907  * @class Roo.HtmlEditorCore
23908  * @extends Roo.Component
23909  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23910  *
23911  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23912  */
23913
23914 Roo.HtmlEditorCore = function(config){
23915     
23916     
23917     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23918     
23919     
23920     this.addEvents({
23921         /**
23922          * @event initialize
23923          * Fires when the editor is fully initialized (including the iframe)
23924          * @param {Roo.HtmlEditorCore} this
23925          */
23926         initialize: true,
23927         /**
23928          * @event activate
23929          * Fires when the editor is first receives the focus. Any insertion must wait
23930          * until after this event.
23931          * @param {Roo.HtmlEditorCore} this
23932          */
23933         activate: true,
23934          /**
23935          * @event beforesync
23936          * Fires before the textarea is updated with content from the editor iframe. Return false
23937          * to cancel the sync.
23938          * @param {Roo.HtmlEditorCore} this
23939          * @param {String} html
23940          */
23941         beforesync: true,
23942          /**
23943          * @event beforepush
23944          * Fires before the iframe editor is updated with content from the textarea. Return false
23945          * to cancel the push.
23946          * @param {Roo.HtmlEditorCore} this
23947          * @param {String} html
23948          */
23949         beforepush: true,
23950          /**
23951          * @event sync
23952          * Fires when the textarea is updated with content from the editor iframe.
23953          * @param {Roo.HtmlEditorCore} this
23954          * @param {String} html
23955          */
23956         sync: true,
23957          /**
23958          * @event push
23959          * Fires when the iframe editor is updated with content from the textarea.
23960          * @param {Roo.HtmlEditorCore} this
23961          * @param {String} html
23962          */
23963         push: true,
23964         
23965         /**
23966          * @event editorevent
23967          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23968          * @param {Roo.HtmlEditorCore} this
23969          */
23970         editorevent: true
23971         
23972     });
23973     
23974     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23975     
23976     // defaults : white / black...
23977     this.applyBlacklists();
23978     
23979     
23980     
23981 };
23982
23983
23984 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23985
23986
23987      /**
23988      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23989      */
23990     
23991     owner : false,
23992     
23993      /**
23994      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23995      *                        Roo.resizable.
23996      */
23997     resizable : false,
23998      /**
23999      * @cfg {Number} height (in pixels)
24000      */   
24001     height: 300,
24002    /**
24003      * @cfg {Number} width (in pixels)
24004      */   
24005     width: 500,
24006     
24007     /**
24008      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24009      * 
24010      */
24011     stylesheets: false,
24012     
24013     // id of frame..
24014     frameId: false,
24015     
24016     // private properties
24017     validationEvent : false,
24018     deferHeight: true,
24019     initialized : false,
24020     activated : false,
24021     sourceEditMode : false,
24022     onFocus : Roo.emptyFn,
24023     iframePad:3,
24024     hideMode:'offsets',
24025     
24026     clearUp: true,
24027     
24028     // blacklist + whitelisted elements..
24029     black: false,
24030     white: false,
24031      
24032     bodyCls : '',
24033
24034     /**
24035      * Protected method that will not generally be called directly. It
24036      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24037      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24038      */
24039     getDocMarkup : function(){
24040         // body styles..
24041         var st = '';
24042         
24043         // inherit styels from page...?? 
24044         if (this.stylesheets === false) {
24045             
24046             Roo.get(document.head).select('style').each(function(node) {
24047                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24048             });
24049             
24050             Roo.get(document.head).select('link').each(function(node) { 
24051                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24052             });
24053             
24054         } else if (!this.stylesheets.length) {
24055                 // simple..
24056                 st = '<style type="text/css">' +
24057                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24058                    '</style>';
24059         } else {
24060             for (var i in this.stylesheets) { 
24061                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24062             }
24063             
24064         }
24065         
24066         st +=  '<style type="text/css">' +
24067             'IMG { cursor: pointer } ' +
24068         '</style>';
24069
24070         var cls = 'roo-htmleditor-body';
24071         
24072         if(this.bodyCls.length){
24073             cls += ' ' + this.bodyCls;
24074         }
24075         
24076         return '<html><head>' + st  +
24077             //<style type="text/css">' +
24078             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24079             //'</style>' +
24080             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24081     },
24082
24083     // private
24084     onRender : function(ct, position)
24085     {
24086         var _t = this;
24087         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24088         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24089         
24090         
24091         this.el.dom.style.border = '0 none';
24092         this.el.dom.setAttribute('tabIndex', -1);
24093         this.el.addClass('x-hidden hide');
24094         
24095         
24096         
24097         if(Roo.isIE){ // fix IE 1px bogus margin
24098             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24099         }
24100        
24101         
24102         this.frameId = Roo.id();
24103         
24104          
24105         
24106         var iframe = this.owner.wrap.createChild({
24107             tag: 'iframe',
24108             cls: 'form-control', // bootstrap..
24109             id: this.frameId,
24110             name: this.frameId,
24111             frameBorder : 'no',
24112             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24113         }, this.el
24114         );
24115         
24116         
24117         this.iframe = iframe.dom;
24118
24119          this.assignDocWin();
24120         
24121         this.doc.designMode = 'on';
24122        
24123         this.doc.open();
24124         this.doc.write(this.getDocMarkup());
24125         this.doc.close();
24126
24127         
24128         var task = { // must defer to wait for browser to be ready
24129             run : function(){
24130                 //console.log("run task?" + this.doc.readyState);
24131                 this.assignDocWin();
24132                 if(this.doc.body || this.doc.readyState == 'complete'){
24133                     try {
24134                         this.doc.designMode="on";
24135                     } catch (e) {
24136                         return;
24137                     }
24138                     Roo.TaskMgr.stop(task);
24139                     this.initEditor.defer(10, this);
24140                 }
24141             },
24142             interval : 10,
24143             duration: 10000,
24144             scope: this
24145         };
24146         Roo.TaskMgr.start(task);
24147
24148     },
24149
24150     // private
24151     onResize : function(w, h)
24152     {
24153          Roo.log('resize: ' +w + ',' + h );
24154         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24155         if(!this.iframe){
24156             return;
24157         }
24158         if(typeof w == 'number'){
24159             
24160             this.iframe.style.width = w + 'px';
24161         }
24162         if(typeof h == 'number'){
24163             
24164             this.iframe.style.height = h + 'px';
24165             if(this.doc){
24166                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24167             }
24168         }
24169         
24170     },
24171
24172     /**
24173      * Toggles the editor between standard and source edit mode.
24174      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24175      */
24176     toggleSourceEdit : function(sourceEditMode){
24177         
24178         this.sourceEditMode = sourceEditMode === true;
24179         
24180         if(this.sourceEditMode){
24181  
24182             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24183             
24184         }else{
24185             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24186             //this.iframe.className = '';
24187             this.deferFocus();
24188         }
24189         //this.setSize(this.owner.wrap.getSize());
24190         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24191     },
24192
24193     
24194   
24195
24196     /**
24197      * Protected method that will not generally be called directly. If you need/want
24198      * custom HTML cleanup, this is the method you should override.
24199      * @param {String} html The HTML to be cleaned
24200      * return {String} The cleaned HTML
24201      */
24202     cleanHtml : function(html){
24203         html = String(html);
24204         if(html.length > 5){
24205             if(Roo.isSafari){ // strip safari nonsense
24206                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24207             }
24208         }
24209         if(html == '&nbsp;'){
24210             html = '';
24211         }
24212         return html;
24213     },
24214
24215     /**
24216      * HTML Editor -> Textarea
24217      * Protected method that will not generally be called directly. Syncs the contents
24218      * of the editor iframe with the textarea.
24219      */
24220     syncValue : function(){
24221         if(this.initialized){
24222             var bd = (this.doc.body || this.doc.documentElement);
24223             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24224             var html = bd.innerHTML;
24225             if(Roo.isSafari){
24226                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24227                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24228                 if(m && m[1]){
24229                     html = '<div style="'+m[0]+'">' + html + '</div>';
24230                 }
24231             }
24232             html = this.cleanHtml(html);
24233             // fix up the special chars.. normaly like back quotes in word...
24234             // however we do not want to do this with chinese..
24235             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24236                 
24237                 var cc = match.charCodeAt();
24238
24239                 // Get the character value, handling surrogate pairs
24240                 if (match.length == 2) {
24241                     // It's a surrogate pair, calculate the Unicode code point
24242                     var high = match.charCodeAt(0) - 0xD800;
24243                     var low  = match.charCodeAt(1) - 0xDC00;
24244                     cc = (high * 0x400) + low + 0x10000;
24245                 }  else if (
24246                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24247                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24248                     (cc >= 0xf900 && cc < 0xfb00 )
24249                 ) {
24250                         return match;
24251                 }  
24252          
24253                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24254                 return "&#" + cc + ";";
24255                 
24256                 
24257             });
24258             
24259             
24260              
24261             if(this.owner.fireEvent('beforesync', this, html) !== false){
24262                 this.el.dom.value = html;
24263                 this.owner.fireEvent('sync', this, html);
24264             }
24265         }
24266     },
24267
24268     /**
24269      * Protected method that will not generally be called directly. Pushes the value of the textarea
24270      * into the iframe editor.
24271      */
24272     pushValue : function(){
24273         if(this.initialized){
24274             var v = this.el.dom.value.trim();
24275             
24276 //            if(v.length < 1){
24277 //                v = '&#160;';
24278 //            }
24279             
24280             if(this.owner.fireEvent('beforepush', this, v) !== false){
24281                 var d = (this.doc.body || this.doc.documentElement);
24282                 d.innerHTML = v;
24283                 this.cleanUpPaste();
24284                 this.el.dom.value = d.innerHTML;
24285                 this.owner.fireEvent('push', this, v);
24286             }
24287         }
24288     },
24289
24290     // private
24291     deferFocus : function(){
24292         this.focus.defer(10, this);
24293     },
24294
24295     // doc'ed in Field
24296     focus : function(){
24297         if(this.win && !this.sourceEditMode){
24298             this.win.focus();
24299         }else{
24300             this.el.focus();
24301         }
24302     },
24303     
24304     assignDocWin: function()
24305     {
24306         var iframe = this.iframe;
24307         
24308          if(Roo.isIE){
24309             this.doc = iframe.contentWindow.document;
24310             this.win = iframe.contentWindow;
24311         } else {
24312 //            if (!Roo.get(this.frameId)) {
24313 //                return;
24314 //            }
24315 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24316 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24317             
24318             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24319                 return;
24320             }
24321             
24322             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24323             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24324         }
24325     },
24326     
24327     // private
24328     initEditor : function(){
24329         //console.log("INIT EDITOR");
24330         this.assignDocWin();
24331         
24332         
24333         
24334         this.doc.designMode="on";
24335         this.doc.open();
24336         this.doc.write(this.getDocMarkup());
24337         this.doc.close();
24338         
24339         var dbody = (this.doc.body || this.doc.documentElement);
24340         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24341         // this copies styles from the containing element into thsi one..
24342         // not sure why we need all of this..
24343         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24344         
24345         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24346         //ss['background-attachment'] = 'fixed'; // w3c
24347         dbody.bgProperties = 'fixed'; // ie
24348         //Roo.DomHelper.applyStyles(dbody, ss);
24349         Roo.EventManager.on(this.doc, {
24350             //'mousedown': this.onEditorEvent,
24351             'mouseup': this.onEditorEvent,
24352             'dblclick': this.onEditorEvent,
24353             'click': this.onEditorEvent,
24354             'keyup': this.onEditorEvent,
24355             buffer:100,
24356             scope: this
24357         });
24358         if(Roo.isGecko){
24359             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24360         }
24361         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24362             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24363         }
24364         this.initialized = true;
24365
24366         this.owner.fireEvent('initialize', this);
24367         this.pushValue();
24368     },
24369
24370     // private
24371     onDestroy : function(){
24372         
24373         
24374         
24375         if(this.rendered){
24376             
24377             //for (var i =0; i < this.toolbars.length;i++) {
24378             //    // fixme - ask toolbars for heights?
24379             //    this.toolbars[i].onDestroy();
24380            // }
24381             
24382             //this.wrap.dom.innerHTML = '';
24383             //this.wrap.remove();
24384         }
24385     },
24386
24387     // private
24388     onFirstFocus : function(){
24389         
24390         this.assignDocWin();
24391         
24392         
24393         this.activated = true;
24394          
24395     
24396         if(Roo.isGecko){ // prevent silly gecko errors
24397             this.win.focus();
24398             var s = this.win.getSelection();
24399             if(!s.focusNode || s.focusNode.nodeType != 3){
24400                 var r = s.getRangeAt(0);
24401                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24402                 r.collapse(true);
24403                 this.deferFocus();
24404             }
24405             try{
24406                 this.execCmd('useCSS', true);
24407                 this.execCmd('styleWithCSS', false);
24408             }catch(e){}
24409         }
24410         this.owner.fireEvent('activate', this);
24411     },
24412
24413     // private
24414     adjustFont: function(btn){
24415         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24416         //if(Roo.isSafari){ // safari
24417         //    adjust *= 2;
24418        // }
24419         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24420         if(Roo.isSafari){ // safari
24421             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24422             v =  (v < 10) ? 10 : v;
24423             v =  (v > 48) ? 48 : v;
24424             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24425             
24426         }
24427         
24428         
24429         v = Math.max(1, v+adjust);
24430         
24431         this.execCmd('FontSize', v  );
24432     },
24433
24434     onEditorEvent : function(e)
24435     {
24436         this.owner.fireEvent('editorevent', this, e);
24437       //  this.updateToolbar();
24438         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24439     },
24440
24441     insertTag : function(tg)
24442     {
24443         // could be a bit smarter... -> wrap the current selected tRoo..
24444         if (tg.toLowerCase() == 'span' ||
24445             tg.toLowerCase() == 'code' ||
24446             tg.toLowerCase() == 'sup' ||
24447             tg.toLowerCase() == 'sub' 
24448             ) {
24449             
24450             range = this.createRange(this.getSelection());
24451             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24452             wrappingNode.appendChild(range.extractContents());
24453             range.insertNode(wrappingNode);
24454
24455             return;
24456             
24457             
24458             
24459         }
24460         this.execCmd("formatblock",   tg);
24461         
24462     },
24463     
24464     insertText : function(txt)
24465     {
24466         
24467         
24468         var range = this.createRange();
24469         range.deleteContents();
24470                //alert(Sender.getAttribute('label'));
24471                
24472         range.insertNode(this.doc.createTextNode(txt));
24473     } ,
24474     
24475      
24476
24477     /**
24478      * Executes a Midas editor command on the editor document and performs necessary focus and
24479      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24480      * @param {String} cmd The Midas command
24481      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24482      */
24483     relayCmd : function(cmd, value){
24484         this.win.focus();
24485         this.execCmd(cmd, value);
24486         this.owner.fireEvent('editorevent', this);
24487         //this.updateToolbar();
24488         this.owner.deferFocus();
24489     },
24490
24491     /**
24492      * Executes a Midas editor command directly on the editor document.
24493      * For visual commands, you should use {@link #relayCmd} instead.
24494      * <b>This should only be called after the editor is initialized.</b>
24495      * @param {String} cmd The Midas command
24496      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24497      */
24498     execCmd : function(cmd, value){
24499         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24500         this.syncValue();
24501     },
24502  
24503  
24504    
24505     /**
24506      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24507      * to insert tRoo.
24508      * @param {String} text | dom node.. 
24509      */
24510     insertAtCursor : function(text)
24511     {
24512         
24513         if(!this.activated){
24514             return;
24515         }
24516         /*
24517         if(Roo.isIE){
24518             this.win.focus();
24519             var r = this.doc.selection.createRange();
24520             if(r){
24521                 r.collapse(true);
24522                 r.pasteHTML(text);
24523                 this.syncValue();
24524                 this.deferFocus();
24525             
24526             }
24527             return;
24528         }
24529         */
24530         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24531             this.win.focus();
24532             
24533             
24534             // from jquery ui (MIT licenced)
24535             var range, node;
24536             var win = this.win;
24537             
24538             if (win.getSelection && win.getSelection().getRangeAt) {
24539                 range = win.getSelection().getRangeAt(0);
24540                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24541                 range.insertNode(node);
24542             } else if (win.document.selection && win.document.selection.createRange) {
24543                 // no firefox support
24544                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24545                 win.document.selection.createRange().pasteHTML(txt);
24546             } else {
24547                 // no firefox support
24548                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24549                 this.execCmd('InsertHTML', txt);
24550             } 
24551             
24552             this.syncValue();
24553             
24554             this.deferFocus();
24555         }
24556     },
24557  // private
24558     mozKeyPress : function(e){
24559         if(e.ctrlKey){
24560             var c = e.getCharCode(), cmd;
24561           
24562             if(c > 0){
24563                 c = String.fromCharCode(c).toLowerCase();
24564                 switch(c){
24565                     case 'b':
24566                         cmd = 'bold';
24567                         break;
24568                     case 'i':
24569                         cmd = 'italic';
24570                         break;
24571                     
24572                     case 'u':
24573                         cmd = 'underline';
24574                         break;
24575                     
24576                     case 'v':
24577                         this.cleanUpPaste.defer(100, this);
24578                         return;
24579                         
24580                 }
24581                 if(cmd){
24582                     this.win.focus();
24583                     this.execCmd(cmd);
24584                     this.deferFocus();
24585                     e.preventDefault();
24586                 }
24587                 
24588             }
24589         }
24590     },
24591
24592     // private
24593     fixKeys : function(){ // load time branching for fastest keydown performance
24594         if(Roo.isIE){
24595             return function(e){
24596                 var k = e.getKey(), r;
24597                 if(k == e.TAB){
24598                     e.stopEvent();
24599                     r = this.doc.selection.createRange();
24600                     if(r){
24601                         r.collapse(true);
24602                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24603                         this.deferFocus();
24604                     }
24605                     return;
24606                 }
24607                 
24608                 if(k == e.ENTER){
24609                     r = this.doc.selection.createRange();
24610                     if(r){
24611                         var target = r.parentElement();
24612                         if(!target || target.tagName.toLowerCase() != 'li'){
24613                             e.stopEvent();
24614                             r.pasteHTML('<br />');
24615                             r.collapse(false);
24616                             r.select();
24617                         }
24618                     }
24619                 }
24620                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24621                     this.cleanUpPaste.defer(100, this);
24622                     return;
24623                 }
24624                 
24625                 
24626             };
24627         }else if(Roo.isOpera){
24628             return function(e){
24629                 var k = e.getKey();
24630                 if(k == e.TAB){
24631                     e.stopEvent();
24632                     this.win.focus();
24633                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24634                     this.deferFocus();
24635                 }
24636                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24637                     this.cleanUpPaste.defer(100, this);
24638                     return;
24639                 }
24640                 
24641             };
24642         }else if(Roo.isSafari){
24643             return function(e){
24644                 var k = e.getKey();
24645                 
24646                 if(k == e.TAB){
24647                     e.stopEvent();
24648                     this.execCmd('InsertText','\t');
24649                     this.deferFocus();
24650                     return;
24651                 }
24652                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24653                     this.cleanUpPaste.defer(100, this);
24654                     return;
24655                 }
24656                 
24657              };
24658         }
24659     }(),
24660     
24661     getAllAncestors: function()
24662     {
24663         var p = this.getSelectedNode();
24664         var a = [];
24665         if (!p) {
24666             a.push(p); // push blank onto stack..
24667             p = this.getParentElement();
24668         }
24669         
24670         
24671         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24672             a.push(p);
24673             p = p.parentNode;
24674         }
24675         a.push(this.doc.body);
24676         return a;
24677     },
24678     lastSel : false,
24679     lastSelNode : false,
24680     
24681     
24682     getSelection : function() 
24683     {
24684         this.assignDocWin();
24685         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24686     },
24687     
24688     getSelectedNode: function() 
24689     {
24690         // this may only work on Gecko!!!
24691         
24692         // should we cache this!!!!
24693         
24694         
24695         
24696          
24697         var range = this.createRange(this.getSelection()).cloneRange();
24698         
24699         if (Roo.isIE) {
24700             var parent = range.parentElement();
24701             while (true) {
24702                 var testRange = range.duplicate();
24703                 testRange.moveToElementText(parent);
24704                 if (testRange.inRange(range)) {
24705                     break;
24706                 }
24707                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24708                     break;
24709                 }
24710                 parent = parent.parentElement;
24711             }
24712             return parent;
24713         }
24714         
24715         // is ancestor a text element.
24716         var ac =  range.commonAncestorContainer;
24717         if (ac.nodeType == 3) {
24718             ac = ac.parentNode;
24719         }
24720         
24721         var ar = ac.childNodes;
24722          
24723         var nodes = [];
24724         var other_nodes = [];
24725         var has_other_nodes = false;
24726         for (var i=0;i<ar.length;i++) {
24727             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24728                 continue;
24729             }
24730             // fullly contained node.
24731             
24732             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24733                 nodes.push(ar[i]);
24734                 continue;
24735             }
24736             
24737             // probably selected..
24738             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24739                 other_nodes.push(ar[i]);
24740                 continue;
24741             }
24742             // outer..
24743             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24744                 continue;
24745             }
24746             
24747             
24748             has_other_nodes = true;
24749         }
24750         if (!nodes.length && other_nodes.length) {
24751             nodes= other_nodes;
24752         }
24753         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24754             return false;
24755         }
24756         
24757         return nodes[0];
24758     },
24759     createRange: function(sel)
24760     {
24761         // this has strange effects when using with 
24762         // top toolbar - not sure if it's a great idea.
24763         //this.editor.contentWindow.focus();
24764         if (typeof sel != "undefined") {
24765             try {
24766                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24767             } catch(e) {
24768                 return this.doc.createRange();
24769             }
24770         } else {
24771             return this.doc.createRange();
24772         }
24773     },
24774     getParentElement: function()
24775     {
24776         
24777         this.assignDocWin();
24778         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24779         
24780         var range = this.createRange(sel);
24781          
24782         try {
24783             var p = range.commonAncestorContainer;
24784             while (p.nodeType == 3) { // text node
24785                 p = p.parentNode;
24786             }
24787             return p;
24788         } catch (e) {
24789             return null;
24790         }
24791     
24792     },
24793     /***
24794      *
24795      * Range intersection.. the hard stuff...
24796      *  '-1' = before
24797      *  '0' = hits..
24798      *  '1' = after.
24799      *         [ -- selected range --- ]
24800      *   [fail]                        [fail]
24801      *
24802      *    basically..
24803      *      if end is before start or  hits it. fail.
24804      *      if start is after end or hits it fail.
24805      *
24806      *   if either hits (but other is outside. - then it's not 
24807      *   
24808      *    
24809      **/
24810     
24811     
24812     // @see http://www.thismuchiknow.co.uk/?p=64.
24813     rangeIntersectsNode : function(range, node)
24814     {
24815         var nodeRange = node.ownerDocument.createRange();
24816         try {
24817             nodeRange.selectNode(node);
24818         } catch (e) {
24819             nodeRange.selectNodeContents(node);
24820         }
24821     
24822         var rangeStartRange = range.cloneRange();
24823         rangeStartRange.collapse(true);
24824     
24825         var rangeEndRange = range.cloneRange();
24826         rangeEndRange.collapse(false);
24827     
24828         var nodeStartRange = nodeRange.cloneRange();
24829         nodeStartRange.collapse(true);
24830     
24831         var nodeEndRange = nodeRange.cloneRange();
24832         nodeEndRange.collapse(false);
24833     
24834         return rangeStartRange.compareBoundaryPoints(
24835                  Range.START_TO_START, nodeEndRange) == -1 &&
24836                rangeEndRange.compareBoundaryPoints(
24837                  Range.START_TO_START, nodeStartRange) == 1;
24838         
24839          
24840     },
24841     rangeCompareNode : function(range, node)
24842     {
24843         var nodeRange = node.ownerDocument.createRange();
24844         try {
24845             nodeRange.selectNode(node);
24846         } catch (e) {
24847             nodeRange.selectNodeContents(node);
24848         }
24849         
24850         
24851         range.collapse(true);
24852     
24853         nodeRange.collapse(true);
24854      
24855         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24856         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24857          
24858         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24859         
24860         var nodeIsBefore   =  ss == 1;
24861         var nodeIsAfter    = ee == -1;
24862         
24863         if (nodeIsBefore && nodeIsAfter) {
24864             return 0; // outer
24865         }
24866         if (!nodeIsBefore && nodeIsAfter) {
24867             return 1; //right trailed.
24868         }
24869         
24870         if (nodeIsBefore && !nodeIsAfter) {
24871             return 2;  // left trailed.
24872         }
24873         // fully contined.
24874         return 3;
24875     },
24876
24877     // private? - in a new class?
24878     cleanUpPaste :  function()
24879     {
24880         // cleans up the whole document..
24881         Roo.log('cleanuppaste');
24882         
24883         this.cleanUpChildren(this.doc.body);
24884         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24885         if (clean != this.doc.body.innerHTML) {
24886             this.doc.body.innerHTML = clean;
24887         }
24888         
24889     },
24890     
24891     cleanWordChars : function(input) {// change the chars to hex code
24892         var he = Roo.HtmlEditorCore;
24893         
24894         var output = input;
24895         Roo.each(he.swapCodes, function(sw) { 
24896             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24897             
24898             output = output.replace(swapper, sw[1]);
24899         });
24900         
24901         return output;
24902     },
24903     
24904     
24905     cleanUpChildren : function (n)
24906     {
24907         if (!n.childNodes.length) {
24908             return;
24909         }
24910         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24911            this.cleanUpChild(n.childNodes[i]);
24912         }
24913     },
24914     
24915     
24916         
24917     
24918     cleanUpChild : function (node)
24919     {
24920         var ed = this;
24921         //console.log(node);
24922         if (node.nodeName == "#text") {
24923             // clean up silly Windows -- stuff?
24924             return; 
24925         }
24926         if (node.nodeName == "#comment") {
24927             node.parentNode.removeChild(node);
24928             // clean up silly Windows -- stuff?
24929             return; 
24930         }
24931         var lcname = node.tagName.toLowerCase();
24932         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24933         // whitelist of tags..
24934         
24935         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24936             // remove node.
24937             node.parentNode.removeChild(node);
24938             return;
24939             
24940         }
24941         
24942         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24943         
24944         // spans with no attributes - just remove them..
24945         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24946             remove_keep_children = true;
24947         }
24948         
24949         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24950         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24951         
24952         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24953         //    remove_keep_children = true;
24954         //}
24955         
24956         if (remove_keep_children) {
24957             this.cleanUpChildren(node);
24958             // inserts everything just before this node...
24959             while (node.childNodes.length) {
24960                 var cn = node.childNodes[0];
24961                 node.removeChild(cn);
24962                 node.parentNode.insertBefore(cn, node);
24963             }
24964             node.parentNode.removeChild(node);
24965             return;
24966         }
24967         
24968         if (!node.attributes || !node.attributes.length) {
24969             
24970           
24971             
24972             
24973             this.cleanUpChildren(node);
24974             return;
24975         }
24976         
24977         function cleanAttr(n,v)
24978         {
24979             
24980             if (v.match(/^\./) || v.match(/^\//)) {
24981                 return;
24982             }
24983             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24984                 return;
24985             }
24986             if (v.match(/^#/)) {
24987                 return;
24988             }
24989             if (v.match(/^\{/)) { // allow template editing.
24990                 return;
24991             }
24992 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24993             node.removeAttribute(n);
24994             
24995         }
24996         
24997         var cwhite = this.cwhite;
24998         var cblack = this.cblack;
24999             
25000         function cleanStyle(n,v)
25001         {
25002             if (v.match(/expression/)) { //XSS?? should we even bother..
25003                 node.removeAttribute(n);
25004                 return;
25005             }
25006             
25007             var parts = v.split(/;/);
25008             var clean = [];
25009             
25010             Roo.each(parts, function(p) {
25011                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25012                 if (!p.length) {
25013                     return true;
25014                 }
25015                 var l = p.split(':').shift().replace(/\s+/g,'');
25016                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25017                 
25018                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25019 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25020                     //node.removeAttribute(n);
25021                     return true;
25022                 }
25023                 //Roo.log()
25024                 // only allow 'c whitelisted system attributes'
25025                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25026 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25027                     //node.removeAttribute(n);
25028                     return true;
25029                 }
25030                 
25031                 
25032                  
25033                 
25034                 clean.push(p);
25035                 return true;
25036             });
25037             if (clean.length) { 
25038                 node.setAttribute(n, clean.join(';'));
25039             } else {
25040                 node.removeAttribute(n);
25041             }
25042             
25043         }
25044         
25045         
25046         for (var i = node.attributes.length-1; i > -1 ; i--) {
25047             var a = node.attributes[i];
25048             //console.log(a);
25049             
25050             if (a.name.toLowerCase().substr(0,2)=='on')  {
25051                 node.removeAttribute(a.name);
25052                 continue;
25053             }
25054             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25055                 node.removeAttribute(a.name);
25056                 continue;
25057             }
25058             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25059                 cleanAttr(a.name,a.value); // fixme..
25060                 continue;
25061             }
25062             if (a.name == 'style') {
25063                 cleanStyle(a.name,a.value);
25064                 continue;
25065             }
25066             /// clean up MS crap..
25067             // tecnically this should be a list of valid class'es..
25068             
25069             
25070             if (a.name == 'class') {
25071                 if (a.value.match(/^Mso/)) {
25072                     node.removeAttribute('class');
25073                 }
25074                 
25075                 if (a.value.match(/^body$/)) {
25076                     node.removeAttribute('class');
25077                 }
25078                 continue;
25079             }
25080             
25081             // style cleanup!?
25082             // class cleanup?
25083             
25084         }
25085         
25086         
25087         this.cleanUpChildren(node);
25088         
25089         
25090     },
25091     
25092     /**
25093      * Clean up MS wordisms...
25094      */
25095     cleanWord : function(node)
25096     {
25097         if (!node) {
25098             this.cleanWord(this.doc.body);
25099             return;
25100         }
25101         
25102         if(
25103                 node.nodeName == 'SPAN' &&
25104                 !node.hasAttributes() &&
25105                 node.childNodes.length == 1 &&
25106                 node.firstChild.nodeName == "#text"  
25107         ) {
25108             var textNode = node.firstChild;
25109             node.removeChild(textNode);
25110             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25111                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25112             }
25113             node.parentNode.insertBefore(textNode, node);
25114             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25115                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25116             }
25117             node.parentNode.removeChild(node);
25118         }
25119         
25120         if (node.nodeName == "#text") {
25121             // clean up silly Windows -- stuff?
25122             return; 
25123         }
25124         if (node.nodeName == "#comment") {
25125             node.parentNode.removeChild(node);
25126             // clean up silly Windows -- stuff?
25127             return; 
25128         }
25129         
25130         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25131             node.parentNode.removeChild(node);
25132             return;
25133         }
25134         //Roo.log(node.tagName);
25135         // remove - but keep children..
25136         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25137             //Roo.log('-- removed');
25138             while (node.childNodes.length) {
25139                 var cn = node.childNodes[0];
25140                 node.removeChild(cn);
25141                 node.parentNode.insertBefore(cn, node);
25142                 // move node to parent - and clean it..
25143                 this.cleanWord(cn);
25144             }
25145             node.parentNode.removeChild(node);
25146             /// no need to iterate chidlren = it's got none..
25147             //this.iterateChildren(node, this.cleanWord);
25148             return;
25149         }
25150         // clean styles
25151         if (node.className.length) {
25152             
25153             var cn = node.className.split(/\W+/);
25154             var cna = [];
25155             Roo.each(cn, function(cls) {
25156                 if (cls.match(/Mso[a-zA-Z]+/)) {
25157                     return;
25158                 }
25159                 cna.push(cls);
25160             });
25161             node.className = cna.length ? cna.join(' ') : '';
25162             if (!cna.length) {
25163                 node.removeAttribute("class");
25164             }
25165         }
25166         
25167         if (node.hasAttribute("lang")) {
25168             node.removeAttribute("lang");
25169         }
25170         
25171         if (node.hasAttribute("style")) {
25172             
25173             var styles = node.getAttribute("style").split(";");
25174             var nstyle = [];
25175             Roo.each(styles, function(s) {
25176                 if (!s.match(/:/)) {
25177                     return;
25178                 }
25179                 var kv = s.split(":");
25180                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25181                     return;
25182                 }
25183                 // what ever is left... we allow.
25184                 nstyle.push(s);
25185             });
25186             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25187             if (!nstyle.length) {
25188                 node.removeAttribute('style');
25189             }
25190         }
25191         this.iterateChildren(node, this.cleanWord);
25192         
25193         
25194         
25195     },
25196     /**
25197      * iterateChildren of a Node, calling fn each time, using this as the scole..
25198      * @param {DomNode} node node to iterate children of.
25199      * @param {Function} fn method of this class to call on each item.
25200      */
25201     iterateChildren : function(node, fn)
25202     {
25203         if (!node.childNodes.length) {
25204                 return;
25205         }
25206         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25207            fn.call(this, node.childNodes[i])
25208         }
25209     },
25210     
25211     
25212     /**
25213      * cleanTableWidths.
25214      *
25215      * Quite often pasting from word etc.. results in tables with column and widths.
25216      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25217      *
25218      */
25219     cleanTableWidths : function(node)
25220     {
25221          
25222          
25223         if (!node) {
25224             this.cleanTableWidths(this.doc.body);
25225             return;
25226         }
25227         
25228         // ignore list...
25229         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25230             return; 
25231         }
25232         Roo.log(node.tagName);
25233         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25234             this.iterateChildren(node, this.cleanTableWidths);
25235             return;
25236         }
25237         if (node.hasAttribute('width')) {
25238             node.removeAttribute('width');
25239         }
25240         
25241          
25242         if (node.hasAttribute("style")) {
25243             // pretty basic...
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(/^\s*(width|min-width)\s*$/)) {
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         
25264         this.iterateChildren(node, this.cleanTableWidths);
25265         
25266         
25267     },
25268     
25269     
25270     
25271     
25272     domToHTML : function(currentElement, depth, nopadtext) {
25273         
25274         depth = depth || 0;
25275         nopadtext = nopadtext || false;
25276     
25277         if (!currentElement) {
25278             return this.domToHTML(this.doc.body);
25279         }
25280         
25281         //Roo.log(currentElement);
25282         var j;
25283         var allText = false;
25284         var nodeName = currentElement.nodeName;
25285         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25286         
25287         if  (nodeName == '#text') {
25288             
25289             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25290         }
25291         
25292         
25293         var ret = '';
25294         if (nodeName != 'BODY') {
25295              
25296             var i = 0;
25297             // Prints the node tagName, such as <A>, <IMG>, etc
25298             if (tagName) {
25299                 var attr = [];
25300                 for(i = 0; i < currentElement.attributes.length;i++) {
25301                     // quoting?
25302                     var aname = currentElement.attributes.item(i).name;
25303                     if (!currentElement.attributes.item(i).value.length) {
25304                         continue;
25305                     }
25306                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25307                 }
25308                 
25309                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25310             } 
25311             else {
25312                 
25313                 // eack
25314             }
25315         } else {
25316             tagName = false;
25317         }
25318         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25319             return ret;
25320         }
25321         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25322             nopadtext = true;
25323         }
25324         
25325         
25326         // Traverse the tree
25327         i = 0;
25328         var currentElementChild = currentElement.childNodes.item(i);
25329         var allText = true;
25330         var innerHTML  = '';
25331         lastnode = '';
25332         while (currentElementChild) {
25333             // Formatting code (indent the tree so it looks nice on the screen)
25334             var nopad = nopadtext;
25335             if (lastnode == 'SPAN') {
25336                 nopad  = true;
25337             }
25338             // text
25339             if  (currentElementChild.nodeName == '#text') {
25340                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25341                 toadd = nopadtext ? toadd : toadd.trim();
25342                 if (!nopad && toadd.length > 80) {
25343                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25344                 }
25345                 innerHTML  += toadd;
25346                 
25347                 i++;
25348                 currentElementChild = currentElement.childNodes.item(i);
25349                 lastNode = '';
25350                 continue;
25351             }
25352             allText = false;
25353             
25354             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25355                 
25356             // Recursively traverse the tree structure of the child node
25357             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25358             lastnode = currentElementChild.nodeName;
25359             i++;
25360             currentElementChild=currentElement.childNodes.item(i);
25361         }
25362         
25363         ret += innerHTML;
25364         
25365         if (!allText) {
25366                 // The remaining code is mostly for formatting the tree
25367             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25368         }
25369         
25370         
25371         if (tagName) {
25372             ret+= "</"+tagName+">";
25373         }
25374         return ret;
25375         
25376     },
25377         
25378     applyBlacklists : function()
25379     {
25380         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25381         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25382         
25383         this.white = [];
25384         this.black = [];
25385         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25386             if (b.indexOf(tag) > -1) {
25387                 return;
25388             }
25389             this.white.push(tag);
25390             
25391         }, this);
25392         
25393         Roo.each(w, function(tag) {
25394             if (b.indexOf(tag) > -1) {
25395                 return;
25396             }
25397             if (this.white.indexOf(tag) > -1) {
25398                 return;
25399             }
25400             this.white.push(tag);
25401             
25402         }, this);
25403         
25404         
25405         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25406             if (w.indexOf(tag) > -1) {
25407                 return;
25408             }
25409             this.black.push(tag);
25410             
25411         }, this);
25412         
25413         Roo.each(b, function(tag) {
25414             if (w.indexOf(tag) > -1) {
25415                 return;
25416             }
25417             if (this.black.indexOf(tag) > -1) {
25418                 return;
25419             }
25420             this.black.push(tag);
25421             
25422         }, this);
25423         
25424         
25425         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25426         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25427         
25428         this.cwhite = [];
25429         this.cblack = [];
25430         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25431             if (b.indexOf(tag) > -1) {
25432                 return;
25433             }
25434             this.cwhite.push(tag);
25435             
25436         }, this);
25437         
25438         Roo.each(w, function(tag) {
25439             if (b.indexOf(tag) > -1) {
25440                 return;
25441             }
25442             if (this.cwhite.indexOf(tag) > -1) {
25443                 return;
25444             }
25445             this.cwhite.push(tag);
25446             
25447         }, this);
25448         
25449         
25450         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25451             if (w.indexOf(tag) > -1) {
25452                 return;
25453             }
25454             this.cblack.push(tag);
25455             
25456         }, this);
25457         
25458         Roo.each(b, function(tag) {
25459             if (w.indexOf(tag) > -1) {
25460                 return;
25461             }
25462             if (this.cblack.indexOf(tag) > -1) {
25463                 return;
25464             }
25465             this.cblack.push(tag);
25466             
25467         }, this);
25468     },
25469     
25470     setStylesheets : function(stylesheets)
25471     {
25472         if(typeof(stylesheets) == 'string'){
25473             Roo.get(this.iframe.contentDocument.head).createChild({
25474                 tag : 'link',
25475                 rel : 'stylesheet',
25476                 type : 'text/css',
25477                 href : stylesheets
25478             });
25479             
25480             return;
25481         }
25482         var _this = this;
25483      
25484         Roo.each(stylesheets, function(s) {
25485             if(!s.length){
25486                 return;
25487             }
25488             
25489             Roo.get(_this.iframe.contentDocument.head).createChild({
25490                 tag : 'link',
25491                 rel : 'stylesheet',
25492                 type : 'text/css',
25493                 href : s
25494             });
25495         });
25496
25497         
25498     },
25499     
25500     removeStylesheets : function()
25501     {
25502         var _this = this;
25503         
25504         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25505             s.remove();
25506         });
25507     },
25508     
25509     setStyle : function(style)
25510     {
25511         Roo.get(this.iframe.contentDocument.head).createChild({
25512             tag : 'style',
25513             type : 'text/css',
25514             html : style
25515         });
25516
25517         return;
25518     }
25519     
25520     // hide stuff that is not compatible
25521     /**
25522      * @event blur
25523      * @hide
25524      */
25525     /**
25526      * @event change
25527      * @hide
25528      */
25529     /**
25530      * @event focus
25531      * @hide
25532      */
25533     /**
25534      * @event specialkey
25535      * @hide
25536      */
25537     /**
25538      * @cfg {String} fieldClass @hide
25539      */
25540     /**
25541      * @cfg {String} focusClass @hide
25542      */
25543     /**
25544      * @cfg {String} autoCreate @hide
25545      */
25546     /**
25547      * @cfg {String} inputType @hide
25548      */
25549     /**
25550      * @cfg {String} invalidClass @hide
25551      */
25552     /**
25553      * @cfg {String} invalidText @hide
25554      */
25555     /**
25556      * @cfg {String} msgFx @hide
25557      */
25558     /**
25559      * @cfg {String} validateOnBlur @hide
25560      */
25561 });
25562
25563 Roo.HtmlEditorCore.white = [
25564         'area', 'br', 'img', 'input', 'hr', 'wbr',
25565         
25566        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25567        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25568        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25569        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25570        'table',   'ul',         'xmp', 
25571        
25572        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25573       'thead',   'tr', 
25574      
25575       'dir', 'menu', 'ol', 'ul', 'dl',
25576        
25577       'embed',  'object'
25578 ];
25579
25580
25581 Roo.HtmlEditorCore.black = [
25582     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25583         'applet', // 
25584         'base',   'basefont', 'bgsound', 'blink',  'body', 
25585         'frame',  'frameset', 'head',    'html',   'ilayer', 
25586         'iframe', 'layer',  'link',     'meta',    'object',   
25587         'script', 'style' ,'title',  'xml' // clean later..
25588 ];
25589 Roo.HtmlEditorCore.clean = [
25590     'script', 'style', 'title', 'xml'
25591 ];
25592 Roo.HtmlEditorCore.remove = [
25593     'font'
25594 ];
25595 // attributes..
25596
25597 Roo.HtmlEditorCore.ablack = [
25598     'on'
25599 ];
25600     
25601 Roo.HtmlEditorCore.aclean = [ 
25602     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25603 ];
25604
25605 // protocols..
25606 Roo.HtmlEditorCore.pwhite= [
25607         'http',  'https',  'mailto'
25608 ];
25609
25610 // white listed style attributes.
25611 Roo.HtmlEditorCore.cwhite= [
25612       //  'text-align', /// default is to allow most things..
25613       
25614          
25615 //        'font-size'//??
25616 ];
25617
25618 // black listed style attributes.
25619 Roo.HtmlEditorCore.cblack= [
25620       //  'font-size' -- this can be set by the project 
25621 ];
25622
25623
25624 Roo.HtmlEditorCore.swapCodes   =[ 
25625     [    8211, "--" ], 
25626     [    8212, "--" ], 
25627     [    8216,  "'" ],  
25628     [    8217, "'" ],  
25629     [    8220, '"' ],  
25630     [    8221, '"' ],  
25631     [    8226, "*" ],  
25632     [    8230, "..." ]
25633 ]; 
25634
25635     /*
25636  * - LGPL
25637  *
25638  * HtmlEditor
25639  * 
25640  */
25641
25642 /**
25643  * @class Roo.bootstrap.HtmlEditor
25644  * @extends Roo.bootstrap.TextArea
25645  * Bootstrap HtmlEditor class
25646
25647  * @constructor
25648  * Create a new HtmlEditor
25649  * @param {Object} config The config object
25650  */
25651
25652 Roo.bootstrap.HtmlEditor = function(config){
25653     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25654     if (!this.toolbars) {
25655         this.toolbars = [];
25656     }
25657     
25658     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25659     this.addEvents({
25660             /**
25661              * @event initialize
25662              * Fires when the editor is fully initialized (including the iframe)
25663              * @param {HtmlEditor} this
25664              */
25665             initialize: true,
25666             /**
25667              * @event activate
25668              * Fires when the editor is first receives the focus. Any insertion must wait
25669              * until after this event.
25670              * @param {HtmlEditor} this
25671              */
25672             activate: true,
25673              /**
25674              * @event beforesync
25675              * Fires before the textarea is updated with content from the editor iframe. Return false
25676              * to cancel the sync.
25677              * @param {HtmlEditor} this
25678              * @param {String} html
25679              */
25680             beforesync: true,
25681              /**
25682              * @event beforepush
25683              * Fires before the iframe editor is updated with content from the textarea. Return false
25684              * to cancel the push.
25685              * @param {HtmlEditor} this
25686              * @param {String} html
25687              */
25688             beforepush: true,
25689              /**
25690              * @event sync
25691              * Fires when the textarea is updated with content from the editor iframe.
25692              * @param {HtmlEditor} this
25693              * @param {String} html
25694              */
25695             sync: true,
25696              /**
25697              * @event push
25698              * Fires when the iframe editor is updated with content from the textarea.
25699              * @param {HtmlEditor} this
25700              * @param {String} html
25701              */
25702             push: true,
25703              /**
25704              * @event editmodechange
25705              * Fires when the editor switches edit modes
25706              * @param {HtmlEditor} this
25707              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25708              */
25709             editmodechange: true,
25710             /**
25711              * @event editorevent
25712              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25713              * @param {HtmlEditor} this
25714              */
25715             editorevent: true,
25716             /**
25717              * @event firstfocus
25718              * Fires when on first focus - needed by toolbars..
25719              * @param {HtmlEditor} this
25720              */
25721             firstfocus: true,
25722             /**
25723              * @event autosave
25724              * Auto save the htmlEditor value as a file into Events
25725              * @param {HtmlEditor} this
25726              */
25727             autosave: true,
25728             /**
25729              * @event savedpreview
25730              * preview the saved version of htmlEditor
25731              * @param {HtmlEditor} this
25732              */
25733             savedpreview: true
25734         });
25735 };
25736
25737
25738 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25739     
25740     
25741       /**
25742      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25743      */
25744     toolbars : false,
25745     
25746      /**
25747     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25748     */
25749     btns : [],
25750    
25751      /**
25752      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25753      *                        Roo.resizable.
25754      */
25755     resizable : false,
25756      /**
25757      * @cfg {Number} height (in pixels)
25758      */   
25759     height: 300,
25760    /**
25761      * @cfg {Number} width (in pixels)
25762      */   
25763     width: false,
25764     
25765     /**
25766      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25767      * 
25768      */
25769     stylesheets: false,
25770     
25771     // id of frame..
25772     frameId: false,
25773     
25774     // private properties
25775     validationEvent : false,
25776     deferHeight: true,
25777     initialized : false,
25778     activated : false,
25779     
25780     onFocus : Roo.emptyFn,
25781     iframePad:3,
25782     hideMode:'offsets',
25783     
25784     tbContainer : false,
25785     
25786     bodyCls : '',
25787     
25788     toolbarContainer :function() {
25789         return this.wrap.select('.x-html-editor-tb',true).first();
25790     },
25791
25792     /**
25793      * Protected method that will not generally be called directly. It
25794      * is called when the editor creates its toolbar. Override this method if you need to
25795      * add custom toolbar buttons.
25796      * @param {HtmlEditor} editor
25797      */
25798     createToolbar : function(){
25799         Roo.log('renewing');
25800         Roo.log("create toolbars");
25801         
25802         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25803         this.toolbars[0].render(this.toolbarContainer());
25804         
25805         return;
25806         
25807 //        if (!editor.toolbars || !editor.toolbars.length) {
25808 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25809 //        }
25810 //        
25811 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25812 //            editor.toolbars[i] = Roo.factory(
25813 //                    typeof(editor.toolbars[i]) == 'string' ?
25814 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25815 //                Roo.bootstrap.HtmlEditor);
25816 //            editor.toolbars[i].init(editor);
25817 //        }
25818     },
25819
25820      
25821     // private
25822     onRender : function(ct, position)
25823     {
25824        // Roo.log("Call onRender: " + this.xtype);
25825         var _t = this;
25826         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25827       
25828         this.wrap = this.inputEl().wrap({
25829             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25830         });
25831         
25832         this.editorcore.onRender(ct, position);
25833          
25834         if (this.resizable) {
25835             this.resizeEl = new Roo.Resizable(this.wrap, {
25836                 pinned : true,
25837                 wrap: true,
25838                 dynamic : true,
25839                 minHeight : this.height,
25840                 height: this.height,
25841                 handles : this.resizable,
25842                 width: this.width,
25843                 listeners : {
25844                     resize : function(r, w, h) {
25845                         _t.onResize(w,h); // -something
25846                     }
25847                 }
25848             });
25849             
25850         }
25851         this.createToolbar(this);
25852        
25853         
25854         if(!this.width && this.resizable){
25855             this.setSize(this.wrap.getSize());
25856         }
25857         if (this.resizeEl) {
25858             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25859             // should trigger onReize..
25860         }
25861         
25862     },
25863
25864     // private
25865     onResize : function(w, h)
25866     {
25867         Roo.log('resize: ' +w + ',' + h );
25868         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25869         var ew = false;
25870         var eh = false;
25871         
25872         if(this.inputEl() ){
25873             if(typeof w == 'number'){
25874                 var aw = w - this.wrap.getFrameWidth('lr');
25875                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25876                 ew = aw;
25877             }
25878             if(typeof h == 'number'){
25879                  var tbh = -11;  // fixme it needs to tool bar size!
25880                 for (var i =0; i < this.toolbars.length;i++) {
25881                     // fixme - ask toolbars for heights?
25882                     tbh += this.toolbars[i].el.getHeight();
25883                     //if (this.toolbars[i].footer) {
25884                     //    tbh += this.toolbars[i].footer.el.getHeight();
25885                     //}
25886                 }
25887               
25888                 
25889                 
25890                 
25891                 
25892                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25893                 ah -= 5; // knock a few pixes off for look..
25894                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25895                 var eh = ah;
25896             }
25897         }
25898         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25899         this.editorcore.onResize(ew,eh);
25900         
25901     },
25902
25903     /**
25904      * Toggles the editor between standard and source edit mode.
25905      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25906      */
25907     toggleSourceEdit : function(sourceEditMode)
25908     {
25909         this.editorcore.toggleSourceEdit(sourceEditMode);
25910         
25911         if(this.editorcore.sourceEditMode){
25912             Roo.log('editor - showing textarea');
25913             
25914 //            Roo.log('in');
25915 //            Roo.log(this.syncValue());
25916             this.syncValue();
25917             this.inputEl().removeClass(['hide', 'x-hidden']);
25918             this.inputEl().dom.removeAttribute('tabIndex');
25919             this.inputEl().focus();
25920         }else{
25921             Roo.log('editor - hiding textarea');
25922 //            Roo.log('out')
25923 //            Roo.log(this.pushValue()); 
25924             this.pushValue();
25925             
25926             this.inputEl().addClass(['hide', 'x-hidden']);
25927             this.inputEl().dom.setAttribute('tabIndex', -1);
25928             //this.deferFocus();
25929         }
25930          
25931         if(this.resizable){
25932             this.setSize(this.wrap.getSize());
25933         }
25934         
25935         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25936     },
25937  
25938     // private (for BoxComponent)
25939     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25940
25941     // private (for BoxComponent)
25942     getResizeEl : function(){
25943         return this.wrap;
25944     },
25945
25946     // private (for BoxComponent)
25947     getPositionEl : function(){
25948         return this.wrap;
25949     },
25950
25951     // private
25952     initEvents : function(){
25953         this.originalValue = this.getValue();
25954     },
25955
25956 //    /**
25957 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25958 //     * @method
25959 //     */
25960 //    markInvalid : Roo.emptyFn,
25961 //    /**
25962 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25963 //     * @method
25964 //     */
25965 //    clearInvalid : Roo.emptyFn,
25966
25967     setValue : function(v){
25968         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25969         this.editorcore.pushValue();
25970     },
25971
25972      
25973     // private
25974     deferFocus : function(){
25975         this.focus.defer(10, this);
25976     },
25977
25978     // doc'ed in Field
25979     focus : function(){
25980         this.editorcore.focus();
25981         
25982     },
25983       
25984
25985     // private
25986     onDestroy : function(){
25987         
25988         
25989         
25990         if(this.rendered){
25991             
25992             for (var i =0; i < this.toolbars.length;i++) {
25993                 // fixme - ask toolbars for heights?
25994                 this.toolbars[i].onDestroy();
25995             }
25996             
25997             this.wrap.dom.innerHTML = '';
25998             this.wrap.remove();
25999         }
26000     },
26001
26002     // private
26003     onFirstFocus : function(){
26004         //Roo.log("onFirstFocus");
26005         this.editorcore.onFirstFocus();
26006          for (var i =0; i < this.toolbars.length;i++) {
26007             this.toolbars[i].onFirstFocus();
26008         }
26009         
26010     },
26011     
26012     // private
26013     syncValue : function()
26014     {   
26015         this.editorcore.syncValue();
26016     },
26017     
26018     pushValue : function()
26019     {   
26020         this.editorcore.pushValue();
26021     }
26022      
26023     
26024     // hide stuff that is not compatible
26025     /**
26026      * @event blur
26027      * @hide
26028      */
26029     /**
26030      * @event change
26031      * @hide
26032      */
26033     /**
26034      * @event focus
26035      * @hide
26036      */
26037     /**
26038      * @event specialkey
26039      * @hide
26040      */
26041     /**
26042      * @cfg {String} fieldClass @hide
26043      */
26044     /**
26045      * @cfg {String} focusClass @hide
26046      */
26047     /**
26048      * @cfg {String} autoCreate @hide
26049      */
26050     /**
26051      * @cfg {String} inputType @hide
26052      */
26053      
26054     /**
26055      * @cfg {String} invalidText @hide
26056      */
26057     /**
26058      * @cfg {String} msgFx @hide
26059      */
26060     /**
26061      * @cfg {String} validateOnBlur @hide
26062      */
26063 });
26064  
26065     
26066    
26067    
26068    
26069       
26070 Roo.namespace('Roo.bootstrap.htmleditor');
26071 /**
26072  * @class Roo.bootstrap.HtmlEditorToolbar1
26073  * Basic Toolbar
26074  * 
26075  * @example
26076  * Usage:
26077  *
26078  new Roo.bootstrap.HtmlEditor({
26079     ....
26080     toolbars : [
26081         new Roo.bootstrap.HtmlEditorToolbar1({
26082             disable : { fonts: 1 , format: 1, ..., ... , ...],
26083             btns : [ .... ]
26084         })
26085     }
26086      
26087  * 
26088  * @cfg {Object} disable List of elements to disable..
26089  * @cfg {Array} btns List of additional buttons.
26090  * 
26091  * 
26092  * NEEDS Extra CSS? 
26093  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26094  */
26095  
26096 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26097 {
26098     
26099     Roo.apply(this, config);
26100     
26101     // default disabled, based on 'good practice'..
26102     this.disable = this.disable || {};
26103     Roo.applyIf(this.disable, {
26104         fontSize : true,
26105         colors : true,
26106         specialElements : true
26107     });
26108     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26109     
26110     this.editor = config.editor;
26111     this.editorcore = config.editor.editorcore;
26112     
26113     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26114     
26115     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26116     // dont call parent... till later.
26117 }
26118 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26119      
26120     bar : true,
26121     
26122     editor : false,
26123     editorcore : false,
26124     
26125     
26126     formats : [
26127         "p" ,  
26128         "h1","h2","h3","h4","h5","h6", 
26129         "pre", "code", 
26130         "abbr", "acronym", "address", "cite", "samp", "var",
26131         'div','span'
26132     ],
26133     
26134     onRender : function(ct, position)
26135     {
26136        // Roo.log("Call onRender: " + this.xtype);
26137         
26138        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26139        Roo.log(this.el);
26140        this.el.dom.style.marginBottom = '0';
26141        var _this = this;
26142        var editorcore = this.editorcore;
26143        var editor= this.editor;
26144        
26145        var children = [];
26146        var btn = function(id,cmd , toggle, handler, html){
26147        
26148             var  event = toggle ? 'toggle' : 'click';
26149        
26150             var a = {
26151                 size : 'sm',
26152                 xtype: 'Button',
26153                 xns: Roo.bootstrap,
26154                 //glyphicon : id,
26155                 fa: id,
26156                 cmd : id || cmd,
26157                 enableToggle:toggle !== false,
26158                 html : html || '',
26159                 pressed : toggle ? false : null,
26160                 listeners : {}
26161             };
26162             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26163                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26164             };
26165             children.push(a);
26166             return a;
26167        }
26168        
26169     //    var cb_box = function...
26170         
26171         var style = {
26172                 xtype: 'Button',
26173                 size : 'sm',
26174                 xns: Roo.bootstrap,
26175                 fa : 'font',
26176                 //html : 'submit'
26177                 menu : {
26178                     xtype: 'Menu',
26179                     xns: Roo.bootstrap,
26180                     items:  []
26181                 }
26182         };
26183         Roo.each(this.formats, function(f) {
26184             style.menu.items.push({
26185                 xtype :'MenuItem',
26186                 xns: Roo.bootstrap,
26187                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26188                 tagname : f,
26189                 listeners : {
26190                     click : function()
26191                     {
26192                         editorcore.insertTag(this.tagname);
26193                         editor.focus();
26194                     }
26195                 }
26196                 
26197             });
26198         });
26199         children.push(style);   
26200         
26201         btn('bold',false,true);
26202         btn('italic',false,true);
26203         btn('align-left', 'justifyleft',true);
26204         btn('align-center', 'justifycenter',true);
26205         btn('align-right' , 'justifyright',true);
26206         btn('link', false, false, function(btn) {
26207             //Roo.log("create link?");
26208             var url = prompt(this.createLinkText, this.defaultLinkValue);
26209             if(url && url != 'http:/'+'/'){
26210                 this.editorcore.relayCmd('createlink', url);
26211             }
26212         }),
26213         btn('list','insertunorderedlist',true);
26214         btn('pencil', false,true, function(btn){
26215                 Roo.log(this);
26216                 this.toggleSourceEdit(btn.pressed);
26217         });
26218         
26219         if (this.editor.btns.length > 0) {
26220             for (var i = 0; i<this.editor.btns.length; i++) {
26221                 children.push(this.editor.btns[i]);
26222             }
26223         }
26224         
26225         /*
26226         var cog = {
26227                 xtype: 'Button',
26228                 size : 'sm',
26229                 xns: Roo.bootstrap,
26230                 glyphicon : 'cog',
26231                 //html : 'submit'
26232                 menu : {
26233                     xtype: 'Menu',
26234                     xns: Roo.bootstrap,
26235                     items:  []
26236                 }
26237         };
26238         
26239         cog.menu.items.push({
26240             xtype :'MenuItem',
26241             xns: Roo.bootstrap,
26242             html : Clean styles,
26243             tagname : f,
26244             listeners : {
26245                 click : function()
26246                 {
26247                     editorcore.insertTag(this.tagname);
26248                     editor.focus();
26249                 }
26250             }
26251             
26252         });
26253        */
26254         
26255          
26256        this.xtype = 'NavSimplebar';
26257         
26258         for(var i=0;i< children.length;i++) {
26259             
26260             this.buttons.add(this.addxtypeChild(children[i]));
26261             
26262         }
26263         
26264         editor.on('editorevent', this.updateToolbar, this);
26265     },
26266     onBtnClick : function(id)
26267     {
26268        this.editorcore.relayCmd(id);
26269        this.editorcore.focus();
26270     },
26271     
26272     /**
26273      * Protected method that will not generally be called directly. It triggers
26274      * a toolbar update by reading the markup state of the current selection in the editor.
26275      */
26276     updateToolbar: function(){
26277
26278         if(!this.editorcore.activated){
26279             this.editor.onFirstFocus(); // is this neeed?
26280             return;
26281         }
26282
26283         var btns = this.buttons; 
26284         var doc = this.editorcore.doc;
26285         btns.get('bold').setActive(doc.queryCommandState('bold'));
26286         btns.get('italic').setActive(doc.queryCommandState('italic'));
26287         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26288         
26289         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26290         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26291         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26292         
26293         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26294         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26295          /*
26296         
26297         var ans = this.editorcore.getAllAncestors();
26298         if (this.formatCombo) {
26299             
26300             
26301             var store = this.formatCombo.store;
26302             this.formatCombo.setValue("");
26303             for (var i =0; i < ans.length;i++) {
26304                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26305                     // select it..
26306                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26307                     break;
26308                 }
26309             }
26310         }
26311         
26312         
26313         
26314         // hides menus... - so this cant be on a menu...
26315         Roo.bootstrap.MenuMgr.hideAll();
26316         */
26317         Roo.bootstrap.MenuMgr.hideAll();
26318         //this.editorsyncValue();
26319     },
26320     onFirstFocus: function() {
26321         this.buttons.each(function(item){
26322            item.enable();
26323         });
26324     },
26325     toggleSourceEdit : function(sourceEditMode){
26326         
26327           
26328         if(sourceEditMode){
26329             Roo.log("disabling buttons");
26330            this.buttons.each( function(item){
26331                 if(item.cmd != 'pencil'){
26332                     item.disable();
26333                 }
26334             });
26335           
26336         }else{
26337             Roo.log("enabling buttons");
26338             if(this.editorcore.initialized){
26339                 this.buttons.each( function(item){
26340                     item.enable();
26341                 });
26342             }
26343             
26344         }
26345         Roo.log("calling toggole on editor");
26346         // tell the editor that it's been pressed..
26347         this.editor.toggleSourceEdit(sourceEditMode);
26348        
26349     }
26350 });
26351
26352
26353
26354
26355  
26356 /*
26357  * - LGPL
26358  */
26359
26360 /**
26361  * @class Roo.bootstrap.Markdown
26362  * @extends Roo.bootstrap.TextArea
26363  * Bootstrap Showdown editable area
26364  * @cfg {string} content
26365  * 
26366  * @constructor
26367  * Create a new Showdown
26368  */
26369
26370 Roo.bootstrap.Markdown = function(config){
26371     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26372    
26373 };
26374
26375 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26376     
26377     editing :false,
26378     
26379     initEvents : function()
26380     {
26381         
26382         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26383         this.markdownEl = this.el.createChild({
26384             cls : 'roo-markdown-area'
26385         });
26386         this.inputEl().addClass('d-none');
26387         if (this.getValue() == '') {
26388             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26389             
26390         } else {
26391             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26392         }
26393         this.markdownEl.on('click', this.toggleTextEdit, this);
26394         this.on('blur', this.toggleTextEdit, this);
26395         this.on('specialkey', this.resizeTextArea, this);
26396     },
26397     
26398     toggleTextEdit : function()
26399     {
26400         var sh = this.markdownEl.getHeight();
26401         this.inputEl().addClass('d-none');
26402         this.markdownEl.addClass('d-none');
26403         if (!this.editing) {
26404             // show editor?
26405             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26406             this.inputEl().removeClass('d-none');
26407             this.inputEl().focus();
26408             this.editing = true;
26409             return;
26410         }
26411         // show showdown...
26412         this.updateMarkdown();
26413         this.markdownEl.removeClass('d-none');
26414         this.editing = false;
26415         return;
26416     },
26417     updateMarkdown : function()
26418     {
26419         if (this.getValue() == '') {
26420             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26421             return;
26422         }
26423  
26424         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26425     },
26426     
26427     resizeTextArea: function () {
26428         
26429         var sh = 100;
26430         Roo.log([sh, this.getValue().split("\n").length * 30]);
26431         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26432     },
26433     setValue : function(val)
26434     {
26435         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26436         if (!this.editing) {
26437             this.updateMarkdown();
26438         }
26439         
26440     },
26441     focus : function()
26442     {
26443         if (!this.editing) {
26444             this.toggleTextEdit();
26445         }
26446         
26447     }
26448
26449
26450 });
26451 /**
26452  * @class Roo.bootstrap.Table.AbstractSelectionModel
26453  * @extends Roo.util.Observable
26454  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26455  * implemented by descendant classes.  This class should not be directly instantiated.
26456  * @constructor
26457  */
26458 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26459     this.locked = false;
26460     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26461 };
26462
26463
26464 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26465     /** @ignore Called by the grid automatically. Do not call directly. */
26466     init : function(grid){
26467         this.grid = grid;
26468         this.initEvents();
26469     },
26470
26471     /**
26472      * Locks the selections.
26473      */
26474     lock : function(){
26475         this.locked = true;
26476     },
26477
26478     /**
26479      * Unlocks the selections.
26480      */
26481     unlock : function(){
26482         this.locked = false;
26483     },
26484
26485     /**
26486      * Returns true if the selections are locked.
26487      * @return {Boolean}
26488      */
26489     isLocked : function(){
26490         return this.locked;
26491     },
26492     
26493     
26494     initEvents : function ()
26495     {
26496         
26497     }
26498 });
26499 /**
26500  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26501  * @class Roo.bootstrap.Table.RowSelectionModel
26502  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26503  * It supports multiple selections and keyboard selection/navigation. 
26504  * @constructor
26505  * @param {Object} config
26506  */
26507
26508 Roo.bootstrap.Table.RowSelectionModel = function(config){
26509     Roo.apply(this, config);
26510     this.selections = new Roo.util.MixedCollection(false, function(o){
26511         return o.id;
26512     });
26513
26514     this.last = false;
26515     this.lastActive = false;
26516
26517     this.addEvents({
26518         /**
26519              * @event selectionchange
26520              * Fires when the selection changes
26521              * @param {SelectionModel} this
26522              */
26523             "selectionchange" : true,
26524         /**
26525              * @event afterselectionchange
26526              * Fires after the selection changes (eg. by key press or clicking)
26527              * @param {SelectionModel} this
26528              */
26529             "afterselectionchange" : true,
26530         /**
26531              * @event beforerowselect
26532              * Fires when a row is selected being selected, return false to cancel.
26533              * @param {SelectionModel} this
26534              * @param {Number} rowIndex The selected index
26535              * @param {Boolean} keepExisting False if other selections will be cleared
26536              */
26537             "beforerowselect" : true,
26538         /**
26539              * @event rowselect
26540              * Fires when a row is selected.
26541              * @param {SelectionModel} this
26542              * @param {Number} rowIndex The selected index
26543              * @param {Roo.data.Record} r The record
26544              */
26545             "rowselect" : true,
26546         /**
26547              * @event rowdeselect
26548              * Fires when a row is deselected.
26549              * @param {SelectionModel} this
26550              * @param {Number} rowIndex The selected index
26551              */
26552         "rowdeselect" : true
26553     });
26554     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26555     this.locked = false;
26556  };
26557
26558 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26559     /**
26560      * @cfg {Boolean} singleSelect
26561      * True to allow selection of only one row at a time (defaults to false)
26562      */
26563     singleSelect : false,
26564
26565     // private
26566     initEvents : function()
26567     {
26568
26569         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26570         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26571         //}else{ // allow click to work like normal
26572          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26573         //}
26574         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26575         this.grid.on("rowclick", this.handleMouseDown, this);
26576         
26577         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26578             "up" : function(e){
26579                 if(!e.shiftKey){
26580                     this.selectPrevious(e.shiftKey);
26581                 }else if(this.last !== false && this.lastActive !== false){
26582                     var last = this.last;
26583                     this.selectRange(this.last,  this.lastActive-1);
26584                     this.grid.getView().focusRow(this.lastActive);
26585                     if(last !== false){
26586                         this.last = last;
26587                     }
26588                 }else{
26589                     this.selectFirstRow();
26590                 }
26591                 this.fireEvent("afterselectionchange", this);
26592             },
26593             "down" : function(e){
26594                 if(!e.shiftKey){
26595                     this.selectNext(e.shiftKey);
26596                 }else if(this.last !== false && this.lastActive !== false){
26597                     var last = this.last;
26598                     this.selectRange(this.last,  this.lastActive+1);
26599                     this.grid.getView().focusRow(this.lastActive);
26600                     if(last !== false){
26601                         this.last = last;
26602                     }
26603                 }else{
26604                     this.selectFirstRow();
26605                 }
26606                 this.fireEvent("afterselectionchange", this);
26607             },
26608             scope: this
26609         });
26610         this.grid.store.on('load', function(){
26611             this.selections.clear();
26612         },this);
26613         /*
26614         var view = this.grid.view;
26615         view.on("refresh", this.onRefresh, this);
26616         view.on("rowupdated", this.onRowUpdated, this);
26617         view.on("rowremoved", this.onRemove, this);
26618         */
26619     },
26620
26621     // private
26622     onRefresh : function()
26623     {
26624         var ds = this.grid.store, i, v = this.grid.view;
26625         var s = this.selections;
26626         s.each(function(r){
26627             if((i = ds.indexOfId(r.id)) != -1){
26628                 v.onRowSelect(i);
26629             }else{
26630                 s.remove(r);
26631             }
26632         });
26633     },
26634
26635     // private
26636     onRemove : function(v, index, r){
26637         this.selections.remove(r);
26638     },
26639
26640     // private
26641     onRowUpdated : function(v, index, r){
26642         if(this.isSelected(r)){
26643             v.onRowSelect(index);
26644         }
26645     },
26646
26647     /**
26648      * Select records.
26649      * @param {Array} records The records to select
26650      * @param {Boolean} keepExisting (optional) True to keep existing selections
26651      */
26652     selectRecords : function(records, keepExisting)
26653     {
26654         if(!keepExisting){
26655             this.clearSelections();
26656         }
26657             var ds = this.grid.store;
26658         for(var i = 0, len = records.length; i < len; i++){
26659             this.selectRow(ds.indexOf(records[i]), true);
26660         }
26661     },
26662
26663     /**
26664      * Gets the number of selected rows.
26665      * @return {Number}
26666      */
26667     getCount : function(){
26668         return this.selections.length;
26669     },
26670
26671     /**
26672      * Selects the first row in the grid.
26673      */
26674     selectFirstRow : function(){
26675         this.selectRow(0);
26676     },
26677
26678     /**
26679      * Select the last row.
26680      * @param {Boolean} keepExisting (optional) True to keep existing selections
26681      */
26682     selectLastRow : function(keepExisting){
26683         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26684         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26685     },
26686
26687     /**
26688      * Selects the row immediately following the last selected row.
26689      * @param {Boolean} keepExisting (optional) True to keep existing selections
26690      */
26691     selectNext : function(keepExisting)
26692     {
26693             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26694             this.selectRow(this.last+1, keepExisting);
26695             this.grid.getView().focusRow(this.last);
26696         }
26697     },
26698
26699     /**
26700      * Selects the row that precedes the last selected row.
26701      * @param {Boolean} keepExisting (optional) True to keep existing selections
26702      */
26703     selectPrevious : function(keepExisting){
26704         if(this.last){
26705             this.selectRow(this.last-1, keepExisting);
26706             this.grid.getView().focusRow(this.last);
26707         }
26708     },
26709
26710     /**
26711      * Returns the selected records
26712      * @return {Array} Array of selected records
26713      */
26714     getSelections : function(){
26715         return [].concat(this.selections.items);
26716     },
26717
26718     /**
26719      * Returns the first selected record.
26720      * @return {Record}
26721      */
26722     getSelected : function(){
26723         return this.selections.itemAt(0);
26724     },
26725
26726
26727     /**
26728      * Clears all selections.
26729      */
26730     clearSelections : function(fast)
26731     {
26732         if(this.locked) {
26733             return;
26734         }
26735         if(fast !== true){
26736                 var ds = this.grid.store;
26737             var s = this.selections;
26738             s.each(function(r){
26739                 this.deselectRow(ds.indexOfId(r.id));
26740             }, this);
26741             s.clear();
26742         }else{
26743             this.selections.clear();
26744         }
26745         this.last = false;
26746     },
26747
26748
26749     /**
26750      * Selects all rows.
26751      */
26752     selectAll : function(){
26753         if(this.locked) {
26754             return;
26755         }
26756         this.selections.clear();
26757         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26758             this.selectRow(i, true);
26759         }
26760     },
26761
26762     /**
26763      * Returns True if there is a selection.
26764      * @return {Boolean}
26765      */
26766     hasSelection : function(){
26767         return this.selections.length > 0;
26768     },
26769
26770     /**
26771      * Returns True if the specified row is selected.
26772      * @param {Number/Record} record The record or index of the record to check
26773      * @return {Boolean}
26774      */
26775     isSelected : function(index){
26776             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26777         return (r && this.selections.key(r.id) ? true : false);
26778     },
26779
26780     /**
26781      * Returns True if the specified record id is selected.
26782      * @param {String} id The id of record to check
26783      * @return {Boolean}
26784      */
26785     isIdSelected : function(id){
26786         return (this.selections.key(id) ? true : false);
26787     },
26788
26789
26790     // private
26791     handleMouseDBClick : function(e, t){
26792         
26793     },
26794     // private
26795     handleMouseDown : function(e, t)
26796     {
26797             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26798         if(this.isLocked() || rowIndex < 0 ){
26799             return;
26800         };
26801         if(e.shiftKey && this.last !== false){
26802             var last = this.last;
26803             this.selectRange(last, rowIndex, e.ctrlKey);
26804             this.last = last; // reset the last
26805             t.focus();
26806     
26807         }else{
26808             var isSelected = this.isSelected(rowIndex);
26809             //Roo.log("select row:" + rowIndex);
26810             if(isSelected){
26811                 this.deselectRow(rowIndex);
26812             } else {
26813                         this.selectRow(rowIndex, true);
26814             }
26815     
26816             /*
26817                 if(e.button !== 0 && isSelected){
26818                 alert('rowIndex 2: ' + rowIndex);
26819                     view.focusRow(rowIndex);
26820                 }else if(e.ctrlKey && isSelected){
26821                     this.deselectRow(rowIndex);
26822                 }else if(!isSelected){
26823                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26824                     view.focusRow(rowIndex);
26825                 }
26826             */
26827         }
26828         this.fireEvent("afterselectionchange", this);
26829     },
26830     // private
26831     handleDragableRowClick :  function(grid, rowIndex, e) 
26832     {
26833         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26834             this.selectRow(rowIndex, false);
26835             grid.view.focusRow(rowIndex);
26836              this.fireEvent("afterselectionchange", this);
26837         }
26838     },
26839     
26840     /**
26841      * Selects multiple rows.
26842      * @param {Array} rows Array of the indexes of the row to select
26843      * @param {Boolean} keepExisting (optional) True to keep existing selections
26844      */
26845     selectRows : function(rows, keepExisting){
26846         if(!keepExisting){
26847             this.clearSelections();
26848         }
26849         for(var i = 0, len = rows.length; i < len; i++){
26850             this.selectRow(rows[i], true);
26851         }
26852     },
26853
26854     /**
26855      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26856      * @param {Number} startRow The index of the first row in the range
26857      * @param {Number} endRow The index of the last row in the range
26858      * @param {Boolean} keepExisting (optional) True to retain existing selections
26859      */
26860     selectRange : function(startRow, endRow, keepExisting){
26861         if(this.locked) {
26862             return;
26863         }
26864         if(!keepExisting){
26865             this.clearSelections();
26866         }
26867         if(startRow <= endRow){
26868             for(var i = startRow; i <= endRow; i++){
26869                 this.selectRow(i, true);
26870             }
26871         }else{
26872             for(var i = startRow; i >= endRow; i--){
26873                 this.selectRow(i, true);
26874             }
26875         }
26876     },
26877
26878     /**
26879      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26880      * @param {Number} startRow The index of the first row in the range
26881      * @param {Number} endRow The index of the last row in the range
26882      */
26883     deselectRange : function(startRow, endRow, preventViewNotify){
26884         if(this.locked) {
26885             return;
26886         }
26887         for(var i = startRow; i <= endRow; i++){
26888             this.deselectRow(i, preventViewNotify);
26889         }
26890     },
26891
26892     /**
26893      * Selects a row.
26894      * @param {Number} row The index of the row to select
26895      * @param {Boolean} keepExisting (optional) True to keep existing selections
26896      */
26897     selectRow : function(index, keepExisting, preventViewNotify)
26898     {
26899             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26900             return;
26901         }
26902         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26903             if(!keepExisting || this.singleSelect){
26904                 this.clearSelections();
26905             }
26906             
26907             var r = this.grid.store.getAt(index);
26908             //console.log('selectRow - record id :' + r.id);
26909             
26910             this.selections.add(r);
26911             this.last = this.lastActive = index;
26912             if(!preventViewNotify){
26913                 var proxy = new Roo.Element(
26914                                 this.grid.getRowDom(index)
26915                 );
26916                 proxy.addClass('bg-info info');
26917             }
26918             this.fireEvent("rowselect", this, index, r);
26919             this.fireEvent("selectionchange", this);
26920         }
26921     },
26922
26923     /**
26924      * Deselects a row.
26925      * @param {Number} row The index of the row to deselect
26926      */
26927     deselectRow : function(index, preventViewNotify)
26928     {
26929         if(this.locked) {
26930             return;
26931         }
26932         if(this.last == index){
26933             this.last = false;
26934         }
26935         if(this.lastActive == index){
26936             this.lastActive = false;
26937         }
26938         
26939         var r = this.grid.store.getAt(index);
26940         if (!r) {
26941             return;
26942         }
26943         
26944         this.selections.remove(r);
26945         //.console.log('deselectRow - record id :' + r.id);
26946         if(!preventViewNotify){
26947         
26948             var proxy = new Roo.Element(
26949                 this.grid.getRowDom(index)
26950             );
26951             proxy.removeClass('bg-info info');
26952         }
26953         this.fireEvent("rowdeselect", this, index);
26954         this.fireEvent("selectionchange", this);
26955     },
26956
26957     // private
26958     restoreLast : function(){
26959         if(this._last){
26960             this.last = this._last;
26961         }
26962     },
26963
26964     // private
26965     acceptsNav : function(row, col, cm){
26966         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26967     },
26968
26969     // private
26970     onEditorKey : function(field, e){
26971         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26972         if(k == e.TAB){
26973             e.stopEvent();
26974             ed.completeEdit();
26975             if(e.shiftKey){
26976                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26977             }else{
26978                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26979             }
26980         }else if(k == e.ENTER && !e.ctrlKey){
26981             e.stopEvent();
26982             ed.completeEdit();
26983             if(e.shiftKey){
26984                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26985             }else{
26986                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26987             }
26988         }else if(k == e.ESC){
26989             ed.cancelEdit();
26990         }
26991         if(newCell){
26992             g.startEditing(newCell[0], newCell[1]);
26993         }
26994     }
26995 });
26996 /*
26997  * Based on:
26998  * Ext JS Library 1.1.1
26999  * Copyright(c) 2006-2007, Ext JS, LLC.
27000  *
27001  * Originally Released Under LGPL - original licence link has changed is not relivant.
27002  *
27003  * Fork - LGPL
27004  * <script type="text/javascript">
27005  */
27006  
27007 /**
27008  * @class Roo.bootstrap.PagingToolbar
27009  * @extends Roo.bootstrap.NavSimplebar
27010  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27011  * @constructor
27012  * Create a new PagingToolbar
27013  * @param {Object} config The config object
27014  * @param {Roo.data.Store} store
27015  */
27016 Roo.bootstrap.PagingToolbar = function(config)
27017 {
27018     // old args format still supported... - xtype is prefered..
27019         // created from xtype...
27020     
27021     this.ds = config.dataSource;
27022     
27023     if (config.store && !this.ds) {
27024         this.store= Roo.factory(config.store, Roo.data);
27025         this.ds = this.store;
27026         this.ds.xmodule = this.xmodule || false;
27027     }
27028     
27029     this.toolbarItems = [];
27030     if (config.items) {
27031         this.toolbarItems = config.items;
27032     }
27033     
27034     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27035     
27036     this.cursor = 0;
27037     
27038     if (this.ds) { 
27039         this.bind(this.ds);
27040     }
27041     
27042     if (Roo.bootstrap.version == 4) {
27043         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27044     } else {
27045         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27046     }
27047     
27048 };
27049
27050 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27051     /**
27052      * @cfg {Roo.data.Store} dataSource
27053      * The underlying data store providing the paged data
27054      */
27055     /**
27056      * @cfg {String/HTMLElement/Element} container
27057      * container The id or element that will contain the toolbar
27058      */
27059     /**
27060      * @cfg {Boolean} displayInfo
27061      * True to display the displayMsg (defaults to false)
27062      */
27063     /**
27064      * @cfg {Number} pageSize
27065      * The number of records to display per page (defaults to 20)
27066      */
27067     pageSize: 20,
27068     /**
27069      * @cfg {String} displayMsg
27070      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27071      */
27072     displayMsg : 'Displaying {0} - {1} of {2}',
27073     /**
27074      * @cfg {String} emptyMsg
27075      * The message to display when no records are found (defaults to "No data to display")
27076      */
27077     emptyMsg : 'No data to display',
27078     /**
27079      * Customizable piece of the default paging text (defaults to "Page")
27080      * @type String
27081      */
27082     beforePageText : "Page",
27083     /**
27084      * Customizable piece of the default paging text (defaults to "of %0")
27085      * @type String
27086      */
27087     afterPageText : "of {0}",
27088     /**
27089      * Customizable piece of the default paging text (defaults to "First Page")
27090      * @type String
27091      */
27092     firstText : "First Page",
27093     /**
27094      * Customizable piece of the default paging text (defaults to "Previous Page")
27095      * @type String
27096      */
27097     prevText : "Previous Page",
27098     /**
27099      * Customizable piece of the default paging text (defaults to "Next Page")
27100      * @type String
27101      */
27102     nextText : "Next Page",
27103     /**
27104      * Customizable piece of the default paging text (defaults to "Last Page")
27105      * @type String
27106      */
27107     lastText : "Last Page",
27108     /**
27109      * Customizable piece of the default paging text (defaults to "Refresh")
27110      * @type String
27111      */
27112     refreshText : "Refresh",
27113
27114     buttons : false,
27115     // private
27116     onRender : function(ct, position) 
27117     {
27118         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27119         this.navgroup.parentId = this.id;
27120         this.navgroup.onRender(this.el, null);
27121         // add the buttons to the navgroup
27122         
27123         if(this.displayInfo){
27124             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27125             this.displayEl = this.el.select('.x-paging-info', true).first();
27126 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27127 //            this.displayEl = navel.el.select('span',true).first();
27128         }
27129         
27130         var _this = this;
27131         
27132         if(this.buttons){
27133             Roo.each(_this.buttons, function(e){ // this might need to use render????
27134                Roo.factory(e).render(_this.el);
27135             });
27136         }
27137             
27138         Roo.each(_this.toolbarItems, function(e) {
27139             _this.navgroup.addItem(e);
27140         });
27141         
27142         
27143         this.first = this.navgroup.addItem({
27144             tooltip: this.firstText,
27145             cls: "prev btn-outline-secondary",
27146             html : ' <i class="fa fa-step-backward"></i>',
27147             disabled: true,
27148             preventDefault: true,
27149             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27150         });
27151         
27152         this.prev =  this.navgroup.addItem({
27153             tooltip: this.prevText,
27154             cls: "prev btn-outline-secondary",
27155             html : ' <i class="fa fa-backward"></i>',
27156             disabled: true,
27157             preventDefault: true,
27158             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27159         });
27160     //this.addSeparator();
27161         
27162         
27163         var field = this.navgroup.addItem( {
27164             tagtype : 'span',
27165             cls : 'x-paging-position  btn-outline-secondary',
27166              disabled: true,
27167             html : this.beforePageText  +
27168                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27169                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27170          } ); //?? escaped?
27171         
27172         this.field = field.el.select('input', true).first();
27173         this.field.on("keydown", this.onPagingKeydown, this);
27174         this.field.on("focus", function(){this.dom.select();});
27175     
27176     
27177         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27178         //this.field.setHeight(18);
27179         //this.addSeparator();
27180         this.next = this.navgroup.addItem({
27181             tooltip: this.nextText,
27182             cls: "next btn-outline-secondary",
27183             html : ' <i class="fa fa-forward"></i>',
27184             disabled: true,
27185             preventDefault: true,
27186             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27187         });
27188         this.last = this.navgroup.addItem({
27189             tooltip: this.lastText,
27190             html : ' <i class="fa fa-step-forward"></i>',
27191             cls: "next btn-outline-secondary",
27192             disabled: true,
27193             preventDefault: true,
27194             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27195         });
27196     //this.addSeparator();
27197         this.loading = this.navgroup.addItem({
27198             tooltip: this.refreshText,
27199             cls: "btn-outline-secondary",
27200             html : ' <i class="fa fa-refresh"></i>',
27201             preventDefault: true,
27202             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27203         });
27204         
27205     },
27206
27207     // private
27208     updateInfo : function(){
27209         if(this.displayEl){
27210             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27211             var msg = count == 0 ?
27212                 this.emptyMsg :
27213                 String.format(
27214                     this.displayMsg,
27215                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27216                 );
27217             this.displayEl.update(msg);
27218         }
27219     },
27220
27221     // private
27222     onLoad : function(ds, r, o)
27223     {
27224         this.cursor = o.params.start ? o.params.start : 0;
27225         
27226         var d = this.getPageData(),
27227             ap = d.activePage,
27228             ps = d.pages;
27229         
27230         
27231         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27232         this.field.dom.value = ap;
27233         this.first.setDisabled(ap == 1);
27234         this.prev.setDisabled(ap == 1);
27235         this.next.setDisabled(ap == ps);
27236         this.last.setDisabled(ap == ps);
27237         this.loading.enable();
27238         this.updateInfo();
27239     },
27240
27241     // private
27242     getPageData : function(){
27243         var total = this.ds.getTotalCount();
27244         return {
27245             total : total,
27246             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27247             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27248         };
27249     },
27250
27251     // private
27252     onLoadError : function(){
27253         this.loading.enable();
27254     },
27255
27256     // private
27257     onPagingKeydown : function(e){
27258         var k = e.getKey();
27259         var d = this.getPageData();
27260         if(k == e.RETURN){
27261             var v = this.field.dom.value, pageNum;
27262             if(!v || isNaN(pageNum = parseInt(v, 10))){
27263                 this.field.dom.value = d.activePage;
27264                 return;
27265             }
27266             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27267             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27268             e.stopEvent();
27269         }
27270         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))
27271         {
27272           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27273           this.field.dom.value = pageNum;
27274           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27275           e.stopEvent();
27276         }
27277         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27278         {
27279           var v = this.field.dom.value, pageNum; 
27280           var increment = (e.shiftKey) ? 10 : 1;
27281           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27282                 increment *= -1;
27283           }
27284           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27285             this.field.dom.value = d.activePage;
27286             return;
27287           }
27288           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27289           {
27290             this.field.dom.value = parseInt(v, 10) + increment;
27291             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27292             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27293           }
27294           e.stopEvent();
27295         }
27296     },
27297
27298     // private
27299     beforeLoad : function(){
27300         if(this.loading){
27301             this.loading.disable();
27302         }
27303     },
27304
27305     // private
27306     onClick : function(which){
27307         
27308         var ds = this.ds;
27309         if (!ds) {
27310             return;
27311         }
27312         
27313         switch(which){
27314             case "first":
27315                 ds.load({params:{start: 0, limit: this.pageSize}});
27316             break;
27317             case "prev":
27318                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27319             break;
27320             case "next":
27321                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27322             break;
27323             case "last":
27324                 var total = ds.getTotalCount();
27325                 var extra = total % this.pageSize;
27326                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27327                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27328             break;
27329             case "refresh":
27330                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27331             break;
27332         }
27333     },
27334
27335     /**
27336      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27337      * @param {Roo.data.Store} store The data store to unbind
27338      */
27339     unbind : function(ds){
27340         ds.un("beforeload", this.beforeLoad, this);
27341         ds.un("load", this.onLoad, this);
27342         ds.un("loadexception", this.onLoadError, this);
27343         ds.un("remove", this.updateInfo, this);
27344         ds.un("add", this.updateInfo, this);
27345         this.ds = undefined;
27346     },
27347
27348     /**
27349      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27350      * @param {Roo.data.Store} store The data store to bind
27351      */
27352     bind : function(ds){
27353         ds.on("beforeload", this.beforeLoad, this);
27354         ds.on("load", this.onLoad, this);
27355         ds.on("loadexception", this.onLoadError, this);
27356         ds.on("remove", this.updateInfo, this);
27357         ds.on("add", this.updateInfo, this);
27358         this.ds = ds;
27359     }
27360 });/*
27361  * - LGPL
27362  *
27363  * element
27364  * 
27365  */
27366
27367 /**
27368  * @class Roo.bootstrap.MessageBar
27369  * @extends Roo.bootstrap.Component
27370  * Bootstrap MessageBar class
27371  * @cfg {String} html contents of the MessageBar
27372  * @cfg {String} weight (info | success | warning | danger) default info
27373  * @cfg {String} beforeClass insert the bar before the given class
27374  * @cfg {Boolean} closable (true | false) default false
27375  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27376  * 
27377  * @constructor
27378  * Create a new Element
27379  * @param {Object} config The config object
27380  */
27381
27382 Roo.bootstrap.MessageBar = function(config){
27383     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27384 };
27385
27386 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27387     
27388     html: '',
27389     weight: 'info',
27390     closable: false,
27391     fixed: false,
27392     beforeClass: 'bootstrap-sticky-wrap',
27393     
27394     getAutoCreate : function(){
27395         
27396         var cfg = {
27397             tag: 'div',
27398             cls: 'alert alert-dismissable alert-' + this.weight,
27399             cn: [
27400                 {
27401                     tag: 'span',
27402                     cls: 'message',
27403                     html: this.html || ''
27404                 }
27405             ]
27406         };
27407         
27408         if(this.fixed){
27409             cfg.cls += ' alert-messages-fixed';
27410         }
27411         
27412         if(this.closable){
27413             cfg.cn.push({
27414                 tag: 'button',
27415                 cls: 'close',
27416                 html: 'x'
27417             });
27418         }
27419         
27420         return cfg;
27421     },
27422     
27423     onRender : function(ct, position)
27424     {
27425         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27426         
27427         if(!this.el){
27428             var cfg = Roo.apply({},  this.getAutoCreate());
27429             cfg.id = Roo.id();
27430             
27431             if (this.cls) {
27432                 cfg.cls += ' ' + this.cls;
27433             }
27434             if (this.style) {
27435                 cfg.style = this.style;
27436             }
27437             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27438             
27439             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27440         }
27441         
27442         this.el.select('>button.close').on('click', this.hide, this);
27443         
27444     },
27445     
27446     show : function()
27447     {
27448         if (!this.rendered) {
27449             this.render();
27450         }
27451         
27452         this.el.show();
27453         
27454         this.fireEvent('show', this);
27455         
27456     },
27457     
27458     hide : function()
27459     {
27460         if (!this.rendered) {
27461             this.render();
27462         }
27463         
27464         this.el.hide();
27465         
27466         this.fireEvent('hide', this);
27467     },
27468     
27469     update : function()
27470     {
27471 //        var e = this.el.dom.firstChild;
27472 //        
27473 //        if(this.closable){
27474 //            e = e.nextSibling;
27475 //        }
27476 //        
27477 //        e.data = this.html || '';
27478
27479         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27480     }
27481    
27482 });
27483
27484  
27485
27486      /*
27487  * - LGPL
27488  *
27489  * Graph
27490  * 
27491  */
27492
27493
27494 /**
27495  * @class Roo.bootstrap.Graph
27496  * @extends Roo.bootstrap.Component
27497  * Bootstrap Graph class
27498 > Prameters
27499  -sm {number} sm 4
27500  -md {number} md 5
27501  @cfg {String} graphtype  bar | vbar | pie
27502  @cfg {number} g_x coodinator | centre x (pie)
27503  @cfg {number} g_y coodinator | centre y (pie)
27504  @cfg {number} g_r radius (pie)
27505  @cfg {number} g_height height of the chart (respected by all elements in the set)
27506  @cfg {number} g_width width of the chart (respected by all elements in the set)
27507  @cfg {Object} title The title of the chart
27508     
27509  -{Array}  values
27510  -opts (object) options for the chart 
27511      o {
27512      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27513      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27514      o vgutter (number)
27515      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.
27516      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27517      o to
27518      o stretch (boolean)
27519      o }
27520  -opts (object) options for the pie
27521      o{
27522      o cut
27523      o startAngle (number)
27524      o endAngle (number)
27525      } 
27526  *
27527  * @constructor
27528  * Create a new Input
27529  * @param {Object} config The config object
27530  */
27531
27532 Roo.bootstrap.Graph = function(config){
27533     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27534     
27535     this.addEvents({
27536         // img events
27537         /**
27538          * @event click
27539          * The img click event for the img.
27540          * @param {Roo.EventObject} e
27541          */
27542         "click" : true
27543     });
27544 };
27545
27546 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27547     
27548     sm: 4,
27549     md: 5,
27550     graphtype: 'bar',
27551     g_height: 250,
27552     g_width: 400,
27553     g_x: 50,
27554     g_y: 50,
27555     g_r: 30,
27556     opts:{
27557         //g_colors: this.colors,
27558         g_type: 'soft',
27559         g_gutter: '20%'
27560
27561     },
27562     title : false,
27563
27564     getAutoCreate : function(){
27565         
27566         var cfg = {
27567             tag: 'div',
27568             html : null
27569         };
27570         
27571         
27572         return  cfg;
27573     },
27574
27575     onRender : function(ct,position){
27576         
27577         
27578         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27579         
27580         if (typeof(Raphael) == 'undefined') {
27581             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27582             return;
27583         }
27584         
27585         this.raphael = Raphael(this.el.dom);
27586         
27587                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27588                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27589                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27590                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27591                 /*
27592                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27593                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27594                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27595                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27596                 
27597                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27598                 r.barchart(330, 10, 300, 220, data1);
27599                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27600                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27601                 */
27602                 
27603                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27604                 // r.barchart(30, 30, 560, 250,  xdata, {
27605                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27606                 //     axis : "0 0 1 1",
27607                 //     axisxlabels :  xdata
27608                 //     //yvalues : cols,
27609                    
27610                 // });
27611 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27612 //        
27613 //        this.load(null,xdata,{
27614 //                axis : "0 0 1 1",
27615 //                axisxlabels :  xdata
27616 //                });
27617
27618     },
27619
27620     load : function(graphtype,xdata,opts)
27621     {
27622         this.raphael.clear();
27623         if(!graphtype) {
27624             graphtype = this.graphtype;
27625         }
27626         if(!opts){
27627             opts = this.opts;
27628         }
27629         var r = this.raphael,
27630             fin = function () {
27631                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27632             },
27633             fout = function () {
27634                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27635             },
27636             pfin = function() {
27637                 this.sector.stop();
27638                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27639
27640                 if (this.label) {
27641                     this.label[0].stop();
27642                     this.label[0].attr({ r: 7.5 });
27643                     this.label[1].attr({ "font-weight": 800 });
27644                 }
27645             },
27646             pfout = function() {
27647                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27648
27649                 if (this.label) {
27650                     this.label[0].animate({ r: 5 }, 500, "bounce");
27651                     this.label[1].attr({ "font-weight": 400 });
27652                 }
27653             };
27654
27655         switch(graphtype){
27656             case 'bar':
27657                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27658                 break;
27659             case 'hbar':
27660                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27661                 break;
27662             case 'pie':
27663 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27664 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27665 //            
27666                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27667                 
27668                 break;
27669
27670         }
27671         
27672         if(this.title){
27673             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27674         }
27675         
27676     },
27677     
27678     setTitle: function(o)
27679     {
27680         this.title = o;
27681     },
27682     
27683     initEvents: function() {
27684         
27685         if(!this.href){
27686             this.el.on('click', this.onClick, this);
27687         }
27688     },
27689     
27690     onClick : function(e)
27691     {
27692         Roo.log('img onclick');
27693         this.fireEvent('click', this, e);
27694     }
27695    
27696 });
27697
27698  
27699 /*
27700  * - LGPL
27701  *
27702  * numberBox
27703  * 
27704  */
27705 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27706
27707 /**
27708  * @class Roo.bootstrap.dash.NumberBox
27709  * @extends Roo.bootstrap.Component
27710  * Bootstrap NumberBox class
27711  * @cfg {String} headline Box headline
27712  * @cfg {String} content Box content
27713  * @cfg {String} icon Box icon
27714  * @cfg {String} footer Footer text
27715  * @cfg {String} fhref Footer href
27716  * 
27717  * @constructor
27718  * Create a new NumberBox
27719  * @param {Object} config The config object
27720  */
27721
27722
27723 Roo.bootstrap.dash.NumberBox = function(config){
27724     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27725     
27726 };
27727
27728 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27729     
27730     headline : '',
27731     content : '',
27732     icon : '',
27733     footer : '',
27734     fhref : '',
27735     ficon : '',
27736     
27737     getAutoCreate : function(){
27738         
27739         var cfg = {
27740             tag : 'div',
27741             cls : 'small-box ',
27742             cn : [
27743                 {
27744                     tag : 'div',
27745                     cls : 'inner',
27746                     cn :[
27747                         {
27748                             tag : 'h3',
27749                             cls : 'roo-headline',
27750                             html : this.headline
27751                         },
27752                         {
27753                             tag : 'p',
27754                             cls : 'roo-content',
27755                             html : this.content
27756                         }
27757                     ]
27758                 }
27759             ]
27760         };
27761         
27762         if(this.icon){
27763             cfg.cn.push({
27764                 tag : 'div',
27765                 cls : 'icon',
27766                 cn :[
27767                     {
27768                         tag : 'i',
27769                         cls : 'ion ' + this.icon
27770                     }
27771                 ]
27772             });
27773         }
27774         
27775         if(this.footer){
27776             var footer = {
27777                 tag : 'a',
27778                 cls : 'small-box-footer',
27779                 href : this.fhref || '#',
27780                 html : this.footer
27781             };
27782             
27783             cfg.cn.push(footer);
27784             
27785         }
27786         
27787         return  cfg;
27788     },
27789
27790     onRender : function(ct,position){
27791         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27792
27793
27794        
27795                 
27796     },
27797
27798     setHeadline: function (value)
27799     {
27800         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27801     },
27802     
27803     setFooter: function (value, href)
27804     {
27805         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27806         
27807         if(href){
27808             this.el.select('a.small-box-footer',true).first().attr('href', href);
27809         }
27810         
27811     },
27812
27813     setContent: function (value)
27814     {
27815         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27816     },
27817
27818     initEvents: function() 
27819     {   
27820         
27821     }
27822     
27823 });
27824
27825  
27826 /*
27827  * - LGPL
27828  *
27829  * TabBox
27830  * 
27831  */
27832 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27833
27834 /**
27835  * @class Roo.bootstrap.dash.TabBox
27836  * @extends Roo.bootstrap.Component
27837  * Bootstrap TabBox class
27838  * @cfg {String} title Title of the TabBox
27839  * @cfg {String} icon Icon of the TabBox
27840  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27841  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27842  * 
27843  * @constructor
27844  * Create a new TabBox
27845  * @param {Object} config The config object
27846  */
27847
27848
27849 Roo.bootstrap.dash.TabBox = function(config){
27850     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27851     this.addEvents({
27852         // raw events
27853         /**
27854          * @event addpane
27855          * When a pane is added
27856          * @param {Roo.bootstrap.dash.TabPane} pane
27857          */
27858         "addpane" : true,
27859         /**
27860          * @event activatepane
27861          * When a pane is activated
27862          * @param {Roo.bootstrap.dash.TabPane} pane
27863          */
27864         "activatepane" : true
27865         
27866          
27867     });
27868     
27869     this.panes = [];
27870 };
27871
27872 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27873
27874     title : '',
27875     icon : false,
27876     showtabs : true,
27877     tabScrollable : false,
27878     
27879     getChildContainer : function()
27880     {
27881         return this.el.select('.tab-content', true).first();
27882     },
27883     
27884     getAutoCreate : function(){
27885         
27886         var header = {
27887             tag: 'li',
27888             cls: 'pull-left header',
27889             html: this.title,
27890             cn : []
27891         };
27892         
27893         if(this.icon){
27894             header.cn.push({
27895                 tag: 'i',
27896                 cls: 'fa ' + this.icon
27897             });
27898         }
27899         
27900         var h = {
27901             tag: 'ul',
27902             cls: 'nav nav-tabs pull-right',
27903             cn: [
27904                 header
27905             ]
27906         };
27907         
27908         if(this.tabScrollable){
27909             h = {
27910                 tag: 'div',
27911                 cls: 'tab-header',
27912                 cn: [
27913                     {
27914                         tag: 'ul',
27915                         cls: 'nav nav-tabs pull-right',
27916                         cn: [
27917                             header
27918                         ]
27919                     }
27920                 ]
27921             };
27922         }
27923         
27924         var cfg = {
27925             tag: 'div',
27926             cls: 'nav-tabs-custom',
27927             cn: [
27928                 h,
27929                 {
27930                     tag: 'div',
27931                     cls: 'tab-content no-padding',
27932                     cn: []
27933                 }
27934             ]
27935         };
27936
27937         return  cfg;
27938     },
27939     initEvents : function()
27940     {
27941         //Roo.log('add add pane handler');
27942         this.on('addpane', this.onAddPane, this);
27943     },
27944      /**
27945      * Updates the box title
27946      * @param {String} html to set the title to.
27947      */
27948     setTitle : function(value)
27949     {
27950         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27951     },
27952     onAddPane : function(pane)
27953     {
27954         this.panes.push(pane);
27955         //Roo.log('addpane');
27956         //Roo.log(pane);
27957         // tabs are rendere left to right..
27958         if(!this.showtabs){
27959             return;
27960         }
27961         
27962         var ctr = this.el.select('.nav-tabs', true).first();
27963          
27964          
27965         var existing = ctr.select('.nav-tab',true);
27966         var qty = existing.getCount();;
27967         
27968         
27969         var tab = ctr.createChild({
27970             tag : 'li',
27971             cls : 'nav-tab' + (qty ? '' : ' active'),
27972             cn : [
27973                 {
27974                     tag : 'a',
27975                     href:'#',
27976                     html : pane.title
27977                 }
27978             ]
27979         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27980         pane.tab = tab;
27981         
27982         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27983         if (!qty) {
27984             pane.el.addClass('active');
27985         }
27986         
27987                 
27988     },
27989     onTabClick : function(ev,un,ob,pane)
27990     {
27991         //Roo.log('tab - prev default');
27992         ev.preventDefault();
27993         
27994         
27995         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27996         pane.tab.addClass('active');
27997         //Roo.log(pane.title);
27998         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27999         // technically we should have a deactivate event.. but maybe add later.
28000         // and it should not de-activate the selected tab...
28001         this.fireEvent('activatepane', pane);
28002         pane.el.addClass('active');
28003         pane.fireEvent('activate');
28004         
28005         
28006     },
28007     
28008     getActivePane : function()
28009     {
28010         var r = false;
28011         Roo.each(this.panes, function(p) {
28012             if(p.el.hasClass('active')){
28013                 r = p;
28014                 return false;
28015             }
28016             
28017             return;
28018         });
28019         
28020         return r;
28021     }
28022     
28023     
28024 });
28025
28026  
28027 /*
28028  * - LGPL
28029  *
28030  * Tab pane
28031  * 
28032  */
28033 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28034 /**
28035  * @class Roo.bootstrap.TabPane
28036  * @extends Roo.bootstrap.Component
28037  * Bootstrap TabPane class
28038  * @cfg {Boolean} active (false | true) Default false
28039  * @cfg {String} title title of panel
28040
28041  * 
28042  * @constructor
28043  * Create a new TabPane
28044  * @param {Object} config The config object
28045  */
28046
28047 Roo.bootstrap.dash.TabPane = function(config){
28048     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28049     
28050     this.addEvents({
28051         // raw events
28052         /**
28053          * @event activate
28054          * When a pane is activated
28055          * @param {Roo.bootstrap.dash.TabPane} pane
28056          */
28057         "activate" : true
28058          
28059     });
28060 };
28061
28062 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28063     
28064     active : false,
28065     title : '',
28066     
28067     // the tabBox that this is attached to.
28068     tab : false,
28069      
28070     getAutoCreate : function() 
28071     {
28072         var cfg = {
28073             tag: 'div',
28074             cls: 'tab-pane'
28075         };
28076         
28077         if(this.active){
28078             cfg.cls += ' active';
28079         }
28080         
28081         return cfg;
28082     },
28083     initEvents  : function()
28084     {
28085         //Roo.log('trigger add pane handler');
28086         this.parent().fireEvent('addpane', this)
28087     },
28088     
28089      /**
28090      * Updates the tab title 
28091      * @param {String} html to set the title to.
28092      */
28093     setTitle: function(str)
28094     {
28095         if (!this.tab) {
28096             return;
28097         }
28098         this.title = str;
28099         this.tab.select('a', true).first().dom.innerHTML = str;
28100         
28101     }
28102     
28103     
28104     
28105 });
28106
28107  
28108
28109
28110  /*
28111  * - LGPL
28112  *
28113  * menu
28114  * 
28115  */
28116 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28117
28118 /**
28119  * @class Roo.bootstrap.menu.Menu
28120  * @extends Roo.bootstrap.Component
28121  * Bootstrap Menu class - container for Menu
28122  * @cfg {String} html Text of the menu
28123  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28124  * @cfg {String} icon Font awesome icon
28125  * @cfg {String} pos Menu align to (top | bottom) default bottom
28126  * 
28127  * 
28128  * @constructor
28129  * Create a new Menu
28130  * @param {Object} config The config object
28131  */
28132
28133
28134 Roo.bootstrap.menu.Menu = function(config){
28135     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28136     
28137     this.addEvents({
28138         /**
28139          * @event beforeshow
28140          * Fires before this menu is displayed
28141          * @param {Roo.bootstrap.menu.Menu} this
28142          */
28143         beforeshow : true,
28144         /**
28145          * @event beforehide
28146          * Fires before this menu is hidden
28147          * @param {Roo.bootstrap.menu.Menu} this
28148          */
28149         beforehide : true,
28150         /**
28151          * @event show
28152          * Fires after this menu is displayed
28153          * @param {Roo.bootstrap.menu.Menu} this
28154          */
28155         show : true,
28156         /**
28157          * @event hide
28158          * Fires after this menu is hidden
28159          * @param {Roo.bootstrap.menu.Menu} this
28160          */
28161         hide : true,
28162         /**
28163          * @event click
28164          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28165          * @param {Roo.bootstrap.menu.Menu} this
28166          * @param {Roo.EventObject} e
28167          */
28168         click : true
28169     });
28170     
28171 };
28172
28173 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28174     
28175     submenu : false,
28176     html : '',
28177     weight : 'default',
28178     icon : false,
28179     pos : 'bottom',
28180     
28181     
28182     getChildContainer : function() {
28183         if(this.isSubMenu){
28184             return this.el;
28185         }
28186         
28187         return this.el.select('ul.dropdown-menu', true).first();  
28188     },
28189     
28190     getAutoCreate : function()
28191     {
28192         var text = [
28193             {
28194                 tag : 'span',
28195                 cls : 'roo-menu-text',
28196                 html : this.html
28197             }
28198         ];
28199         
28200         if(this.icon){
28201             text.unshift({
28202                 tag : 'i',
28203                 cls : 'fa ' + this.icon
28204             })
28205         }
28206         
28207         
28208         var cfg = {
28209             tag : 'div',
28210             cls : 'btn-group',
28211             cn : [
28212                 {
28213                     tag : 'button',
28214                     cls : 'dropdown-button btn btn-' + this.weight,
28215                     cn : text
28216                 },
28217                 {
28218                     tag : 'button',
28219                     cls : 'dropdown-toggle btn btn-' + this.weight,
28220                     cn : [
28221                         {
28222                             tag : 'span',
28223                             cls : 'caret'
28224                         }
28225                     ]
28226                 },
28227                 {
28228                     tag : 'ul',
28229                     cls : 'dropdown-menu'
28230                 }
28231             ]
28232             
28233         };
28234         
28235         if(this.pos == 'top'){
28236             cfg.cls += ' dropup';
28237         }
28238         
28239         if(this.isSubMenu){
28240             cfg = {
28241                 tag : 'ul',
28242                 cls : 'dropdown-menu'
28243             }
28244         }
28245         
28246         return cfg;
28247     },
28248     
28249     onRender : function(ct, position)
28250     {
28251         this.isSubMenu = ct.hasClass('dropdown-submenu');
28252         
28253         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28254     },
28255     
28256     initEvents : function() 
28257     {
28258         if(this.isSubMenu){
28259             return;
28260         }
28261         
28262         this.hidden = true;
28263         
28264         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28265         this.triggerEl.on('click', this.onTriggerPress, this);
28266         
28267         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28268         this.buttonEl.on('click', this.onClick, this);
28269         
28270     },
28271     
28272     list : function()
28273     {
28274         if(this.isSubMenu){
28275             return this.el;
28276         }
28277         
28278         return this.el.select('ul.dropdown-menu', true).first();
28279     },
28280     
28281     onClick : function(e)
28282     {
28283         this.fireEvent("click", this, e);
28284     },
28285     
28286     onTriggerPress  : function(e)
28287     {   
28288         if (this.isVisible()) {
28289             this.hide();
28290         } else {
28291             this.show();
28292         }
28293     },
28294     
28295     isVisible : function(){
28296         return !this.hidden;
28297     },
28298     
28299     show : function()
28300     {
28301         this.fireEvent("beforeshow", this);
28302         
28303         this.hidden = false;
28304         this.el.addClass('open');
28305         
28306         Roo.get(document).on("mouseup", this.onMouseUp, this);
28307         
28308         this.fireEvent("show", this);
28309         
28310         
28311     },
28312     
28313     hide : function()
28314     {
28315         this.fireEvent("beforehide", this);
28316         
28317         this.hidden = true;
28318         this.el.removeClass('open');
28319         
28320         Roo.get(document).un("mouseup", this.onMouseUp);
28321         
28322         this.fireEvent("hide", this);
28323     },
28324     
28325     onMouseUp : function()
28326     {
28327         this.hide();
28328     }
28329     
28330 });
28331
28332  
28333  /*
28334  * - LGPL
28335  *
28336  * menu item
28337  * 
28338  */
28339 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28340
28341 /**
28342  * @class Roo.bootstrap.menu.Item
28343  * @extends Roo.bootstrap.Component
28344  * Bootstrap MenuItem class
28345  * @cfg {Boolean} submenu (true | false) default false
28346  * @cfg {String} html text of the item
28347  * @cfg {String} href the link
28348  * @cfg {Boolean} disable (true | false) default false
28349  * @cfg {Boolean} preventDefault (true | false) default true
28350  * @cfg {String} icon Font awesome icon
28351  * @cfg {String} pos Submenu align to (left | right) default right 
28352  * 
28353  * 
28354  * @constructor
28355  * Create a new Item
28356  * @param {Object} config The config object
28357  */
28358
28359
28360 Roo.bootstrap.menu.Item = function(config){
28361     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28362     this.addEvents({
28363         /**
28364          * @event mouseover
28365          * Fires when the mouse is hovering over this menu
28366          * @param {Roo.bootstrap.menu.Item} this
28367          * @param {Roo.EventObject} e
28368          */
28369         mouseover : true,
28370         /**
28371          * @event mouseout
28372          * Fires when the mouse exits this menu
28373          * @param {Roo.bootstrap.menu.Item} this
28374          * @param {Roo.EventObject} e
28375          */
28376         mouseout : true,
28377         // raw events
28378         /**
28379          * @event click
28380          * The raw click event for the entire grid.
28381          * @param {Roo.EventObject} e
28382          */
28383         click : true
28384     });
28385 };
28386
28387 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28388     
28389     submenu : false,
28390     href : '',
28391     html : '',
28392     preventDefault: true,
28393     disable : false,
28394     icon : false,
28395     pos : 'right',
28396     
28397     getAutoCreate : function()
28398     {
28399         var text = [
28400             {
28401                 tag : 'span',
28402                 cls : 'roo-menu-item-text',
28403                 html : this.html
28404             }
28405         ];
28406         
28407         if(this.icon){
28408             text.unshift({
28409                 tag : 'i',
28410                 cls : 'fa ' + this.icon
28411             })
28412         }
28413         
28414         var cfg = {
28415             tag : 'li',
28416             cn : [
28417                 {
28418                     tag : 'a',
28419                     href : this.href || '#',
28420                     cn : text
28421                 }
28422             ]
28423         };
28424         
28425         if(this.disable){
28426             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28427         }
28428         
28429         if(this.submenu){
28430             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28431             
28432             if(this.pos == 'left'){
28433                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28434             }
28435         }
28436         
28437         return cfg;
28438     },
28439     
28440     initEvents : function() 
28441     {
28442         this.el.on('mouseover', this.onMouseOver, this);
28443         this.el.on('mouseout', this.onMouseOut, this);
28444         
28445         this.el.select('a', true).first().on('click', this.onClick, this);
28446         
28447     },
28448     
28449     onClick : function(e)
28450     {
28451         if(this.preventDefault){
28452             e.preventDefault();
28453         }
28454         
28455         this.fireEvent("click", this, e);
28456     },
28457     
28458     onMouseOver : function(e)
28459     {
28460         if(this.submenu && this.pos == 'left'){
28461             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28462         }
28463         
28464         this.fireEvent("mouseover", this, e);
28465     },
28466     
28467     onMouseOut : function(e)
28468     {
28469         this.fireEvent("mouseout", this, e);
28470     }
28471 });
28472
28473  
28474
28475  /*
28476  * - LGPL
28477  *
28478  * menu separator
28479  * 
28480  */
28481 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28482
28483 /**
28484  * @class Roo.bootstrap.menu.Separator
28485  * @extends Roo.bootstrap.Component
28486  * Bootstrap Separator class
28487  * 
28488  * @constructor
28489  * Create a new Separator
28490  * @param {Object} config The config object
28491  */
28492
28493
28494 Roo.bootstrap.menu.Separator = function(config){
28495     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28496 };
28497
28498 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28499     
28500     getAutoCreate : function(){
28501         var cfg = {
28502             tag : 'li',
28503             cls: 'divider'
28504         };
28505         
28506         return cfg;
28507     }
28508    
28509 });
28510
28511  
28512
28513  /*
28514  * - LGPL
28515  *
28516  * Tooltip
28517  * 
28518  */
28519
28520 /**
28521  * @class Roo.bootstrap.Tooltip
28522  * Bootstrap Tooltip class
28523  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28524  * to determine which dom element triggers the tooltip.
28525  * 
28526  * It needs to add support for additional attributes like tooltip-position
28527  * 
28528  * @constructor
28529  * Create a new Toolti
28530  * @param {Object} config The config object
28531  */
28532
28533 Roo.bootstrap.Tooltip = function(config){
28534     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28535     
28536     this.alignment = Roo.bootstrap.Tooltip.alignment;
28537     
28538     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28539         this.alignment = config.alignment;
28540     }
28541     
28542 };
28543
28544 Roo.apply(Roo.bootstrap.Tooltip, {
28545     /**
28546      * @function init initialize tooltip monitoring.
28547      * @static
28548      */
28549     currentEl : false,
28550     currentTip : false,
28551     currentRegion : false,
28552     
28553     //  init : delay?
28554     
28555     init : function()
28556     {
28557         Roo.get(document).on('mouseover', this.enter ,this);
28558         Roo.get(document).on('mouseout', this.leave, this);
28559          
28560         
28561         this.currentTip = new Roo.bootstrap.Tooltip();
28562     },
28563     
28564     enter : function(ev)
28565     {
28566         var dom = ev.getTarget();
28567         
28568         //Roo.log(['enter',dom]);
28569         var el = Roo.fly(dom);
28570         if (this.currentEl) {
28571             //Roo.log(dom);
28572             //Roo.log(this.currentEl);
28573             //Roo.log(this.currentEl.contains(dom));
28574             if (this.currentEl == el) {
28575                 return;
28576             }
28577             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28578                 return;
28579             }
28580
28581         }
28582         
28583         if (this.currentTip.el) {
28584             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28585         }    
28586         //Roo.log(ev);
28587         
28588         if(!el || el.dom == document){
28589             return;
28590         }
28591         
28592         var bindEl = el;
28593         
28594         // you can not look for children, as if el is the body.. then everythign is the child..
28595         if (!el.attr('tooltip')) { //
28596             if (!el.select("[tooltip]").elements.length) {
28597                 return;
28598             }
28599             // is the mouse over this child...?
28600             bindEl = el.select("[tooltip]").first();
28601             var xy = ev.getXY();
28602             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28603                 //Roo.log("not in region.");
28604                 return;
28605             }
28606             //Roo.log("child element over..");
28607             
28608         }
28609         this.currentEl = bindEl;
28610         this.currentTip.bind(bindEl);
28611         this.currentRegion = Roo.lib.Region.getRegion(dom);
28612         this.currentTip.enter();
28613         
28614     },
28615     leave : function(ev)
28616     {
28617         var dom = ev.getTarget();
28618         //Roo.log(['leave',dom]);
28619         if (!this.currentEl) {
28620             return;
28621         }
28622         
28623         
28624         if (dom != this.currentEl.dom) {
28625             return;
28626         }
28627         var xy = ev.getXY();
28628         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28629             return;
28630         }
28631         // only activate leave if mouse cursor is outside... bounding box..
28632         
28633         
28634         
28635         
28636         if (this.currentTip) {
28637             this.currentTip.leave();
28638         }
28639         //Roo.log('clear currentEl');
28640         this.currentEl = false;
28641         
28642         
28643     },
28644     alignment : {
28645         'left' : ['r-l', [-2,0], 'right'],
28646         'right' : ['l-r', [2,0], 'left'],
28647         'bottom' : ['t-b', [0,2], 'top'],
28648         'top' : [ 'b-t', [0,-2], 'bottom']
28649     }
28650     
28651 });
28652
28653
28654 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28655     
28656     
28657     bindEl : false,
28658     
28659     delay : null, // can be { show : 300 , hide: 500}
28660     
28661     timeout : null,
28662     
28663     hoverState : null, //???
28664     
28665     placement : 'bottom', 
28666     
28667     alignment : false,
28668     
28669     getAutoCreate : function(){
28670     
28671         var cfg = {
28672            cls : 'tooltip',   
28673            role : 'tooltip',
28674            cn : [
28675                 {
28676                     cls : 'tooltip-arrow arrow'
28677                 },
28678                 {
28679                     cls : 'tooltip-inner'
28680                 }
28681            ]
28682         };
28683         
28684         return cfg;
28685     },
28686     bind : function(el)
28687     {
28688         this.bindEl = el;
28689     },
28690     
28691     initEvents : function()
28692     {
28693         this.arrowEl = this.el.select('.arrow', true).first();
28694         this.innerEl = this.el.select('.tooltip-inner', true).first();
28695     },
28696     
28697     enter : function () {
28698        
28699         if (this.timeout != null) {
28700             clearTimeout(this.timeout);
28701         }
28702         
28703         this.hoverState = 'in';
28704          //Roo.log("enter - show");
28705         if (!this.delay || !this.delay.show) {
28706             this.show();
28707             return;
28708         }
28709         var _t = this;
28710         this.timeout = setTimeout(function () {
28711             if (_t.hoverState == 'in') {
28712                 _t.show();
28713             }
28714         }, this.delay.show);
28715     },
28716     leave : function()
28717     {
28718         clearTimeout(this.timeout);
28719     
28720         this.hoverState = 'out';
28721          if (!this.delay || !this.delay.hide) {
28722             this.hide();
28723             return;
28724         }
28725        
28726         var _t = this;
28727         this.timeout = setTimeout(function () {
28728             //Roo.log("leave - timeout");
28729             
28730             if (_t.hoverState == 'out') {
28731                 _t.hide();
28732                 Roo.bootstrap.Tooltip.currentEl = false;
28733             }
28734         }, delay);
28735     },
28736     
28737     show : function (msg)
28738     {
28739         if (!this.el) {
28740             this.render(document.body);
28741         }
28742         // set content.
28743         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28744         
28745         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28746         
28747         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28748         
28749         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28750                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28751         
28752         var placement = typeof this.placement == 'function' ?
28753             this.placement.call(this, this.el, on_el) :
28754             this.placement;
28755             
28756         var autoToken = /\s?auto?\s?/i;
28757         var autoPlace = autoToken.test(placement);
28758         if (autoPlace) {
28759             placement = placement.replace(autoToken, '') || 'top';
28760         }
28761         
28762         //this.el.detach()
28763         //this.el.setXY([0,0]);
28764         this.el.show();
28765         //this.el.dom.style.display='block';
28766         
28767         //this.el.appendTo(on_el);
28768         
28769         var p = this.getPosition();
28770         var box = this.el.getBox();
28771         
28772         if (autoPlace) {
28773             // fixme..
28774         }
28775         
28776         var align = this.alignment[placement];
28777         
28778         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28779         
28780         if(placement == 'top' || placement == 'bottom'){
28781             if(xy[0] < 0){
28782                 placement = 'right';
28783             }
28784             
28785             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28786                 placement = 'left';
28787             }
28788             
28789             var scroll = Roo.select('body', true).first().getScroll();
28790             
28791             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28792                 placement = 'top';
28793             }
28794             
28795             align = this.alignment[placement];
28796             
28797             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28798             
28799         }
28800         
28801         this.el.alignTo(this.bindEl, align[0],align[1]);
28802         //var arrow = this.el.select('.arrow',true).first();
28803         //arrow.set(align[2], 
28804         
28805         this.el.addClass(placement);
28806         this.el.addClass("bs-tooltip-"+ placement);
28807         
28808         this.el.addClass('in fade show');
28809         
28810         this.hoverState = null;
28811         
28812         if (this.el.hasClass('fade')) {
28813             // fade it?
28814         }
28815         
28816         
28817         
28818         
28819         
28820     },
28821     hide : function()
28822     {
28823          
28824         if (!this.el) {
28825             return;
28826         }
28827         //this.el.setXY([0,0]);
28828         this.el.removeClass(['show', 'in']);
28829         //this.el.hide();
28830         
28831     }
28832     
28833 });
28834  
28835
28836  /*
28837  * - LGPL
28838  *
28839  * Location Picker
28840  * 
28841  */
28842
28843 /**
28844  * @class Roo.bootstrap.LocationPicker
28845  * @extends Roo.bootstrap.Component
28846  * Bootstrap LocationPicker class
28847  * @cfg {Number} latitude Position when init default 0
28848  * @cfg {Number} longitude Position when init default 0
28849  * @cfg {Number} zoom default 15
28850  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28851  * @cfg {Boolean} mapTypeControl default false
28852  * @cfg {Boolean} disableDoubleClickZoom default false
28853  * @cfg {Boolean} scrollwheel default true
28854  * @cfg {Boolean} streetViewControl default false
28855  * @cfg {Number} radius default 0
28856  * @cfg {String} locationName
28857  * @cfg {Boolean} draggable default true
28858  * @cfg {Boolean} enableAutocomplete default false
28859  * @cfg {Boolean} enableReverseGeocode default true
28860  * @cfg {String} markerTitle
28861  * 
28862  * @constructor
28863  * Create a new LocationPicker
28864  * @param {Object} config The config object
28865  */
28866
28867
28868 Roo.bootstrap.LocationPicker = function(config){
28869     
28870     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28871     
28872     this.addEvents({
28873         /**
28874          * @event initial
28875          * Fires when the picker initialized.
28876          * @param {Roo.bootstrap.LocationPicker} this
28877          * @param {Google Location} location
28878          */
28879         initial : true,
28880         /**
28881          * @event positionchanged
28882          * Fires when the picker position changed.
28883          * @param {Roo.bootstrap.LocationPicker} this
28884          * @param {Google Location} location
28885          */
28886         positionchanged : true,
28887         /**
28888          * @event resize
28889          * Fires when the map resize.
28890          * @param {Roo.bootstrap.LocationPicker} this
28891          */
28892         resize : true,
28893         /**
28894          * @event show
28895          * Fires when the map show.
28896          * @param {Roo.bootstrap.LocationPicker} this
28897          */
28898         show : true,
28899         /**
28900          * @event hide
28901          * Fires when the map hide.
28902          * @param {Roo.bootstrap.LocationPicker} this
28903          */
28904         hide : true,
28905         /**
28906          * @event mapClick
28907          * Fires when click the map.
28908          * @param {Roo.bootstrap.LocationPicker} this
28909          * @param {Map event} e
28910          */
28911         mapClick : true,
28912         /**
28913          * @event mapRightClick
28914          * Fires when right click the map.
28915          * @param {Roo.bootstrap.LocationPicker} this
28916          * @param {Map event} e
28917          */
28918         mapRightClick : true,
28919         /**
28920          * @event markerClick
28921          * Fires when click the marker.
28922          * @param {Roo.bootstrap.LocationPicker} this
28923          * @param {Map event} e
28924          */
28925         markerClick : true,
28926         /**
28927          * @event markerRightClick
28928          * Fires when right click the marker.
28929          * @param {Roo.bootstrap.LocationPicker} this
28930          * @param {Map event} e
28931          */
28932         markerRightClick : true,
28933         /**
28934          * @event OverlayViewDraw
28935          * Fires when OverlayView Draw
28936          * @param {Roo.bootstrap.LocationPicker} this
28937          */
28938         OverlayViewDraw : true,
28939         /**
28940          * @event OverlayViewOnAdd
28941          * Fires when OverlayView Draw
28942          * @param {Roo.bootstrap.LocationPicker} this
28943          */
28944         OverlayViewOnAdd : true,
28945         /**
28946          * @event OverlayViewOnRemove
28947          * Fires when OverlayView Draw
28948          * @param {Roo.bootstrap.LocationPicker} this
28949          */
28950         OverlayViewOnRemove : true,
28951         /**
28952          * @event OverlayViewShow
28953          * Fires when OverlayView Draw
28954          * @param {Roo.bootstrap.LocationPicker} this
28955          * @param {Pixel} cpx
28956          */
28957         OverlayViewShow : true,
28958         /**
28959          * @event OverlayViewHide
28960          * Fires when OverlayView Draw
28961          * @param {Roo.bootstrap.LocationPicker} this
28962          */
28963         OverlayViewHide : true,
28964         /**
28965          * @event loadexception
28966          * Fires when load google lib failed.
28967          * @param {Roo.bootstrap.LocationPicker} this
28968          */
28969         loadexception : true
28970     });
28971         
28972 };
28973
28974 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28975     
28976     gMapContext: false,
28977     
28978     latitude: 0,
28979     longitude: 0,
28980     zoom: 15,
28981     mapTypeId: false,
28982     mapTypeControl: false,
28983     disableDoubleClickZoom: false,
28984     scrollwheel: true,
28985     streetViewControl: false,
28986     radius: 0,
28987     locationName: '',
28988     draggable: true,
28989     enableAutocomplete: false,
28990     enableReverseGeocode: true,
28991     markerTitle: '',
28992     
28993     getAutoCreate: function()
28994     {
28995
28996         var cfg = {
28997             tag: 'div',
28998             cls: 'roo-location-picker'
28999         };
29000         
29001         return cfg
29002     },
29003     
29004     initEvents: function(ct, position)
29005     {       
29006         if(!this.el.getWidth() || this.isApplied()){
29007             return;
29008         }
29009         
29010         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29011         
29012         this.initial();
29013     },
29014     
29015     initial: function()
29016     {
29017         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29018             this.fireEvent('loadexception', this);
29019             return;
29020         }
29021         
29022         if(!this.mapTypeId){
29023             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29024         }
29025         
29026         this.gMapContext = this.GMapContext();
29027         
29028         this.initOverlayView();
29029         
29030         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29031         
29032         var _this = this;
29033                 
29034         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29035             _this.setPosition(_this.gMapContext.marker.position);
29036         });
29037         
29038         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29039             _this.fireEvent('mapClick', this, event);
29040             
29041         });
29042
29043         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29044             _this.fireEvent('mapRightClick', this, event);
29045             
29046         });
29047         
29048         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29049             _this.fireEvent('markerClick', this, event);
29050             
29051         });
29052
29053         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29054             _this.fireEvent('markerRightClick', this, event);
29055             
29056         });
29057         
29058         this.setPosition(this.gMapContext.location);
29059         
29060         this.fireEvent('initial', this, this.gMapContext.location);
29061     },
29062     
29063     initOverlayView: function()
29064     {
29065         var _this = this;
29066         
29067         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29068             
29069             draw: function()
29070             {
29071                 _this.fireEvent('OverlayViewDraw', _this);
29072             },
29073             
29074             onAdd: function()
29075             {
29076                 _this.fireEvent('OverlayViewOnAdd', _this);
29077             },
29078             
29079             onRemove: function()
29080             {
29081                 _this.fireEvent('OverlayViewOnRemove', _this);
29082             },
29083             
29084             show: function(cpx)
29085             {
29086                 _this.fireEvent('OverlayViewShow', _this, cpx);
29087             },
29088             
29089             hide: function()
29090             {
29091                 _this.fireEvent('OverlayViewHide', _this);
29092             }
29093             
29094         });
29095     },
29096     
29097     fromLatLngToContainerPixel: function(event)
29098     {
29099         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29100     },
29101     
29102     isApplied: function() 
29103     {
29104         return this.getGmapContext() == false ? false : true;
29105     },
29106     
29107     getGmapContext: function() 
29108     {
29109         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29110     },
29111     
29112     GMapContext: function() 
29113     {
29114         var position = new google.maps.LatLng(this.latitude, this.longitude);
29115         
29116         var _map = new google.maps.Map(this.el.dom, {
29117             center: position,
29118             zoom: this.zoom,
29119             mapTypeId: this.mapTypeId,
29120             mapTypeControl: this.mapTypeControl,
29121             disableDoubleClickZoom: this.disableDoubleClickZoom,
29122             scrollwheel: this.scrollwheel,
29123             streetViewControl: this.streetViewControl,
29124             locationName: this.locationName,
29125             draggable: this.draggable,
29126             enableAutocomplete: this.enableAutocomplete,
29127             enableReverseGeocode: this.enableReverseGeocode
29128         });
29129         
29130         var _marker = new google.maps.Marker({
29131             position: position,
29132             map: _map,
29133             title: this.markerTitle,
29134             draggable: this.draggable
29135         });
29136         
29137         return {
29138             map: _map,
29139             marker: _marker,
29140             circle: null,
29141             location: position,
29142             radius: this.radius,
29143             locationName: this.locationName,
29144             addressComponents: {
29145                 formatted_address: null,
29146                 addressLine1: null,
29147                 addressLine2: null,
29148                 streetName: null,
29149                 streetNumber: null,
29150                 city: null,
29151                 district: null,
29152                 state: null,
29153                 stateOrProvince: null
29154             },
29155             settings: this,
29156             domContainer: this.el.dom,
29157             geodecoder: new google.maps.Geocoder()
29158         };
29159     },
29160     
29161     drawCircle: function(center, radius, options) 
29162     {
29163         if (this.gMapContext.circle != null) {
29164             this.gMapContext.circle.setMap(null);
29165         }
29166         if (radius > 0) {
29167             radius *= 1;
29168             options = Roo.apply({}, options, {
29169                 strokeColor: "#0000FF",
29170                 strokeOpacity: .35,
29171                 strokeWeight: 2,
29172                 fillColor: "#0000FF",
29173                 fillOpacity: .2
29174             });
29175             
29176             options.map = this.gMapContext.map;
29177             options.radius = radius;
29178             options.center = center;
29179             this.gMapContext.circle = new google.maps.Circle(options);
29180             return this.gMapContext.circle;
29181         }
29182         
29183         return null;
29184     },
29185     
29186     setPosition: function(location) 
29187     {
29188         this.gMapContext.location = location;
29189         this.gMapContext.marker.setPosition(location);
29190         this.gMapContext.map.panTo(location);
29191         this.drawCircle(location, this.gMapContext.radius, {});
29192         
29193         var _this = this;
29194         
29195         if (this.gMapContext.settings.enableReverseGeocode) {
29196             this.gMapContext.geodecoder.geocode({
29197                 latLng: this.gMapContext.location
29198             }, function(results, status) {
29199                 
29200                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29201                     _this.gMapContext.locationName = results[0].formatted_address;
29202                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29203                     
29204                     _this.fireEvent('positionchanged', this, location);
29205                 }
29206             });
29207             
29208             return;
29209         }
29210         
29211         this.fireEvent('positionchanged', this, location);
29212     },
29213     
29214     resize: function()
29215     {
29216         google.maps.event.trigger(this.gMapContext.map, "resize");
29217         
29218         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29219         
29220         this.fireEvent('resize', this);
29221     },
29222     
29223     setPositionByLatLng: function(latitude, longitude)
29224     {
29225         this.setPosition(new google.maps.LatLng(latitude, longitude));
29226     },
29227     
29228     getCurrentPosition: function() 
29229     {
29230         return {
29231             latitude: this.gMapContext.location.lat(),
29232             longitude: this.gMapContext.location.lng()
29233         };
29234     },
29235     
29236     getAddressName: function() 
29237     {
29238         return this.gMapContext.locationName;
29239     },
29240     
29241     getAddressComponents: function() 
29242     {
29243         return this.gMapContext.addressComponents;
29244     },
29245     
29246     address_component_from_google_geocode: function(address_components) 
29247     {
29248         var result = {};
29249         
29250         for (var i = 0; i < address_components.length; i++) {
29251             var component = address_components[i];
29252             if (component.types.indexOf("postal_code") >= 0) {
29253                 result.postalCode = component.short_name;
29254             } else if (component.types.indexOf("street_number") >= 0) {
29255                 result.streetNumber = component.short_name;
29256             } else if (component.types.indexOf("route") >= 0) {
29257                 result.streetName = component.short_name;
29258             } else if (component.types.indexOf("neighborhood") >= 0) {
29259                 result.city = component.short_name;
29260             } else if (component.types.indexOf("locality") >= 0) {
29261                 result.city = component.short_name;
29262             } else if (component.types.indexOf("sublocality") >= 0) {
29263                 result.district = component.short_name;
29264             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29265                 result.stateOrProvince = component.short_name;
29266             } else if (component.types.indexOf("country") >= 0) {
29267                 result.country = component.short_name;
29268             }
29269         }
29270         
29271         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29272         result.addressLine2 = "";
29273         return result;
29274     },
29275     
29276     setZoomLevel: function(zoom)
29277     {
29278         this.gMapContext.map.setZoom(zoom);
29279     },
29280     
29281     show: function()
29282     {
29283         if(!this.el){
29284             return;
29285         }
29286         
29287         this.el.show();
29288         
29289         this.resize();
29290         
29291         this.fireEvent('show', this);
29292     },
29293     
29294     hide: function()
29295     {
29296         if(!this.el){
29297             return;
29298         }
29299         
29300         this.el.hide();
29301         
29302         this.fireEvent('hide', this);
29303     }
29304     
29305 });
29306
29307 Roo.apply(Roo.bootstrap.LocationPicker, {
29308     
29309     OverlayView : function(map, options)
29310     {
29311         options = options || {};
29312         
29313         this.setMap(map);
29314     }
29315     
29316     
29317 });/**
29318  * @class Roo.bootstrap.Alert
29319  * @extends Roo.bootstrap.Component
29320  * Bootstrap Alert class - shows an alert area box
29321  * eg
29322  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29323   Enter a valid email address
29324 </div>
29325  * @licence LGPL
29326  * @cfg {String} title The title of alert
29327  * @cfg {String} html The content of alert
29328  * @cfg {String} weight (  success | info | warning | danger )
29329  * @cfg {String} faicon font-awesomeicon
29330  * 
29331  * @constructor
29332  * Create a new alert
29333  * @param {Object} config The config object
29334  */
29335
29336
29337 Roo.bootstrap.Alert = function(config){
29338     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29339     
29340 };
29341
29342 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29343     
29344     title: '',
29345     html: '',
29346     weight: false,
29347     faicon: false,
29348     
29349     getAutoCreate : function()
29350     {
29351         
29352         var cfg = {
29353             tag : 'div',
29354             cls : 'alert',
29355             cn : [
29356                 {
29357                     tag : 'i',
29358                     cls : 'roo-alert-icon'
29359                     
29360                 },
29361                 {
29362                     tag : 'b',
29363                     cls : 'roo-alert-title',
29364                     html : this.title
29365                 },
29366                 {
29367                     tag : 'span',
29368                     cls : 'roo-alert-text',
29369                     html : this.html
29370                 }
29371             ]
29372         };
29373         
29374         if(this.faicon){
29375             cfg.cn[0].cls += ' fa ' + this.faicon;
29376         }
29377         
29378         if(this.weight){
29379             cfg.cls += ' alert-' + this.weight;
29380         }
29381         
29382         return cfg;
29383     },
29384     
29385     initEvents: function() 
29386     {
29387         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29388     },
29389     
29390     setTitle : function(str)
29391     {
29392         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29393     },
29394     
29395     setText : function(str)
29396     {
29397         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29398     },
29399     
29400     setWeight : function(weight)
29401     {
29402         if(this.weight){
29403             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29404         }
29405         
29406         this.weight = weight;
29407         
29408         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29409     },
29410     
29411     setIcon : function(icon)
29412     {
29413         if(this.faicon){
29414             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29415         }
29416         
29417         this.faicon = icon;
29418         
29419         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29420     },
29421     
29422     hide: function() 
29423     {
29424         this.el.hide();   
29425     },
29426     
29427     show: function() 
29428     {  
29429         this.el.show();   
29430     }
29431     
29432 });
29433
29434  
29435 /*
29436 * Licence: LGPL
29437 */
29438
29439 /**
29440  * @class Roo.bootstrap.UploadCropbox
29441  * @extends Roo.bootstrap.Component
29442  * Bootstrap UploadCropbox class
29443  * @cfg {String} emptyText show when image has been loaded
29444  * @cfg {String} rotateNotify show when image too small to rotate
29445  * @cfg {Number} errorTimeout default 3000
29446  * @cfg {Number} minWidth default 300
29447  * @cfg {Number} minHeight default 300
29448  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29449  * @cfg {Boolean} isDocument (true|false) default false
29450  * @cfg {String} url action url
29451  * @cfg {String} paramName default 'imageUpload'
29452  * @cfg {String} method default POST
29453  * @cfg {Boolean} loadMask (true|false) default true
29454  * @cfg {Boolean} loadingText default 'Loading...'
29455  * 
29456  * @constructor
29457  * Create a new UploadCropbox
29458  * @param {Object} config The config object
29459  */
29460
29461 Roo.bootstrap.UploadCropbox = function(config){
29462     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29463     
29464     this.addEvents({
29465         /**
29466          * @event beforeselectfile
29467          * Fire before select file
29468          * @param {Roo.bootstrap.UploadCropbox} this
29469          */
29470         "beforeselectfile" : true,
29471         /**
29472          * @event initial
29473          * Fire after initEvent
29474          * @param {Roo.bootstrap.UploadCropbox} this
29475          */
29476         "initial" : true,
29477         /**
29478          * @event crop
29479          * Fire after initEvent
29480          * @param {Roo.bootstrap.UploadCropbox} this
29481          * @param {String} data
29482          */
29483         "crop" : true,
29484         /**
29485          * @event prepare
29486          * Fire when preparing the file data
29487          * @param {Roo.bootstrap.UploadCropbox} this
29488          * @param {Object} file
29489          */
29490         "prepare" : true,
29491         /**
29492          * @event exception
29493          * Fire when get exception
29494          * @param {Roo.bootstrap.UploadCropbox} this
29495          * @param {XMLHttpRequest} xhr
29496          */
29497         "exception" : true,
29498         /**
29499          * @event beforeloadcanvas
29500          * Fire before load the canvas
29501          * @param {Roo.bootstrap.UploadCropbox} this
29502          * @param {String} src
29503          */
29504         "beforeloadcanvas" : true,
29505         /**
29506          * @event trash
29507          * Fire when trash image
29508          * @param {Roo.bootstrap.UploadCropbox} this
29509          */
29510         "trash" : true,
29511         /**
29512          * @event download
29513          * Fire when download the image
29514          * @param {Roo.bootstrap.UploadCropbox} this
29515          */
29516         "download" : true,
29517         /**
29518          * @event footerbuttonclick
29519          * Fire when footerbuttonclick
29520          * @param {Roo.bootstrap.UploadCropbox} this
29521          * @param {String} type
29522          */
29523         "footerbuttonclick" : true,
29524         /**
29525          * @event resize
29526          * Fire when resize
29527          * @param {Roo.bootstrap.UploadCropbox} this
29528          */
29529         "resize" : true,
29530         /**
29531          * @event rotate
29532          * Fire when rotate the image
29533          * @param {Roo.bootstrap.UploadCropbox} this
29534          * @param {String} pos
29535          */
29536         "rotate" : true,
29537         /**
29538          * @event inspect
29539          * Fire when inspect the file
29540          * @param {Roo.bootstrap.UploadCropbox} this
29541          * @param {Object} file
29542          */
29543         "inspect" : true,
29544         /**
29545          * @event upload
29546          * Fire when xhr upload the file
29547          * @param {Roo.bootstrap.UploadCropbox} this
29548          * @param {Object} data
29549          */
29550         "upload" : true,
29551         /**
29552          * @event arrange
29553          * Fire when arrange the file data
29554          * @param {Roo.bootstrap.UploadCropbox} this
29555          * @param {Object} formData
29556          */
29557         "arrange" : true
29558     });
29559     
29560     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29561 };
29562
29563 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29564     
29565     emptyText : 'Click to upload image',
29566     rotateNotify : 'Image is too small to rotate',
29567     errorTimeout : 3000,
29568     scale : 0,
29569     baseScale : 1,
29570     rotate : 0,
29571     dragable : false,
29572     pinching : false,
29573     mouseX : 0,
29574     mouseY : 0,
29575     cropData : false,
29576     minWidth : 300,
29577     minHeight : 300,
29578     file : false,
29579     exif : {},
29580     baseRotate : 1,
29581     cropType : 'image/jpeg',
29582     buttons : false,
29583     canvasLoaded : false,
29584     isDocument : false,
29585     method : 'POST',
29586     paramName : 'imageUpload',
29587     loadMask : true,
29588     loadingText : 'Loading...',
29589     maskEl : false,
29590     
29591     getAutoCreate : function()
29592     {
29593         var cfg = {
29594             tag : 'div',
29595             cls : 'roo-upload-cropbox',
29596             cn : [
29597                 {
29598                     tag : 'input',
29599                     cls : 'roo-upload-cropbox-selector',
29600                     type : 'file'
29601                 },
29602                 {
29603                     tag : 'div',
29604                     cls : 'roo-upload-cropbox-body',
29605                     style : 'cursor:pointer',
29606                     cn : [
29607                         {
29608                             tag : 'div',
29609                             cls : 'roo-upload-cropbox-preview'
29610                         },
29611                         {
29612                             tag : 'div',
29613                             cls : 'roo-upload-cropbox-thumb'
29614                         },
29615                         {
29616                             tag : 'div',
29617                             cls : 'roo-upload-cropbox-empty-notify',
29618                             html : this.emptyText
29619                         },
29620                         {
29621                             tag : 'div',
29622                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29623                             html : this.rotateNotify
29624                         }
29625                     ]
29626                 },
29627                 {
29628                     tag : 'div',
29629                     cls : 'roo-upload-cropbox-footer',
29630                     cn : {
29631                         tag : 'div',
29632                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29633                         cn : []
29634                     }
29635                 }
29636             ]
29637         };
29638         
29639         return cfg;
29640     },
29641     
29642     onRender : function(ct, position)
29643     {
29644         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29645         
29646         if (this.buttons.length) {
29647             
29648             Roo.each(this.buttons, function(bb) {
29649                 
29650                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29651                 
29652                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29653                 
29654             }, this);
29655         }
29656         
29657         if(this.loadMask){
29658             this.maskEl = this.el;
29659         }
29660     },
29661     
29662     initEvents : function()
29663     {
29664         this.urlAPI = (window.createObjectURL && window) || 
29665                                 (window.URL && URL.revokeObjectURL && URL) || 
29666                                 (window.webkitURL && webkitURL);
29667                         
29668         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29669         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29670         
29671         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29672         this.selectorEl.hide();
29673         
29674         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29675         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29676         
29677         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29678         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29679         this.thumbEl.hide();
29680         
29681         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29682         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29683         
29684         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29685         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29686         this.errorEl.hide();
29687         
29688         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29689         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29690         this.footerEl.hide();
29691         
29692         this.setThumbBoxSize();
29693         
29694         this.bind();
29695         
29696         this.resize();
29697         
29698         this.fireEvent('initial', this);
29699     },
29700
29701     bind : function()
29702     {
29703         var _this = this;
29704         
29705         window.addEventListener("resize", function() { _this.resize(); } );
29706         
29707         this.bodyEl.on('click', this.beforeSelectFile, this);
29708         
29709         if(Roo.isTouch){
29710             this.bodyEl.on('touchstart', this.onTouchStart, this);
29711             this.bodyEl.on('touchmove', this.onTouchMove, this);
29712             this.bodyEl.on('touchend', this.onTouchEnd, this);
29713         }
29714         
29715         if(!Roo.isTouch){
29716             this.bodyEl.on('mousedown', this.onMouseDown, this);
29717             this.bodyEl.on('mousemove', this.onMouseMove, this);
29718             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29719             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29720             Roo.get(document).on('mouseup', this.onMouseUp, this);
29721         }
29722         
29723         this.selectorEl.on('change', this.onFileSelected, this);
29724     },
29725     
29726     reset : function()
29727     {    
29728         this.scale = 0;
29729         this.baseScale = 1;
29730         this.rotate = 0;
29731         this.baseRotate = 1;
29732         this.dragable = false;
29733         this.pinching = false;
29734         this.mouseX = 0;
29735         this.mouseY = 0;
29736         this.cropData = false;
29737         this.notifyEl.dom.innerHTML = this.emptyText;
29738         
29739         this.selectorEl.dom.value = '';
29740         
29741     },
29742     
29743     resize : function()
29744     {
29745         if(this.fireEvent('resize', this) != false){
29746             this.setThumbBoxPosition();
29747             this.setCanvasPosition();
29748         }
29749     },
29750     
29751     onFooterButtonClick : function(e, el, o, type)
29752     {
29753         switch (type) {
29754             case 'rotate-left' :
29755                 this.onRotateLeft(e);
29756                 break;
29757             case 'rotate-right' :
29758                 this.onRotateRight(e);
29759                 break;
29760             case 'picture' :
29761                 this.beforeSelectFile(e);
29762                 break;
29763             case 'trash' :
29764                 this.trash(e);
29765                 break;
29766             case 'crop' :
29767                 this.crop(e);
29768                 break;
29769             case 'download' :
29770                 this.download(e);
29771                 break;
29772             default :
29773                 break;
29774         }
29775         
29776         this.fireEvent('footerbuttonclick', this, type);
29777     },
29778     
29779     beforeSelectFile : function(e)
29780     {
29781         e.preventDefault();
29782         
29783         if(this.fireEvent('beforeselectfile', this) != false){
29784             this.selectorEl.dom.click();
29785         }
29786     },
29787     
29788     onFileSelected : function(e)
29789     {
29790         e.preventDefault();
29791         
29792         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29793             return;
29794         }
29795         
29796         var file = this.selectorEl.dom.files[0];
29797         
29798         if(this.fireEvent('inspect', this, file) != false){
29799             this.prepare(file);
29800         }
29801         
29802     },
29803     
29804     trash : function(e)
29805     {
29806         this.fireEvent('trash', this);
29807     },
29808     
29809     download : function(e)
29810     {
29811         this.fireEvent('download', this);
29812     },
29813     
29814     loadCanvas : function(src)
29815     {   
29816         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29817             
29818             this.reset();
29819             
29820             this.imageEl = document.createElement('img');
29821             
29822             var _this = this;
29823             
29824             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29825             
29826             this.imageEl.src = src;
29827         }
29828     },
29829     
29830     onLoadCanvas : function()
29831     {   
29832         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29833         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29834         
29835         this.bodyEl.un('click', this.beforeSelectFile, this);
29836         
29837         this.notifyEl.hide();
29838         this.thumbEl.show();
29839         this.footerEl.show();
29840         
29841         this.baseRotateLevel();
29842         
29843         if(this.isDocument){
29844             this.setThumbBoxSize();
29845         }
29846         
29847         this.setThumbBoxPosition();
29848         
29849         this.baseScaleLevel();
29850         
29851         this.draw();
29852         
29853         this.resize();
29854         
29855         this.canvasLoaded = true;
29856         
29857         if(this.loadMask){
29858             this.maskEl.unmask();
29859         }
29860         
29861     },
29862     
29863     setCanvasPosition : function()
29864     {   
29865         if(!this.canvasEl){
29866             return;
29867         }
29868         
29869         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29870         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29871         
29872         this.previewEl.setLeft(pw);
29873         this.previewEl.setTop(ph);
29874         
29875     },
29876     
29877     onMouseDown : function(e)
29878     {   
29879         e.stopEvent();
29880         
29881         this.dragable = true;
29882         this.pinching = false;
29883         
29884         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29885             this.dragable = false;
29886             return;
29887         }
29888         
29889         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29890         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29891         
29892     },
29893     
29894     onMouseMove : function(e)
29895     {   
29896         e.stopEvent();
29897         
29898         if(!this.canvasLoaded){
29899             return;
29900         }
29901         
29902         if (!this.dragable){
29903             return;
29904         }
29905         
29906         var minX = Math.ceil(this.thumbEl.getLeft(true));
29907         var minY = Math.ceil(this.thumbEl.getTop(true));
29908         
29909         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29910         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29911         
29912         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29913         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29914         
29915         x = x - this.mouseX;
29916         y = y - this.mouseY;
29917         
29918         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29919         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29920         
29921         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29922         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29923         
29924         this.previewEl.setLeft(bgX);
29925         this.previewEl.setTop(bgY);
29926         
29927         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29928         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29929     },
29930     
29931     onMouseUp : function(e)
29932     {   
29933         e.stopEvent();
29934         
29935         this.dragable = false;
29936     },
29937     
29938     onMouseWheel : function(e)
29939     {   
29940         e.stopEvent();
29941         
29942         this.startScale = this.scale;
29943         
29944         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29945         
29946         if(!this.zoomable()){
29947             this.scale = this.startScale;
29948             return;
29949         }
29950         
29951         this.draw();
29952         
29953         return;
29954     },
29955     
29956     zoomable : function()
29957     {
29958         var minScale = this.thumbEl.getWidth() / this.minWidth;
29959         
29960         if(this.minWidth < this.minHeight){
29961             minScale = this.thumbEl.getHeight() / this.minHeight;
29962         }
29963         
29964         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29965         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29966         
29967         if(
29968                 this.isDocument &&
29969                 (this.rotate == 0 || this.rotate == 180) && 
29970                 (
29971                     width > this.imageEl.OriginWidth || 
29972                     height > this.imageEl.OriginHeight ||
29973                     (width < this.minWidth && height < this.minHeight)
29974                 )
29975         ){
29976             return false;
29977         }
29978         
29979         if(
29980                 this.isDocument &&
29981                 (this.rotate == 90 || this.rotate == 270) && 
29982                 (
29983                     width > this.imageEl.OriginWidth || 
29984                     height > this.imageEl.OriginHeight ||
29985                     (width < this.minHeight && height < this.minWidth)
29986                 )
29987         ){
29988             return false;
29989         }
29990         
29991         if(
29992                 !this.isDocument &&
29993                 (this.rotate == 0 || this.rotate == 180) && 
29994                 (
29995                     width < this.minWidth || 
29996                     width > this.imageEl.OriginWidth || 
29997                     height < this.minHeight || 
29998                     height > this.imageEl.OriginHeight
29999                 )
30000         ){
30001             return false;
30002         }
30003         
30004         if(
30005                 !this.isDocument &&
30006                 (this.rotate == 90 || this.rotate == 270) && 
30007                 (
30008                     width < this.minHeight || 
30009                     width > this.imageEl.OriginWidth || 
30010                     height < this.minWidth || 
30011                     height > this.imageEl.OriginHeight
30012                 )
30013         ){
30014             return false;
30015         }
30016         
30017         return true;
30018         
30019     },
30020     
30021     onRotateLeft : function(e)
30022     {   
30023         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30024             
30025             var minScale = this.thumbEl.getWidth() / this.minWidth;
30026             
30027             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30028             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30029             
30030             this.startScale = this.scale;
30031             
30032             while (this.getScaleLevel() < minScale){
30033             
30034                 this.scale = this.scale + 1;
30035                 
30036                 if(!this.zoomable()){
30037                     break;
30038                 }
30039                 
30040                 if(
30041                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30042                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30043                 ){
30044                     continue;
30045                 }
30046                 
30047                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30048
30049                 this.draw();
30050                 
30051                 return;
30052             }
30053             
30054             this.scale = this.startScale;
30055             
30056             this.onRotateFail();
30057             
30058             return false;
30059         }
30060         
30061         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30062
30063         if(this.isDocument){
30064             this.setThumbBoxSize();
30065             this.setThumbBoxPosition();
30066             this.setCanvasPosition();
30067         }
30068         
30069         this.draw();
30070         
30071         this.fireEvent('rotate', this, 'left');
30072         
30073     },
30074     
30075     onRotateRight : function(e)
30076     {
30077         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30078             
30079             var minScale = this.thumbEl.getWidth() / this.minWidth;
30080         
30081             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30082             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30083             
30084             this.startScale = this.scale;
30085             
30086             while (this.getScaleLevel() < minScale){
30087             
30088                 this.scale = this.scale + 1;
30089                 
30090                 if(!this.zoomable()){
30091                     break;
30092                 }
30093                 
30094                 if(
30095                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30096                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30097                 ){
30098                     continue;
30099                 }
30100                 
30101                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30102
30103                 this.draw();
30104                 
30105                 return;
30106             }
30107             
30108             this.scale = this.startScale;
30109             
30110             this.onRotateFail();
30111             
30112             return false;
30113         }
30114         
30115         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30116
30117         if(this.isDocument){
30118             this.setThumbBoxSize();
30119             this.setThumbBoxPosition();
30120             this.setCanvasPosition();
30121         }
30122         
30123         this.draw();
30124         
30125         this.fireEvent('rotate', this, 'right');
30126     },
30127     
30128     onRotateFail : function()
30129     {
30130         this.errorEl.show(true);
30131         
30132         var _this = this;
30133         
30134         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30135     },
30136     
30137     draw : function()
30138     {
30139         this.previewEl.dom.innerHTML = '';
30140         
30141         var canvasEl = document.createElement("canvas");
30142         
30143         var contextEl = canvasEl.getContext("2d");
30144         
30145         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30146         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30147         var center = this.imageEl.OriginWidth / 2;
30148         
30149         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30150             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30151             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30152             center = this.imageEl.OriginHeight / 2;
30153         }
30154         
30155         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30156         
30157         contextEl.translate(center, center);
30158         contextEl.rotate(this.rotate * Math.PI / 180);
30159
30160         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30161         
30162         this.canvasEl = document.createElement("canvas");
30163         
30164         this.contextEl = this.canvasEl.getContext("2d");
30165         
30166         switch (this.rotate) {
30167             case 0 :
30168                 
30169                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30170                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30171                 
30172                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30173                 
30174                 break;
30175             case 90 : 
30176                 
30177                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30178                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30179                 
30180                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30181                     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);
30182                     break;
30183                 }
30184                 
30185                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30186                 
30187                 break;
30188             case 180 :
30189                 
30190                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30191                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30192                 
30193                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30194                     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);
30195                     break;
30196                 }
30197                 
30198                 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);
30199                 
30200                 break;
30201             case 270 :
30202                 
30203                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30204                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30205         
30206                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30207                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30208                     break;
30209                 }
30210                 
30211                 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);
30212                 
30213                 break;
30214             default : 
30215                 break;
30216         }
30217         
30218         this.previewEl.appendChild(this.canvasEl);
30219         
30220         this.setCanvasPosition();
30221     },
30222     
30223     crop : function()
30224     {
30225         if(!this.canvasLoaded){
30226             return;
30227         }
30228         
30229         var imageCanvas = document.createElement("canvas");
30230         
30231         var imageContext = imageCanvas.getContext("2d");
30232         
30233         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30234         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30235         
30236         var center = imageCanvas.width / 2;
30237         
30238         imageContext.translate(center, center);
30239         
30240         imageContext.rotate(this.rotate * Math.PI / 180);
30241         
30242         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30243         
30244         var canvas = document.createElement("canvas");
30245         
30246         var context = canvas.getContext("2d");
30247                 
30248         canvas.width = this.minWidth;
30249         canvas.height = this.minHeight;
30250
30251         switch (this.rotate) {
30252             case 0 :
30253                 
30254                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30255                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30256                 
30257                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30258                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30259                 
30260                 var targetWidth = this.minWidth - 2 * x;
30261                 var targetHeight = this.minHeight - 2 * y;
30262                 
30263                 var scale = 1;
30264                 
30265                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30266                     scale = targetWidth / width;
30267                 }
30268                 
30269                 if(x > 0 && y == 0){
30270                     scale = targetHeight / height;
30271                 }
30272                 
30273                 if(x > 0 && y > 0){
30274                     scale = targetWidth / width;
30275                     
30276                     if(width < height){
30277                         scale = targetHeight / height;
30278                     }
30279                 }
30280                 
30281                 context.scale(scale, scale);
30282                 
30283                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30284                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30285
30286                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30287                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30288
30289                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30290                 
30291                 break;
30292             case 90 : 
30293                 
30294                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30295                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30296                 
30297                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30298                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30299                 
30300                 var targetWidth = this.minWidth - 2 * x;
30301                 var targetHeight = this.minHeight - 2 * y;
30302                 
30303                 var scale = 1;
30304                 
30305                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30306                     scale = targetWidth / width;
30307                 }
30308                 
30309                 if(x > 0 && y == 0){
30310                     scale = targetHeight / height;
30311                 }
30312                 
30313                 if(x > 0 && y > 0){
30314                     scale = targetWidth / width;
30315                     
30316                     if(width < height){
30317                         scale = targetHeight / height;
30318                     }
30319                 }
30320                 
30321                 context.scale(scale, scale);
30322                 
30323                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30324                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30325
30326                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30327                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30328                 
30329                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30330                 
30331                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30332                 
30333                 break;
30334             case 180 :
30335                 
30336                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30337                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30338                 
30339                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30340                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30341                 
30342                 var targetWidth = this.minWidth - 2 * x;
30343                 var targetHeight = this.minHeight - 2 * y;
30344                 
30345                 var scale = 1;
30346                 
30347                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30348                     scale = targetWidth / width;
30349                 }
30350                 
30351                 if(x > 0 && y == 0){
30352                     scale = targetHeight / height;
30353                 }
30354                 
30355                 if(x > 0 && y > 0){
30356                     scale = targetWidth / width;
30357                     
30358                     if(width < height){
30359                         scale = targetHeight / height;
30360                     }
30361                 }
30362                 
30363                 context.scale(scale, scale);
30364                 
30365                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30366                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30367
30368                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30369                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30370
30371                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30372                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30373                 
30374                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30375                 
30376                 break;
30377             case 270 :
30378                 
30379                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30380                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30381                 
30382                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30383                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30384                 
30385                 var targetWidth = this.minWidth - 2 * x;
30386                 var targetHeight = this.minHeight - 2 * y;
30387                 
30388                 var scale = 1;
30389                 
30390                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30391                     scale = targetWidth / width;
30392                 }
30393                 
30394                 if(x > 0 && y == 0){
30395                     scale = targetHeight / height;
30396                 }
30397                 
30398                 if(x > 0 && y > 0){
30399                     scale = targetWidth / width;
30400                     
30401                     if(width < height){
30402                         scale = targetHeight / height;
30403                     }
30404                 }
30405                 
30406                 context.scale(scale, scale);
30407                 
30408                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30409                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30410
30411                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30412                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30413                 
30414                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30415                 
30416                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30417                 
30418                 break;
30419             default : 
30420                 break;
30421         }
30422         
30423         this.cropData = canvas.toDataURL(this.cropType);
30424         
30425         if(this.fireEvent('crop', this, this.cropData) !== false){
30426             this.process(this.file, this.cropData);
30427         }
30428         
30429         return;
30430         
30431     },
30432     
30433     setThumbBoxSize : function()
30434     {
30435         var width, height;
30436         
30437         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30438             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30439             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30440             
30441             this.minWidth = width;
30442             this.minHeight = height;
30443             
30444             if(this.rotate == 90 || this.rotate == 270){
30445                 this.minWidth = height;
30446                 this.minHeight = width;
30447             }
30448         }
30449         
30450         height = 300;
30451         width = Math.ceil(this.minWidth * height / this.minHeight);
30452         
30453         if(this.minWidth > this.minHeight){
30454             width = 300;
30455             height = Math.ceil(this.minHeight * width / this.minWidth);
30456         }
30457         
30458         this.thumbEl.setStyle({
30459             width : width + 'px',
30460             height : height + 'px'
30461         });
30462
30463         return;
30464             
30465     },
30466     
30467     setThumbBoxPosition : function()
30468     {
30469         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30470         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30471         
30472         this.thumbEl.setLeft(x);
30473         this.thumbEl.setTop(y);
30474         
30475     },
30476     
30477     baseRotateLevel : function()
30478     {
30479         this.baseRotate = 1;
30480         
30481         if(
30482                 typeof(this.exif) != 'undefined' &&
30483                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30484                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30485         ){
30486             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30487         }
30488         
30489         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30490         
30491     },
30492     
30493     baseScaleLevel : function()
30494     {
30495         var width, height;
30496         
30497         if(this.isDocument){
30498             
30499             if(this.baseRotate == 6 || this.baseRotate == 8){
30500             
30501                 height = this.thumbEl.getHeight();
30502                 this.baseScale = height / this.imageEl.OriginWidth;
30503
30504                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30505                     width = this.thumbEl.getWidth();
30506                     this.baseScale = width / this.imageEl.OriginHeight;
30507                 }
30508
30509                 return;
30510             }
30511
30512             height = this.thumbEl.getHeight();
30513             this.baseScale = height / this.imageEl.OriginHeight;
30514
30515             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30516                 width = this.thumbEl.getWidth();
30517                 this.baseScale = width / this.imageEl.OriginWidth;
30518             }
30519
30520             return;
30521         }
30522         
30523         if(this.baseRotate == 6 || this.baseRotate == 8){
30524             
30525             width = this.thumbEl.getHeight();
30526             this.baseScale = width / this.imageEl.OriginHeight;
30527             
30528             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30529                 height = this.thumbEl.getWidth();
30530                 this.baseScale = height / this.imageEl.OriginHeight;
30531             }
30532             
30533             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30534                 height = this.thumbEl.getWidth();
30535                 this.baseScale = height / this.imageEl.OriginHeight;
30536                 
30537                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30538                     width = this.thumbEl.getHeight();
30539                     this.baseScale = width / this.imageEl.OriginWidth;
30540                 }
30541             }
30542             
30543             return;
30544         }
30545         
30546         width = this.thumbEl.getWidth();
30547         this.baseScale = width / this.imageEl.OriginWidth;
30548         
30549         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30550             height = this.thumbEl.getHeight();
30551             this.baseScale = height / this.imageEl.OriginHeight;
30552         }
30553         
30554         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30555             
30556             height = this.thumbEl.getHeight();
30557             this.baseScale = height / this.imageEl.OriginHeight;
30558             
30559             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30560                 width = this.thumbEl.getWidth();
30561                 this.baseScale = width / this.imageEl.OriginWidth;
30562             }
30563             
30564         }
30565         
30566         return;
30567     },
30568     
30569     getScaleLevel : function()
30570     {
30571         return this.baseScale * Math.pow(1.1, this.scale);
30572     },
30573     
30574     onTouchStart : function(e)
30575     {
30576         if(!this.canvasLoaded){
30577             this.beforeSelectFile(e);
30578             return;
30579         }
30580         
30581         var touches = e.browserEvent.touches;
30582         
30583         if(!touches){
30584             return;
30585         }
30586         
30587         if(touches.length == 1){
30588             this.onMouseDown(e);
30589             return;
30590         }
30591         
30592         if(touches.length != 2){
30593             return;
30594         }
30595         
30596         var coords = [];
30597         
30598         for(var i = 0, finger; finger = touches[i]; i++){
30599             coords.push(finger.pageX, finger.pageY);
30600         }
30601         
30602         var x = Math.pow(coords[0] - coords[2], 2);
30603         var y = Math.pow(coords[1] - coords[3], 2);
30604         
30605         this.startDistance = Math.sqrt(x + y);
30606         
30607         this.startScale = this.scale;
30608         
30609         this.pinching = true;
30610         this.dragable = false;
30611         
30612     },
30613     
30614     onTouchMove : function(e)
30615     {
30616         if(!this.pinching && !this.dragable){
30617             return;
30618         }
30619         
30620         var touches = e.browserEvent.touches;
30621         
30622         if(!touches){
30623             return;
30624         }
30625         
30626         if(this.dragable){
30627             this.onMouseMove(e);
30628             return;
30629         }
30630         
30631         var coords = [];
30632         
30633         for(var i = 0, finger; finger = touches[i]; i++){
30634             coords.push(finger.pageX, finger.pageY);
30635         }
30636         
30637         var x = Math.pow(coords[0] - coords[2], 2);
30638         var y = Math.pow(coords[1] - coords[3], 2);
30639         
30640         this.endDistance = Math.sqrt(x + y);
30641         
30642         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30643         
30644         if(!this.zoomable()){
30645             this.scale = this.startScale;
30646             return;
30647         }
30648         
30649         this.draw();
30650         
30651     },
30652     
30653     onTouchEnd : function(e)
30654     {
30655         this.pinching = false;
30656         this.dragable = false;
30657         
30658     },
30659     
30660     process : function(file, crop)
30661     {
30662         if(this.loadMask){
30663             this.maskEl.mask(this.loadingText);
30664         }
30665         
30666         this.xhr = new XMLHttpRequest();
30667         
30668         file.xhr = this.xhr;
30669
30670         this.xhr.open(this.method, this.url, true);
30671         
30672         var headers = {
30673             "Accept": "application/json",
30674             "Cache-Control": "no-cache",
30675             "X-Requested-With": "XMLHttpRequest"
30676         };
30677         
30678         for (var headerName in headers) {
30679             var headerValue = headers[headerName];
30680             if (headerValue) {
30681                 this.xhr.setRequestHeader(headerName, headerValue);
30682             }
30683         }
30684         
30685         var _this = this;
30686         
30687         this.xhr.onload = function()
30688         {
30689             _this.xhrOnLoad(_this.xhr);
30690         }
30691         
30692         this.xhr.onerror = function()
30693         {
30694             _this.xhrOnError(_this.xhr);
30695         }
30696         
30697         var formData = new FormData();
30698
30699         formData.append('returnHTML', 'NO');
30700         
30701         if(crop){
30702             formData.append('crop', crop);
30703         }
30704         
30705         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30706             formData.append(this.paramName, file, file.name);
30707         }
30708         
30709         if(typeof(file.filename) != 'undefined'){
30710             formData.append('filename', file.filename);
30711         }
30712         
30713         if(typeof(file.mimetype) != 'undefined'){
30714             formData.append('mimetype', file.mimetype);
30715         }
30716         
30717         if(this.fireEvent('arrange', this, formData) != false){
30718             this.xhr.send(formData);
30719         };
30720     },
30721     
30722     xhrOnLoad : function(xhr)
30723     {
30724         if(this.loadMask){
30725             this.maskEl.unmask();
30726         }
30727         
30728         if (xhr.readyState !== 4) {
30729             this.fireEvent('exception', this, xhr);
30730             return;
30731         }
30732
30733         var response = Roo.decode(xhr.responseText);
30734         
30735         if(!response.success){
30736             this.fireEvent('exception', this, xhr);
30737             return;
30738         }
30739         
30740         var response = Roo.decode(xhr.responseText);
30741         
30742         this.fireEvent('upload', this, response);
30743         
30744     },
30745     
30746     xhrOnError : function()
30747     {
30748         if(this.loadMask){
30749             this.maskEl.unmask();
30750         }
30751         
30752         Roo.log('xhr on error');
30753         
30754         var response = Roo.decode(xhr.responseText);
30755           
30756         Roo.log(response);
30757         
30758     },
30759     
30760     prepare : function(file)
30761     {   
30762         if(this.loadMask){
30763             this.maskEl.mask(this.loadingText);
30764         }
30765         
30766         this.file = false;
30767         this.exif = {};
30768         
30769         if(typeof(file) === 'string'){
30770             this.loadCanvas(file);
30771             return;
30772         }
30773         
30774         if(!file || !this.urlAPI){
30775             return;
30776         }
30777         
30778         this.file = file;
30779         this.cropType = file.type;
30780         
30781         var _this = this;
30782         
30783         if(this.fireEvent('prepare', this, this.file) != false){
30784             
30785             var reader = new FileReader();
30786             
30787             reader.onload = function (e) {
30788                 if (e.target.error) {
30789                     Roo.log(e.target.error);
30790                     return;
30791                 }
30792                 
30793                 var buffer = e.target.result,
30794                     dataView = new DataView(buffer),
30795                     offset = 2,
30796                     maxOffset = dataView.byteLength - 4,
30797                     markerBytes,
30798                     markerLength;
30799                 
30800                 if (dataView.getUint16(0) === 0xffd8) {
30801                     while (offset < maxOffset) {
30802                         markerBytes = dataView.getUint16(offset);
30803                         
30804                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30805                             markerLength = dataView.getUint16(offset + 2) + 2;
30806                             if (offset + markerLength > dataView.byteLength) {
30807                                 Roo.log('Invalid meta data: Invalid segment size.');
30808                                 break;
30809                             }
30810                             
30811                             if(markerBytes == 0xffe1){
30812                                 _this.parseExifData(
30813                                     dataView,
30814                                     offset,
30815                                     markerLength
30816                                 );
30817                             }
30818                             
30819                             offset += markerLength;
30820                             
30821                             continue;
30822                         }
30823                         
30824                         break;
30825                     }
30826                     
30827                 }
30828                 
30829                 var url = _this.urlAPI.createObjectURL(_this.file);
30830                 
30831                 _this.loadCanvas(url);
30832                 
30833                 return;
30834             }
30835             
30836             reader.readAsArrayBuffer(this.file);
30837             
30838         }
30839         
30840     },
30841     
30842     parseExifData : function(dataView, offset, length)
30843     {
30844         var tiffOffset = offset + 10,
30845             littleEndian,
30846             dirOffset;
30847     
30848         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30849             // No Exif data, might be XMP data instead
30850             return;
30851         }
30852         
30853         // Check for the ASCII code for "Exif" (0x45786966):
30854         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30855             // No Exif data, might be XMP data instead
30856             return;
30857         }
30858         if (tiffOffset + 8 > dataView.byteLength) {
30859             Roo.log('Invalid Exif data: Invalid segment size.');
30860             return;
30861         }
30862         // Check for the two null bytes:
30863         if (dataView.getUint16(offset + 8) !== 0x0000) {
30864             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30865             return;
30866         }
30867         // Check the byte alignment:
30868         switch (dataView.getUint16(tiffOffset)) {
30869         case 0x4949:
30870             littleEndian = true;
30871             break;
30872         case 0x4D4D:
30873             littleEndian = false;
30874             break;
30875         default:
30876             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30877             return;
30878         }
30879         // Check for the TIFF tag marker (0x002A):
30880         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30881             Roo.log('Invalid Exif data: Missing TIFF marker.');
30882             return;
30883         }
30884         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30885         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30886         
30887         this.parseExifTags(
30888             dataView,
30889             tiffOffset,
30890             tiffOffset + dirOffset,
30891             littleEndian
30892         );
30893     },
30894     
30895     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30896     {
30897         var tagsNumber,
30898             dirEndOffset,
30899             i;
30900         if (dirOffset + 6 > dataView.byteLength) {
30901             Roo.log('Invalid Exif data: Invalid directory offset.');
30902             return;
30903         }
30904         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30905         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30906         if (dirEndOffset + 4 > dataView.byteLength) {
30907             Roo.log('Invalid Exif data: Invalid directory size.');
30908             return;
30909         }
30910         for (i = 0; i < tagsNumber; i += 1) {
30911             this.parseExifTag(
30912                 dataView,
30913                 tiffOffset,
30914                 dirOffset + 2 + 12 * i, // tag offset
30915                 littleEndian
30916             );
30917         }
30918         // Return the offset to the next directory:
30919         return dataView.getUint32(dirEndOffset, littleEndian);
30920     },
30921     
30922     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30923     {
30924         var tag = dataView.getUint16(offset, littleEndian);
30925         
30926         this.exif[tag] = this.getExifValue(
30927             dataView,
30928             tiffOffset,
30929             offset,
30930             dataView.getUint16(offset + 2, littleEndian), // tag type
30931             dataView.getUint32(offset + 4, littleEndian), // tag length
30932             littleEndian
30933         );
30934     },
30935     
30936     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30937     {
30938         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30939             tagSize,
30940             dataOffset,
30941             values,
30942             i,
30943             str,
30944             c;
30945     
30946         if (!tagType) {
30947             Roo.log('Invalid Exif data: Invalid tag type.');
30948             return;
30949         }
30950         
30951         tagSize = tagType.size * length;
30952         // Determine if the value is contained in the dataOffset bytes,
30953         // or if the value at the dataOffset is a pointer to the actual data:
30954         dataOffset = tagSize > 4 ?
30955                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30956         if (dataOffset + tagSize > dataView.byteLength) {
30957             Roo.log('Invalid Exif data: Invalid data offset.');
30958             return;
30959         }
30960         if (length === 1) {
30961             return tagType.getValue(dataView, dataOffset, littleEndian);
30962         }
30963         values = [];
30964         for (i = 0; i < length; i += 1) {
30965             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30966         }
30967         
30968         if (tagType.ascii) {
30969             str = '';
30970             // Concatenate the chars:
30971             for (i = 0; i < values.length; i += 1) {
30972                 c = values[i];
30973                 // Ignore the terminating NULL byte(s):
30974                 if (c === '\u0000') {
30975                     break;
30976                 }
30977                 str += c;
30978             }
30979             return str;
30980         }
30981         return values;
30982     }
30983     
30984 });
30985
30986 Roo.apply(Roo.bootstrap.UploadCropbox, {
30987     tags : {
30988         'Orientation': 0x0112
30989     },
30990     
30991     Orientation: {
30992             1: 0, //'top-left',
30993 //            2: 'top-right',
30994             3: 180, //'bottom-right',
30995 //            4: 'bottom-left',
30996 //            5: 'left-top',
30997             6: 90, //'right-top',
30998 //            7: 'right-bottom',
30999             8: 270 //'left-bottom'
31000     },
31001     
31002     exifTagTypes : {
31003         // byte, 8-bit unsigned int:
31004         1: {
31005             getValue: function (dataView, dataOffset) {
31006                 return dataView.getUint8(dataOffset);
31007             },
31008             size: 1
31009         },
31010         // ascii, 8-bit byte:
31011         2: {
31012             getValue: function (dataView, dataOffset) {
31013                 return String.fromCharCode(dataView.getUint8(dataOffset));
31014             },
31015             size: 1,
31016             ascii: true
31017         },
31018         // short, 16 bit int:
31019         3: {
31020             getValue: function (dataView, dataOffset, littleEndian) {
31021                 return dataView.getUint16(dataOffset, littleEndian);
31022             },
31023             size: 2
31024         },
31025         // long, 32 bit int:
31026         4: {
31027             getValue: function (dataView, dataOffset, littleEndian) {
31028                 return dataView.getUint32(dataOffset, littleEndian);
31029             },
31030             size: 4
31031         },
31032         // rational = two long values, first is numerator, second is denominator:
31033         5: {
31034             getValue: function (dataView, dataOffset, littleEndian) {
31035                 return dataView.getUint32(dataOffset, littleEndian) /
31036                     dataView.getUint32(dataOffset + 4, littleEndian);
31037             },
31038             size: 8
31039         },
31040         // slong, 32 bit signed int:
31041         9: {
31042             getValue: function (dataView, dataOffset, littleEndian) {
31043                 return dataView.getInt32(dataOffset, littleEndian);
31044             },
31045             size: 4
31046         },
31047         // srational, two slongs, first is numerator, second is denominator:
31048         10: {
31049             getValue: function (dataView, dataOffset, littleEndian) {
31050                 return dataView.getInt32(dataOffset, littleEndian) /
31051                     dataView.getInt32(dataOffset + 4, littleEndian);
31052             },
31053             size: 8
31054         }
31055     },
31056     
31057     footer : {
31058         STANDARD : [
31059             {
31060                 tag : 'div',
31061                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31062                 action : 'rotate-left',
31063                 cn : [
31064                     {
31065                         tag : 'button',
31066                         cls : 'btn btn-default',
31067                         html : '<i class="fa fa-undo"></i>'
31068                     }
31069                 ]
31070             },
31071             {
31072                 tag : 'div',
31073                 cls : 'btn-group roo-upload-cropbox-picture',
31074                 action : 'picture',
31075                 cn : [
31076                     {
31077                         tag : 'button',
31078                         cls : 'btn btn-default',
31079                         html : '<i class="fa fa-picture-o"></i>'
31080                     }
31081                 ]
31082             },
31083             {
31084                 tag : 'div',
31085                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31086                 action : 'rotate-right',
31087                 cn : [
31088                     {
31089                         tag : 'button',
31090                         cls : 'btn btn-default',
31091                         html : '<i class="fa fa-repeat"></i>'
31092                     }
31093                 ]
31094             }
31095         ],
31096         DOCUMENT : [
31097             {
31098                 tag : 'div',
31099                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31100                 action : 'rotate-left',
31101                 cn : [
31102                     {
31103                         tag : 'button',
31104                         cls : 'btn btn-default',
31105                         html : '<i class="fa fa-undo"></i>'
31106                     }
31107                 ]
31108             },
31109             {
31110                 tag : 'div',
31111                 cls : 'btn-group roo-upload-cropbox-download',
31112                 action : 'download',
31113                 cn : [
31114                     {
31115                         tag : 'button',
31116                         cls : 'btn btn-default',
31117                         html : '<i class="fa fa-download"></i>'
31118                     }
31119                 ]
31120             },
31121             {
31122                 tag : 'div',
31123                 cls : 'btn-group roo-upload-cropbox-crop',
31124                 action : 'crop',
31125                 cn : [
31126                     {
31127                         tag : 'button',
31128                         cls : 'btn btn-default',
31129                         html : '<i class="fa fa-crop"></i>'
31130                     }
31131                 ]
31132             },
31133             {
31134                 tag : 'div',
31135                 cls : 'btn-group roo-upload-cropbox-trash',
31136                 action : 'trash',
31137                 cn : [
31138                     {
31139                         tag : 'button',
31140                         cls : 'btn btn-default',
31141                         html : '<i class="fa fa-trash"></i>'
31142                     }
31143                 ]
31144             },
31145             {
31146                 tag : 'div',
31147                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31148                 action : 'rotate-right',
31149                 cn : [
31150                     {
31151                         tag : 'button',
31152                         cls : 'btn btn-default',
31153                         html : '<i class="fa fa-repeat"></i>'
31154                     }
31155                 ]
31156             }
31157         ],
31158         ROTATOR : [
31159             {
31160                 tag : 'div',
31161                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31162                 action : 'rotate-left',
31163                 cn : [
31164                     {
31165                         tag : 'button',
31166                         cls : 'btn btn-default',
31167                         html : '<i class="fa fa-undo"></i>'
31168                     }
31169                 ]
31170             },
31171             {
31172                 tag : 'div',
31173                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31174                 action : 'rotate-right',
31175                 cn : [
31176                     {
31177                         tag : 'button',
31178                         cls : 'btn btn-default',
31179                         html : '<i class="fa fa-repeat"></i>'
31180                     }
31181                 ]
31182             }
31183         ]
31184     }
31185 });
31186
31187 /*
31188 * Licence: LGPL
31189 */
31190
31191 /**
31192  * @class Roo.bootstrap.DocumentManager
31193  * @extends Roo.bootstrap.Component
31194  * Bootstrap DocumentManager class
31195  * @cfg {String} paramName default 'imageUpload'
31196  * @cfg {String} toolTipName default 'filename'
31197  * @cfg {String} method default POST
31198  * @cfg {String} url action url
31199  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31200  * @cfg {Boolean} multiple multiple upload default true
31201  * @cfg {Number} thumbSize default 300
31202  * @cfg {String} fieldLabel
31203  * @cfg {Number} labelWidth default 4
31204  * @cfg {String} labelAlign (left|top) default left
31205  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31206 * @cfg {Number} labellg set the width of label (1-12)
31207  * @cfg {Number} labelmd set the width of label (1-12)
31208  * @cfg {Number} labelsm set the width of label (1-12)
31209  * @cfg {Number} labelxs set the width of label (1-12)
31210  * 
31211  * @constructor
31212  * Create a new DocumentManager
31213  * @param {Object} config The config object
31214  */
31215
31216 Roo.bootstrap.DocumentManager = function(config){
31217     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31218     
31219     this.files = [];
31220     this.delegates = [];
31221     
31222     this.addEvents({
31223         /**
31224          * @event initial
31225          * Fire when initial the DocumentManager
31226          * @param {Roo.bootstrap.DocumentManager} this
31227          */
31228         "initial" : true,
31229         /**
31230          * @event inspect
31231          * inspect selected file
31232          * @param {Roo.bootstrap.DocumentManager} this
31233          * @param {File} file
31234          */
31235         "inspect" : true,
31236         /**
31237          * @event exception
31238          * Fire when xhr load exception
31239          * @param {Roo.bootstrap.DocumentManager} this
31240          * @param {XMLHttpRequest} xhr
31241          */
31242         "exception" : true,
31243         /**
31244          * @event afterupload
31245          * Fire when xhr load exception
31246          * @param {Roo.bootstrap.DocumentManager} this
31247          * @param {XMLHttpRequest} xhr
31248          */
31249         "afterupload" : true,
31250         /**
31251          * @event prepare
31252          * prepare the form data
31253          * @param {Roo.bootstrap.DocumentManager} this
31254          * @param {Object} formData
31255          */
31256         "prepare" : true,
31257         /**
31258          * @event remove
31259          * Fire when remove the file
31260          * @param {Roo.bootstrap.DocumentManager} this
31261          * @param {Object} file
31262          */
31263         "remove" : true,
31264         /**
31265          * @event refresh
31266          * Fire after refresh the file
31267          * @param {Roo.bootstrap.DocumentManager} this
31268          */
31269         "refresh" : true,
31270         /**
31271          * @event click
31272          * Fire after click the image
31273          * @param {Roo.bootstrap.DocumentManager} this
31274          * @param {Object} file
31275          */
31276         "click" : true,
31277         /**
31278          * @event edit
31279          * Fire when upload a image and editable set to true
31280          * @param {Roo.bootstrap.DocumentManager} this
31281          * @param {Object} file
31282          */
31283         "edit" : true,
31284         /**
31285          * @event beforeselectfile
31286          * Fire before select file
31287          * @param {Roo.bootstrap.DocumentManager} this
31288          */
31289         "beforeselectfile" : true,
31290         /**
31291          * @event process
31292          * Fire before process file
31293          * @param {Roo.bootstrap.DocumentManager} this
31294          * @param {Object} file
31295          */
31296         "process" : true,
31297         /**
31298          * @event previewrendered
31299          * Fire when preview rendered
31300          * @param {Roo.bootstrap.DocumentManager} this
31301          * @param {Object} file
31302          */
31303         "previewrendered" : true,
31304         /**
31305          */
31306         "previewResize" : true
31307         
31308     });
31309 };
31310
31311 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31312     
31313     boxes : 0,
31314     inputName : '',
31315     thumbSize : 300,
31316     multiple : true,
31317     files : false,
31318     method : 'POST',
31319     url : '',
31320     paramName : 'imageUpload',
31321     toolTipName : 'filename',
31322     fieldLabel : '',
31323     labelWidth : 4,
31324     labelAlign : 'left',
31325     editable : true,
31326     delegates : false,
31327     xhr : false, 
31328     
31329     labellg : 0,
31330     labelmd : 0,
31331     labelsm : 0,
31332     labelxs : 0,
31333     
31334     getAutoCreate : function()
31335     {   
31336         var managerWidget = {
31337             tag : 'div',
31338             cls : 'roo-document-manager',
31339             cn : [
31340                 {
31341                     tag : 'input',
31342                     cls : 'roo-document-manager-selector',
31343                     type : 'file'
31344                 },
31345                 {
31346                     tag : 'div',
31347                     cls : 'roo-document-manager-uploader',
31348                     cn : [
31349                         {
31350                             tag : 'div',
31351                             cls : 'roo-document-manager-upload-btn',
31352                             html : '<i class="fa fa-plus"></i>'
31353                         }
31354                     ]
31355                     
31356                 }
31357             ]
31358         };
31359         
31360         var content = [
31361             {
31362                 tag : 'div',
31363                 cls : 'column col-md-12',
31364                 cn : managerWidget
31365             }
31366         ];
31367         
31368         if(this.fieldLabel.length){
31369             
31370             content = [
31371                 {
31372                     tag : 'div',
31373                     cls : 'column col-md-12',
31374                     html : this.fieldLabel
31375                 },
31376                 {
31377                     tag : 'div',
31378                     cls : 'column col-md-12',
31379                     cn : managerWidget
31380                 }
31381             ];
31382
31383             if(this.labelAlign == 'left'){
31384                 content = [
31385                     {
31386                         tag : 'div',
31387                         cls : 'column',
31388                         html : this.fieldLabel
31389                     },
31390                     {
31391                         tag : 'div',
31392                         cls : 'column',
31393                         cn : managerWidget
31394                     }
31395                 ];
31396                 
31397                 if(this.labelWidth > 12){
31398                     content[0].style = "width: " + this.labelWidth + 'px';
31399                 }
31400
31401                 if(this.labelWidth < 13 && this.labelmd == 0){
31402                     this.labelmd = this.labelWidth;
31403                 }
31404
31405                 if(this.labellg > 0){
31406                     content[0].cls += ' col-lg-' + this.labellg;
31407                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31408                 }
31409
31410                 if(this.labelmd > 0){
31411                     content[0].cls += ' col-md-' + this.labelmd;
31412                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31413                 }
31414
31415                 if(this.labelsm > 0){
31416                     content[0].cls += ' col-sm-' + this.labelsm;
31417                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31418                 }
31419
31420                 if(this.labelxs > 0){
31421                     content[0].cls += ' col-xs-' + this.labelxs;
31422                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31423                 }
31424                 
31425             }
31426         }
31427         
31428         var cfg = {
31429             tag : 'div',
31430             cls : 'row clearfix',
31431             cn : content
31432         };
31433         
31434         return cfg;
31435         
31436     },
31437     
31438     initEvents : function()
31439     {
31440         this.managerEl = this.el.select('.roo-document-manager', true).first();
31441         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31442         
31443         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31444         this.selectorEl.hide();
31445         
31446         if(this.multiple){
31447             this.selectorEl.attr('multiple', 'multiple');
31448         }
31449         
31450         this.selectorEl.on('change', this.onFileSelected, this);
31451         
31452         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31453         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31454         
31455         this.uploader.on('click', this.onUploaderClick, this);
31456         
31457         this.renderProgressDialog();
31458         
31459         var _this = this;
31460         
31461         window.addEventListener("resize", function() { _this.refresh(); } );
31462         
31463         this.fireEvent('initial', this);
31464     },
31465     
31466     renderProgressDialog : function()
31467     {
31468         var _this = this;
31469         
31470         this.progressDialog = new Roo.bootstrap.Modal({
31471             cls : 'roo-document-manager-progress-dialog',
31472             allow_close : false,
31473             animate : false,
31474             title : '',
31475             buttons : [
31476                 {
31477                     name  :'cancel',
31478                     weight : 'danger',
31479                     html : 'Cancel'
31480                 }
31481             ], 
31482             listeners : { 
31483                 btnclick : function() {
31484                     _this.uploadCancel();
31485                     this.hide();
31486                 }
31487             }
31488         });
31489          
31490         this.progressDialog.render(Roo.get(document.body));
31491          
31492         this.progress = new Roo.bootstrap.Progress({
31493             cls : 'roo-document-manager-progress',
31494             active : true,
31495             striped : true
31496         });
31497         
31498         this.progress.render(this.progressDialog.getChildContainer());
31499         
31500         this.progressBar = new Roo.bootstrap.ProgressBar({
31501             cls : 'roo-document-manager-progress-bar',
31502             aria_valuenow : 0,
31503             aria_valuemin : 0,
31504             aria_valuemax : 12,
31505             panel : 'success'
31506         });
31507         
31508         this.progressBar.render(this.progress.getChildContainer());
31509     },
31510     
31511     onUploaderClick : function(e)
31512     {
31513         e.preventDefault();
31514      
31515         if(this.fireEvent('beforeselectfile', this) != false){
31516             this.selectorEl.dom.click();
31517         }
31518         
31519     },
31520     
31521     onFileSelected : function(e)
31522     {
31523         e.preventDefault();
31524         
31525         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31526             return;
31527         }
31528         
31529         Roo.each(this.selectorEl.dom.files, function(file){
31530             if(this.fireEvent('inspect', this, file) != false){
31531                 this.files.push(file);
31532             }
31533         }, this);
31534         
31535         this.queue();
31536         
31537     },
31538     
31539     queue : function()
31540     {
31541         this.selectorEl.dom.value = '';
31542         
31543         if(!this.files || !this.files.length){
31544             return;
31545         }
31546         
31547         if(this.boxes > 0 && this.files.length > this.boxes){
31548             this.files = this.files.slice(0, this.boxes);
31549         }
31550         
31551         this.uploader.show();
31552         
31553         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31554             this.uploader.hide();
31555         }
31556         
31557         var _this = this;
31558         
31559         var files = [];
31560         
31561         var docs = [];
31562         
31563         Roo.each(this.files, function(file){
31564             
31565             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31566                 var f = this.renderPreview(file);
31567                 files.push(f);
31568                 return;
31569             }
31570             
31571             if(file.type.indexOf('image') != -1){
31572                 this.delegates.push(
31573                     (function(){
31574                         _this.process(file);
31575                     }).createDelegate(this)
31576                 );
31577         
31578                 return;
31579             }
31580             
31581             docs.push(
31582                 (function(){
31583                     _this.process(file);
31584                 }).createDelegate(this)
31585             );
31586             
31587         }, this);
31588         
31589         this.files = files;
31590         
31591         this.delegates = this.delegates.concat(docs);
31592         
31593         if(!this.delegates.length){
31594             this.refresh();
31595             return;
31596         }
31597         
31598         this.progressBar.aria_valuemax = this.delegates.length;
31599         
31600         this.arrange();
31601         
31602         return;
31603     },
31604     
31605     arrange : function()
31606     {
31607         if(!this.delegates.length){
31608             this.progressDialog.hide();
31609             this.refresh();
31610             return;
31611         }
31612         
31613         var delegate = this.delegates.shift();
31614         
31615         this.progressDialog.show();
31616         
31617         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31618         
31619         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31620         
31621         delegate();
31622     },
31623     
31624     refresh : function()
31625     {
31626         this.uploader.show();
31627         
31628         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31629             this.uploader.hide();
31630         }
31631         
31632         Roo.isTouch ? this.closable(false) : this.closable(true);
31633         
31634         this.fireEvent('refresh', this);
31635     },
31636     
31637     onRemove : function(e, el, o)
31638     {
31639         e.preventDefault();
31640         
31641         this.fireEvent('remove', this, o);
31642         
31643     },
31644     
31645     remove : function(o)
31646     {
31647         var files = [];
31648         
31649         Roo.each(this.files, function(file){
31650             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31651                 files.push(file);
31652                 return;
31653             }
31654
31655             o.target.remove();
31656
31657         }, this);
31658         
31659         this.files = files;
31660         
31661         this.refresh();
31662     },
31663     
31664     clear : function()
31665     {
31666         Roo.each(this.files, function(file){
31667             if(!file.target){
31668                 return;
31669             }
31670             
31671             file.target.remove();
31672
31673         }, this);
31674         
31675         this.files = [];
31676         
31677         this.refresh();
31678     },
31679     
31680     onClick : function(e, el, o)
31681     {
31682         e.preventDefault();
31683         
31684         this.fireEvent('click', this, o);
31685         
31686     },
31687     
31688     closable : function(closable)
31689     {
31690         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31691             
31692             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31693             
31694             if(closable){
31695                 el.show();
31696                 return;
31697             }
31698             
31699             el.hide();
31700             
31701         }, this);
31702     },
31703     
31704     xhrOnLoad : function(xhr)
31705     {
31706         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31707             el.remove();
31708         }, this);
31709         
31710         if (xhr.readyState !== 4) {
31711             this.arrange();
31712             this.fireEvent('exception', this, xhr);
31713             return;
31714         }
31715
31716         var response = Roo.decode(xhr.responseText);
31717         
31718         if(!response.success){
31719             this.arrange();
31720             this.fireEvent('exception', this, xhr);
31721             return;
31722         }
31723         
31724         var file = this.renderPreview(response.data);
31725         
31726         this.files.push(file);
31727         
31728         this.arrange();
31729         
31730         this.fireEvent('afterupload', this, xhr);
31731         
31732     },
31733     
31734     xhrOnError : function(xhr)
31735     {
31736         Roo.log('xhr on error');
31737         
31738         var response = Roo.decode(xhr.responseText);
31739           
31740         Roo.log(response);
31741         
31742         this.arrange();
31743     },
31744     
31745     process : function(file)
31746     {
31747         if(this.fireEvent('process', this, file) !== false){
31748             if(this.editable && file.type.indexOf('image') != -1){
31749                 this.fireEvent('edit', this, file);
31750                 return;
31751             }
31752
31753             this.uploadStart(file, false);
31754
31755             return;
31756         }
31757         
31758     },
31759     
31760     uploadStart : function(file, crop)
31761     {
31762         this.xhr = new XMLHttpRequest();
31763         
31764         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31765             this.arrange();
31766             return;
31767         }
31768         
31769         file.xhr = this.xhr;
31770             
31771         this.managerEl.createChild({
31772             tag : 'div',
31773             cls : 'roo-document-manager-loading',
31774             cn : [
31775                 {
31776                     tag : 'div',
31777                     tooltip : file.name,
31778                     cls : 'roo-document-manager-thumb',
31779                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31780                 }
31781             ]
31782
31783         });
31784
31785         this.xhr.open(this.method, this.url, true);
31786         
31787         var headers = {
31788             "Accept": "application/json",
31789             "Cache-Control": "no-cache",
31790             "X-Requested-With": "XMLHttpRequest"
31791         };
31792         
31793         for (var headerName in headers) {
31794             var headerValue = headers[headerName];
31795             if (headerValue) {
31796                 this.xhr.setRequestHeader(headerName, headerValue);
31797             }
31798         }
31799         
31800         var _this = this;
31801         
31802         this.xhr.onload = function()
31803         {
31804             _this.xhrOnLoad(_this.xhr);
31805         }
31806         
31807         this.xhr.onerror = function()
31808         {
31809             _this.xhrOnError(_this.xhr);
31810         }
31811         
31812         var formData = new FormData();
31813
31814         formData.append('returnHTML', 'NO');
31815         
31816         if(crop){
31817             formData.append('crop', crop);
31818         }
31819         
31820         formData.append(this.paramName, file, file.name);
31821         
31822         var options = {
31823             file : file, 
31824             manually : false
31825         };
31826         
31827         if(this.fireEvent('prepare', this, formData, options) != false){
31828             
31829             if(options.manually){
31830                 return;
31831             }
31832             
31833             this.xhr.send(formData);
31834             return;
31835         };
31836         
31837         this.uploadCancel();
31838     },
31839     
31840     uploadCancel : function()
31841     {
31842         if (this.xhr) {
31843             this.xhr.abort();
31844         }
31845         
31846         this.delegates = [];
31847         
31848         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31849             el.remove();
31850         }, this);
31851         
31852         this.arrange();
31853     },
31854     
31855     renderPreview : function(file)
31856     {
31857         if(typeof(file.target) != 'undefined' && file.target){
31858             return file;
31859         }
31860         
31861         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31862         
31863         var previewEl = this.managerEl.createChild({
31864             tag : 'div',
31865             cls : 'roo-document-manager-preview',
31866             cn : [
31867                 {
31868                     tag : 'div',
31869                     tooltip : file[this.toolTipName],
31870                     cls : 'roo-document-manager-thumb',
31871                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31872                 },
31873                 {
31874                     tag : 'button',
31875                     cls : 'close',
31876                     html : '<i class="fa fa-times-circle"></i>'
31877                 }
31878             ]
31879         });
31880
31881         var close = previewEl.select('button.close', true).first();
31882
31883         close.on('click', this.onRemove, this, file);
31884
31885         file.target = previewEl;
31886
31887         var image = previewEl.select('img', true).first();
31888         
31889         var _this = this;
31890         
31891         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31892         
31893         image.on('click', this.onClick, this, file);
31894         
31895         this.fireEvent('previewrendered', this, file);
31896         
31897         return file;
31898         
31899     },
31900     
31901     onPreviewLoad : function(file, image)
31902     {
31903         if(typeof(file.target) == 'undefined' || !file.target){
31904             return;
31905         }
31906         
31907         var width = image.dom.naturalWidth || image.dom.width;
31908         var height = image.dom.naturalHeight || image.dom.height;
31909         
31910         if(!this.previewResize) {
31911             return;
31912         }
31913         
31914         if(width > height){
31915             file.target.addClass('wide');
31916             return;
31917         }
31918         
31919         file.target.addClass('tall');
31920         return;
31921         
31922     },
31923     
31924     uploadFromSource : function(file, crop)
31925     {
31926         this.xhr = new XMLHttpRequest();
31927         
31928         this.managerEl.createChild({
31929             tag : 'div',
31930             cls : 'roo-document-manager-loading',
31931             cn : [
31932                 {
31933                     tag : 'div',
31934                     tooltip : file.name,
31935                     cls : 'roo-document-manager-thumb',
31936                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31937                 }
31938             ]
31939
31940         });
31941
31942         this.xhr.open(this.method, this.url, true);
31943         
31944         var headers = {
31945             "Accept": "application/json",
31946             "Cache-Control": "no-cache",
31947             "X-Requested-With": "XMLHttpRequest"
31948         };
31949         
31950         for (var headerName in headers) {
31951             var headerValue = headers[headerName];
31952             if (headerValue) {
31953                 this.xhr.setRequestHeader(headerName, headerValue);
31954             }
31955         }
31956         
31957         var _this = this;
31958         
31959         this.xhr.onload = function()
31960         {
31961             _this.xhrOnLoad(_this.xhr);
31962         }
31963         
31964         this.xhr.onerror = function()
31965         {
31966             _this.xhrOnError(_this.xhr);
31967         }
31968         
31969         var formData = new FormData();
31970
31971         formData.append('returnHTML', 'NO');
31972         
31973         formData.append('crop', crop);
31974         
31975         if(typeof(file.filename) != 'undefined'){
31976             formData.append('filename', file.filename);
31977         }
31978         
31979         if(typeof(file.mimetype) != 'undefined'){
31980             formData.append('mimetype', file.mimetype);
31981         }
31982         
31983         Roo.log(formData);
31984         
31985         if(this.fireEvent('prepare', this, formData) != false){
31986             this.xhr.send(formData);
31987         };
31988     }
31989 });
31990
31991 /*
31992 * Licence: LGPL
31993 */
31994
31995 /**
31996  * @class Roo.bootstrap.DocumentViewer
31997  * @extends Roo.bootstrap.Component
31998  * Bootstrap DocumentViewer class
31999  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32000  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32001  * 
32002  * @constructor
32003  * Create a new DocumentViewer
32004  * @param {Object} config The config object
32005  */
32006
32007 Roo.bootstrap.DocumentViewer = function(config){
32008     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32009     
32010     this.addEvents({
32011         /**
32012          * @event initial
32013          * Fire after initEvent
32014          * @param {Roo.bootstrap.DocumentViewer} this
32015          */
32016         "initial" : true,
32017         /**
32018          * @event click
32019          * Fire after click
32020          * @param {Roo.bootstrap.DocumentViewer} this
32021          */
32022         "click" : true,
32023         /**
32024          * @event download
32025          * Fire after download button
32026          * @param {Roo.bootstrap.DocumentViewer} this
32027          */
32028         "download" : true,
32029         /**
32030          * @event trash
32031          * Fire after trash button
32032          * @param {Roo.bootstrap.DocumentViewer} this
32033          */
32034         "trash" : true
32035         
32036     });
32037 };
32038
32039 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32040     
32041     showDownload : true,
32042     
32043     showTrash : true,
32044     
32045     getAutoCreate : function()
32046     {
32047         var cfg = {
32048             tag : 'div',
32049             cls : 'roo-document-viewer',
32050             cn : [
32051                 {
32052                     tag : 'div',
32053                     cls : 'roo-document-viewer-body',
32054                     cn : [
32055                         {
32056                             tag : 'div',
32057                             cls : 'roo-document-viewer-thumb',
32058                             cn : [
32059                                 {
32060                                     tag : 'img',
32061                                     cls : 'roo-document-viewer-image'
32062                                 }
32063                             ]
32064                         }
32065                     ]
32066                 },
32067                 {
32068                     tag : 'div',
32069                     cls : 'roo-document-viewer-footer',
32070                     cn : {
32071                         tag : 'div',
32072                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32073                         cn : [
32074                             {
32075                                 tag : 'div',
32076                                 cls : 'btn-group roo-document-viewer-download',
32077                                 cn : [
32078                                     {
32079                                         tag : 'button',
32080                                         cls : 'btn btn-default',
32081                                         html : '<i class="fa fa-download"></i>'
32082                                     }
32083                                 ]
32084                             },
32085                             {
32086                                 tag : 'div',
32087                                 cls : 'btn-group roo-document-viewer-trash',
32088                                 cn : [
32089                                     {
32090                                         tag : 'button',
32091                                         cls : 'btn btn-default',
32092                                         html : '<i class="fa fa-trash"></i>'
32093                                     }
32094                                 ]
32095                             }
32096                         ]
32097                     }
32098                 }
32099             ]
32100         };
32101         
32102         return cfg;
32103     },
32104     
32105     initEvents : function()
32106     {
32107         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32108         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32109         
32110         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32111         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32112         
32113         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32114         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32115         
32116         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32117         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32118         
32119         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32120         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32121         
32122         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32123         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32124         
32125         this.bodyEl.on('click', this.onClick, this);
32126         this.downloadBtn.on('click', this.onDownload, this);
32127         this.trashBtn.on('click', this.onTrash, this);
32128         
32129         this.downloadBtn.hide();
32130         this.trashBtn.hide();
32131         
32132         if(this.showDownload){
32133             this.downloadBtn.show();
32134         }
32135         
32136         if(this.showTrash){
32137             this.trashBtn.show();
32138         }
32139         
32140         if(!this.showDownload && !this.showTrash) {
32141             this.footerEl.hide();
32142         }
32143         
32144     },
32145     
32146     initial : function()
32147     {
32148         this.fireEvent('initial', this);
32149         
32150     },
32151     
32152     onClick : function(e)
32153     {
32154         e.preventDefault();
32155         
32156         this.fireEvent('click', this);
32157     },
32158     
32159     onDownload : function(e)
32160     {
32161         e.preventDefault();
32162         
32163         this.fireEvent('download', this);
32164     },
32165     
32166     onTrash : function(e)
32167     {
32168         e.preventDefault();
32169         
32170         this.fireEvent('trash', this);
32171     }
32172     
32173 });
32174 /*
32175  * - LGPL
32176  *
32177  * nav progress bar
32178  * 
32179  */
32180
32181 /**
32182  * @class Roo.bootstrap.NavProgressBar
32183  * @extends Roo.bootstrap.Component
32184  * Bootstrap NavProgressBar class
32185  * 
32186  * @constructor
32187  * Create a new nav progress bar
32188  * @param {Object} config The config object
32189  */
32190
32191 Roo.bootstrap.NavProgressBar = function(config){
32192     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32193
32194     this.bullets = this.bullets || [];
32195    
32196 //    Roo.bootstrap.NavProgressBar.register(this);
32197      this.addEvents({
32198         /**
32199              * @event changed
32200              * Fires when the active item changes
32201              * @param {Roo.bootstrap.NavProgressBar} this
32202              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32203              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32204          */
32205         'changed': true
32206      });
32207     
32208 };
32209
32210 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32211     
32212     bullets : [],
32213     barItems : [],
32214     
32215     getAutoCreate : function()
32216     {
32217         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32218         
32219         cfg = {
32220             tag : 'div',
32221             cls : 'roo-navigation-bar-group',
32222             cn : [
32223                 {
32224                     tag : 'div',
32225                     cls : 'roo-navigation-top-bar'
32226                 },
32227                 {
32228                     tag : 'div',
32229                     cls : 'roo-navigation-bullets-bar',
32230                     cn : [
32231                         {
32232                             tag : 'ul',
32233                             cls : 'roo-navigation-bar'
32234                         }
32235                     ]
32236                 },
32237                 
32238                 {
32239                     tag : 'div',
32240                     cls : 'roo-navigation-bottom-bar'
32241                 }
32242             ]
32243             
32244         };
32245         
32246         return cfg;
32247         
32248     },
32249     
32250     initEvents: function() 
32251     {
32252         
32253     },
32254     
32255     onRender : function(ct, position) 
32256     {
32257         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32258         
32259         if(this.bullets.length){
32260             Roo.each(this.bullets, function(b){
32261                this.addItem(b);
32262             }, this);
32263         }
32264         
32265         this.format();
32266         
32267     },
32268     
32269     addItem : function(cfg)
32270     {
32271         var item = new Roo.bootstrap.NavProgressItem(cfg);
32272         
32273         item.parentId = this.id;
32274         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32275         
32276         if(cfg.html){
32277             var top = new Roo.bootstrap.Element({
32278                 tag : 'div',
32279                 cls : 'roo-navigation-bar-text'
32280             });
32281             
32282             var bottom = new Roo.bootstrap.Element({
32283                 tag : 'div',
32284                 cls : 'roo-navigation-bar-text'
32285             });
32286             
32287             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32288             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32289             
32290             var topText = new Roo.bootstrap.Element({
32291                 tag : 'span',
32292                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32293             });
32294             
32295             var bottomText = new Roo.bootstrap.Element({
32296                 tag : 'span',
32297                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32298             });
32299             
32300             topText.onRender(top.el, null);
32301             bottomText.onRender(bottom.el, null);
32302             
32303             item.topEl = top;
32304             item.bottomEl = bottom;
32305         }
32306         
32307         this.barItems.push(item);
32308         
32309         return item;
32310     },
32311     
32312     getActive : function()
32313     {
32314         var active = false;
32315         
32316         Roo.each(this.barItems, function(v){
32317             
32318             if (!v.isActive()) {
32319                 return;
32320             }
32321             
32322             active = v;
32323             return false;
32324             
32325         });
32326         
32327         return active;
32328     },
32329     
32330     setActiveItem : function(item)
32331     {
32332         var prev = false;
32333         
32334         Roo.each(this.barItems, function(v){
32335             if (v.rid == item.rid) {
32336                 return ;
32337             }
32338             
32339             if (v.isActive()) {
32340                 v.setActive(false);
32341                 prev = v;
32342             }
32343         });
32344
32345         item.setActive(true);
32346         
32347         this.fireEvent('changed', this, item, prev);
32348     },
32349     
32350     getBarItem: function(rid)
32351     {
32352         var ret = false;
32353         
32354         Roo.each(this.barItems, function(e) {
32355             if (e.rid != rid) {
32356                 return;
32357             }
32358             
32359             ret =  e;
32360             return false;
32361         });
32362         
32363         return ret;
32364     },
32365     
32366     indexOfItem : function(item)
32367     {
32368         var index = false;
32369         
32370         Roo.each(this.barItems, function(v, i){
32371             
32372             if (v.rid != item.rid) {
32373                 return;
32374             }
32375             
32376             index = i;
32377             return false
32378         });
32379         
32380         return index;
32381     },
32382     
32383     setActiveNext : function()
32384     {
32385         var i = this.indexOfItem(this.getActive());
32386         
32387         if (i > this.barItems.length) {
32388             return;
32389         }
32390         
32391         this.setActiveItem(this.barItems[i+1]);
32392     },
32393     
32394     setActivePrev : function()
32395     {
32396         var i = this.indexOfItem(this.getActive());
32397         
32398         if (i  < 1) {
32399             return;
32400         }
32401         
32402         this.setActiveItem(this.barItems[i-1]);
32403     },
32404     
32405     format : function()
32406     {
32407         if(!this.barItems.length){
32408             return;
32409         }
32410      
32411         var width = 100 / this.barItems.length;
32412         
32413         Roo.each(this.barItems, function(i){
32414             i.el.setStyle('width', width + '%');
32415             i.topEl.el.setStyle('width', width + '%');
32416             i.bottomEl.el.setStyle('width', width + '%');
32417         }, this);
32418         
32419     }
32420     
32421 });
32422 /*
32423  * - LGPL
32424  *
32425  * Nav Progress Item
32426  * 
32427  */
32428
32429 /**
32430  * @class Roo.bootstrap.NavProgressItem
32431  * @extends Roo.bootstrap.Component
32432  * Bootstrap NavProgressItem class
32433  * @cfg {String} rid the reference id
32434  * @cfg {Boolean} active (true|false) Is item active default false
32435  * @cfg {Boolean} disabled (true|false) Is item active default false
32436  * @cfg {String} html
32437  * @cfg {String} position (top|bottom) text position default bottom
32438  * @cfg {String} icon show icon instead of number
32439  * 
32440  * @constructor
32441  * Create a new NavProgressItem
32442  * @param {Object} config The config object
32443  */
32444 Roo.bootstrap.NavProgressItem = function(config){
32445     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32446     this.addEvents({
32447         // raw events
32448         /**
32449          * @event click
32450          * The raw click event for the entire grid.
32451          * @param {Roo.bootstrap.NavProgressItem} this
32452          * @param {Roo.EventObject} e
32453          */
32454         "click" : true
32455     });
32456    
32457 };
32458
32459 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32460     
32461     rid : '',
32462     active : false,
32463     disabled : false,
32464     html : '',
32465     position : 'bottom',
32466     icon : false,
32467     
32468     getAutoCreate : function()
32469     {
32470         var iconCls = 'roo-navigation-bar-item-icon';
32471         
32472         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32473         
32474         var cfg = {
32475             tag: 'li',
32476             cls: 'roo-navigation-bar-item',
32477             cn : [
32478                 {
32479                     tag : 'i',
32480                     cls : iconCls
32481                 }
32482             ]
32483         };
32484         
32485         if(this.active){
32486             cfg.cls += ' active';
32487         }
32488         if(this.disabled){
32489             cfg.cls += ' disabled';
32490         }
32491         
32492         return cfg;
32493     },
32494     
32495     disable : function()
32496     {
32497         this.setDisabled(true);
32498     },
32499     
32500     enable : function()
32501     {
32502         this.setDisabled(false);
32503     },
32504     
32505     initEvents: function() 
32506     {
32507         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32508         
32509         this.iconEl.on('click', this.onClick, this);
32510     },
32511     
32512     onClick : function(e)
32513     {
32514         e.preventDefault();
32515         
32516         if(this.disabled){
32517             return;
32518         }
32519         
32520         if(this.fireEvent('click', this, e) === false){
32521             return;
32522         };
32523         
32524         this.parent().setActiveItem(this);
32525     },
32526     
32527     isActive: function () 
32528     {
32529         return this.active;
32530     },
32531     
32532     setActive : function(state)
32533     {
32534         if(this.active == state){
32535             return;
32536         }
32537         
32538         this.active = state;
32539         
32540         if (state) {
32541             this.el.addClass('active');
32542             return;
32543         }
32544         
32545         this.el.removeClass('active');
32546         
32547         return;
32548     },
32549     
32550     setDisabled : function(state)
32551     {
32552         if(this.disabled == state){
32553             return;
32554         }
32555         
32556         this.disabled = state;
32557         
32558         if (state) {
32559             this.el.addClass('disabled');
32560             return;
32561         }
32562         
32563         this.el.removeClass('disabled');
32564     },
32565     
32566     tooltipEl : function()
32567     {
32568         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32569     }
32570 });
32571  
32572
32573  /*
32574  * - LGPL
32575  *
32576  * FieldLabel
32577  * 
32578  */
32579
32580 /**
32581  * @class Roo.bootstrap.FieldLabel
32582  * @extends Roo.bootstrap.Component
32583  * Bootstrap FieldLabel class
32584  * @cfg {String} html contents of the element
32585  * @cfg {String} tag tag of the element default label
32586  * @cfg {String} cls class of the element
32587  * @cfg {String} target label target 
32588  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32589  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32590  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32591  * @cfg {String} iconTooltip default "This field is required"
32592  * @cfg {String} indicatorpos (left|right) default left
32593  * 
32594  * @constructor
32595  * Create a new FieldLabel
32596  * @param {Object} config The config object
32597  */
32598
32599 Roo.bootstrap.FieldLabel = function(config){
32600     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32601     
32602     this.addEvents({
32603             /**
32604              * @event invalid
32605              * Fires after the field has been marked as invalid.
32606              * @param {Roo.form.FieldLabel} this
32607              * @param {String} msg The validation message
32608              */
32609             invalid : true,
32610             /**
32611              * @event valid
32612              * Fires after the field has been validated with no errors.
32613              * @param {Roo.form.FieldLabel} this
32614              */
32615             valid : true
32616         });
32617 };
32618
32619 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32620     
32621     tag: 'label',
32622     cls: '',
32623     html: '',
32624     target: '',
32625     allowBlank : true,
32626     invalidClass : 'has-warning',
32627     validClass : 'has-success',
32628     iconTooltip : 'This field is required',
32629     indicatorpos : 'left',
32630     
32631     getAutoCreate : function(){
32632         
32633         var cls = "";
32634         if (!this.allowBlank) {
32635             cls  = "visible";
32636         }
32637         
32638         var cfg = {
32639             tag : this.tag,
32640             cls : 'roo-bootstrap-field-label ' + this.cls,
32641             for : this.target,
32642             cn : [
32643                 {
32644                     tag : 'i',
32645                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32646                     tooltip : this.iconTooltip
32647                 },
32648                 {
32649                     tag : 'span',
32650                     html : this.html
32651                 }
32652             ] 
32653         };
32654         
32655         if(this.indicatorpos == 'right'){
32656             var cfg = {
32657                 tag : this.tag,
32658                 cls : 'roo-bootstrap-field-label ' + this.cls,
32659                 for : this.target,
32660                 cn : [
32661                     {
32662                         tag : 'span',
32663                         html : this.html
32664                     },
32665                     {
32666                         tag : 'i',
32667                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32668                         tooltip : this.iconTooltip
32669                     }
32670                 ] 
32671             };
32672         }
32673         
32674         return cfg;
32675     },
32676     
32677     initEvents: function() 
32678     {
32679         Roo.bootstrap.Element.superclass.initEvents.call(this);
32680         
32681         this.indicator = this.indicatorEl();
32682         
32683         if(this.indicator){
32684             this.indicator.removeClass('visible');
32685             this.indicator.addClass('invisible');
32686         }
32687         
32688         Roo.bootstrap.FieldLabel.register(this);
32689     },
32690     
32691     indicatorEl : function()
32692     {
32693         var indicator = this.el.select('i.roo-required-indicator',true).first();
32694         
32695         if(!indicator){
32696             return false;
32697         }
32698         
32699         return indicator;
32700         
32701     },
32702     
32703     /**
32704      * Mark this field as valid
32705      */
32706     markValid : function()
32707     {
32708         if(this.indicator){
32709             this.indicator.removeClass('visible');
32710             this.indicator.addClass('invisible');
32711         }
32712         if (Roo.bootstrap.version == 3) {
32713             this.el.removeClass(this.invalidClass);
32714             this.el.addClass(this.validClass);
32715         } else {
32716             this.el.removeClass('is-invalid');
32717             this.el.addClass('is-valid');
32718         }
32719         
32720         
32721         this.fireEvent('valid', this);
32722     },
32723     
32724     /**
32725      * Mark this field as invalid
32726      * @param {String} msg The validation message
32727      */
32728     markInvalid : function(msg)
32729     {
32730         if(this.indicator){
32731             this.indicator.removeClass('invisible');
32732             this.indicator.addClass('visible');
32733         }
32734           if (Roo.bootstrap.version == 3) {
32735             this.el.removeClass(this.validClass);
32736             this.el.addClass(this.invalidClass);
32737         } else {
32738             this.el.removeClass('is-valid');
32739             this.el.addClass('is-invalid');
32740         }
32741         
32742         
32743         this.fireEvent('invalid', this, msg);
32744     }
32745     
32746    
32747 });
32748
32749 Roo.apply(Roo.bootstrap.FieldLabel, {
32750     
32751     groups: {},
32752     
32753      /**
32754     * register a FieldLabel Group
32755     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32756     */
32757     register : function(label)
32758     {
32759         if(this.groups.hasOwnProperty(label.target)){
32760             return;
32761         }
32762      
32763         this.groups[label.target] = label;
32764         
32765     },
32766     /**
32767     * fetch a FieldLabel Group based on the target
32768     * @param {string} target
32769     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32770     */
32771     get: function(target) {
32772         if (typeof(this.groups[target]) == 'undefined') {
32773             return false;
32774         }
32775         
32776         return this.groups[target] ;
32777     }
32778 });
32779
32780  
32781
32782  /*
32783  * - LGPL
32784  *
32785  * page DateSplitField.
32786  * 
32787  */
32788
32789
32790 /**
32791  * @class Roo.bootstrap.DateSplitField
32792  * @extends Roo.bootstrap.Component
32793  * Bootstrap DateSplitField class
32794  * @cfg {string} fieldLabel - the label associated
32795  * @cfg {Number} labelWidth set the width of label (0-12)
32796  * @cfg {String} labelAlign (top|left)
32797  * @cfg {Boolean} dayAllowBlank (true|false) default false
32798  * @cfg {Boolean} monthAllowBlank (true|false) default false
32799  * @cfg {Boolean} yearAllowBlank (true|false) default false
32800  * @cfg {string} dayPlaceholder 
32801  * @cfg {string} monthPlaceholder
32802  * @cfg {string} yearPlaceholder
32803  * @cfg {string} dayFormat default 'd'
32804  * @cfg {string} monthFormat default 'm'
32805  * @cfg {string} yearFormat default 'Y'
32806  * @cfg {Number} labellg set the width of label (1-12)
32807  * @cfg {Number} labelmd set the width of label (1-12)
32808  * @cfg {Number} labelsm set the width of label (1-12)
32809  * @cfg {Number} labelxs set the width of label (1-12)
32810
32811  *     
32812  * @constructor
32813  * Create a new DateSplitField
32814  * @param {Object} config The config object
32815  */
32816
32817 Roo.bootstrap.DateSplitField = function(config){
32818     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32819     
32820     this.addEvents({
32821         // raw events
32822          /**
32823          * @event years
32824          * getting the data of years
32825          * @param {Roo.bootstrap.DateSplitField} this
32826          * @param {Object} years
32827          */
32828         "years" : true,
32829         /**
32830          * @event days
32831          * getting the data of days
32832          * @param {Roo.bootstrap.DateSplitField} this
32833          * @param {Object} days
32834          */
32835         "days" : true,
32836         /**
32837          * @event invalid
32838          * Fires after the field has been marked as invalid.
32839          * @param {Roo.form.Field} this
32840          * @param {String} msg The validation message
32841          */
32842         invalid : true,
32843        /**
32844          * @event valid
32845          * Fires after the field has been validated with no errors.
32846          * @param {Roo.form.Field} this
32847          */
32848         valid : true
32849     });
32850 };
32851
32852 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32853     
32854     fieldLabel : '',
32855     labelAlign : 'top',
32856     labelWidth : 3,
32857     dayAllowBlank : false,
32858     monthAllowBlank : false,
32859     yearAllowBlank : false,
32860     dayPlaceholder : '',
32861     monthPlaceholder : '',
32862     yearPlaceholder : '',
32863     dayFormat : 'd',
32864     monthFormat : 'm',
32865     yearFormat : 'Y',
32866     isFormField : true,
32867     labellg : 0,
32868     labelmd : 0,
32869     labelsm : 0,
32870     labelxs : 0,
32871     
32872     getAutoCreate : function()
32873     {
32874         var cfg = {
32875             tag : 'div',
32876             cls : 'row roo-date-split-field-group',
32877             cn : [
32878                 {
32879                     tag : 'input',
32880                     type : 'hidden',
32881                     cls : 'form-hidden-field roo-date-split-field-group-value',
32882                     name : this.name
32883                 }
32884             ]
32885         };
32886         
32887         var labelCls = 'col-md-12';
32888         var contentCls = 'col-md-4';
32889         
32890         if(this.fieldLabel){
32891             
32892             var label = {
32893                 tag : 'div',
32894                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32895                 cn : [
32896                     {
32897                         tag : 'label',
32898                         html : this.fieldLabel
32899                     }
32900                 ]
32901             };
32902             
32903             if(this.labelAlign == 'left'){
32904             
32905                 if(this.labelWidth > 12){
32906                     label.style = "width: " + this.labelWidth + 'px';
32907                 }
32908
32909                 if(this.labelWidth < 13 && this.labelmd == 0){
32910                     this.labelmd = this.labelWidth;
32911                 }
32912
32913                 if(this.labellg > 0){
32914                     labelCls = ' col-lg-' + this.labellg;
32915                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32916                 }
32917
32918                 if(this.labelmd > 0){
32919                     labelCls = ' col-md-' + this.labelmd;
32920                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32921                 }
32922
32923                 if(this.labelsm > 0){
32924                     labelCls = ' col-sm-' + this.labelsm;
32925                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32926                 }
32927
32928                 if(this.labelxs > 0){
32929                     labelCls = ' col-xs-' + this.labelxs;
32930                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32931                 }
32932             }
32933             
32934             label.cls += ' ' + labelCls;
32935             
32936             cfg.cn.push(label);
32937         }
32938         
32939         Roo.each(['day', 'month', 'year'], function(t){
32940             cfg.cn.push({
32941                 tag : 'div',
32942                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32943             });
32944         }, this);
32945         
32946         return cfg;
32947     },
32948     
32949     inputEl: function ()
32950     {
32951         return this.el.select('.roo-date-split-field-group-value', true).first();
32952     },
32953     
32954     onRender : function(ct, position) 
32955     {
32956         var _this = this;
32957         
32958         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32959         
32960         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32961         
32962         this.dayField = new Roo.bootstrap.ComboBox({
32963             allowBlank : this.dayAllowBlank,
32964             alwaysQuery : true,
32965             displayField : 'value',
32966             editable : false,
32967             fieldLabel : '',
32968             forceSelection : true,
32969             mode : 'local',
32970             placeholder : this.dayPlaceholder,
32971             selectOnFocus : true,
32972             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32973             triggerAction : 'all',
32974             typeAhead : true,
32975             valueField : 'value',
32976             store : new Roo.data.SimpleStore({
32977                 data : (function() {    
32978                     var days = [];
32979                     _this.fireEvent('days', _this, days);
32980                     return days;
32981                 })(),
32982                 fields : [ 'value' ]
32983             }),
32984             listeners : {
32985                 select : function (_self, record, index)
32986                 {
32987                     _this.setValue(_this.getValue());
32988                 }
32989             }
32990         });
32991
32992         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32993         
32994         this.monthField = new Roo.bootstrap.MonthField({
32995             after : '<i class=\"fa fa-calendar\"></i>',
32996             allowBlank : this.monthAllowBlank,
32997             placeholder : this.monthPlaceholder,
32998             readOnly : true,
32999             listeners : {
33000                 render : function (_self)
33001                 {
33002                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33003                         e.preventDefault();
33004                         _self.focus();
33005                     });
33006                 },
33007                 select : function (_self, oldvalue, newvalue)
33008                 {
33009                     _this.setValue(_this.getValue());
33010                 }
33011             }
33012         });
33013         
33014         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33015         
33016         this.yearField = new Roo.bootstrap.ComboBox({
33017             allowBlank : this.yearAllowBlank,
33018             alwaysQuery : true,
33019             displayField : 'value',
33020             editable : false,
33021             fieldLabel : '',
33022             forceSelection : true,
33023             mode : 'local',
33024             placeholder : this.yearPlaceholder,
33025             selectOnFocus : true,
33026             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33027             triggerAction : 'all',
33028             typeAhead : true,
33029             valueField : 'value',
33030             store : new Roo.data.SimpleStore({
33031                 data : (function() {
33032                     var years = [];
33033                     _this.fireEvent('years', _this, years);
33034                     return years;
33035                 })(),
33036                 fields : [ 'value' ]
33037             }),
33038             listeners : {
33039                 select : function (_self, record, index)
33040                 {
33041                     _this.setValue(_this.getValue());
33042                 }
33043             }
33044         });
33045
33046         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33047     },
33048     
33049     setValue : function(v, format)
33050     {
33051         this.inputEl.dom.value = v;
33052         
33053         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33054         
33055         var d = Date.parseDate(v, f);
33056         
33057         if(!d){
33058             this.validate();
33059             return;
33060         }
33061         
33062         this.setDay(d.format(this.dayFormat));
33063         this.setMonth(d.format(this.monthFormat));
33064         this.setYear(d.format(this.yearFormat));
33065         
33066         this.validate();
33067         
33068         return;
33069     },
33070     
33071     setDay : function(v)
33072     {
33073         this.dayField.setValue(v);
33074         this.inputEl.dom.value = this.getValue();
33075         this.validate();
33076         return;
33077     },
33078     
33079     setMonth : function(v)
33080     {
33081         this.monthField.setValue(v, true);
33082         this.inputEl.dom.value = this.getValue();
33083         this.validate();
33084         return;
33085     },
33086     
33087     setYear : function(v)
33088     {
33089         this.yearField.setValue(v);
33090         this.inputEl.dom.value = this.getValue();
33091         this.validate();
33092         return;
33093     },
33094     
33095     getDay : function()
33096     {
33097         return this.dayField.getValue();
33098     },
33099     
33100     getMonth : function()
33101     {
33102         return this.monthField.getValue();
33103     },
33104     
33105     getYear : function()
33106     {
33107         return this.yearField.getValue();
33108     },
33109     
33110     getValue : function()
33111     {
33112         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33113         
33114         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33115         
33116         return date;
33117     },
33118     
33119     reset : function()
33120     {
33121         this.setDay('');
33122         this.setMonth('');
33123         this.setYear('');
33124         this.inputEl.dom.value = '';
33125         this.validate();
33126         return;
33127     },
33128     
33129     validate : function()
33130     {
33131         var d = this.dayField.validate();
33132         var m = this.monthField.validate();
33133         var y = this.yearField.validate();
33134         
33135         var valid = true;
33136         
33137         if(
33138                 (!this.dayAllowBlank && !d) ||
33139                 (!this.monthAllowBlank && !m) ||
33140                 (!this.yearAllowBlank && !y)
33141         ){
33142             valid = false;
33143         }
33144         
33145         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33146             return valid;
33147         }
33148         
33149         if(valid){
33150             this.markValid();
33151             return valid;
33152         }
33153         
33154         this.markInvalid();
33155         
33156         return valid;
33157     },
33158     
33159     markValid : function()
33160     {
33161         
33162         var label = this.el.select('label', true).first();
33163         var icon = this.el.select('i.fa-star', true).first();
33164
33165         if(label && icon){
33166             icon.remove();
33167         }
33168         
33169         this.fireEvent('valid', this);
33170     },
33171     
33172      /**
33173      * Mark this field as invalid
33174      * @param {String} msg The validation message
33175      */
33176     markInvalid : function(msg)
33177     {
33178         
33179         var label = this.el.select('label', true).first();
33180         var icon = this.el.select('i.fa-star', true).first();
33181
33182         if(label && !icon){
33183             this.el.select('.roo-date-split-field-label', true).createChild({
33184                 tag : 'i',
33185                 cls : 'text-danger fa fa-lg fa-star',
33186                 tooltip : 'This field is required',
33187                 style : 'margin-right:5px;'
33188             }, label, true);
33189         }
33190         
33191         this.fireEvent('invalid', this, msg);
33192     },
33193     
33194     clearInvalid : function()
33195     {
33196         var label = this.el.select('label', true).first();
33197         var icon = this.el.select('i.fa-star', true).first();
33198
33199         if(label && icon){
33200             icon.remove();
33201         }
33202         
33203         this.fireEvent('valid', this);
33204     },
33205     
33206     getName: function()
33207     {
33208         return this.name;
33209     }
33210     
33211 });
33212
33213  /**
33214  *
33215  * This is based on 
33216  * http://masonry.desandro.com
33217  *
33218  * The idea is to render all the bricks based on vertical width...
33219  *
33220  * The original code extends 'outlayer' - we might need to use that....
33221  * 
33222  */
33223
33224
33225 /**
33226  * @class Roo.bootstrap.LayoutMasonry
33227  * @extends Roo.bootstrap.Component
33228  * Bootstrap Layout Masonry class
33229  * 
33230  * @constructor
33231  * Create a new Element
33232  * @param {Object} config The config object
33233  */
33234
33235 Roo.bootstrap.LayoutMasonry = function(config){
33236     
33237     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33238     
33239     this.bricks = [];
33240     
33241     Roo.bootstrap.LayoutMasonry.register(this);
33242     
33243     this.addEvents({
33244         // raw events
33245         /**
33246          * @event layout
33247          * Fire after layout the items
33248          * @param {Roo.bootstrap.LayoutMasonry} this
33249          * @param {Roo.EventObject} e
33250          */
33251         "layout" : true
33252     });
33253     
33254 };
33255
33256 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33257     
33258     /**
33259      * @cfg {Boolean} isLayoutInstant = no animation?
33260      */   
33261     isLayoutInstant : false, // needed?
33262    
33263     /**
33264      * @cfg {Number} boxWidth  width of the columns
33265      */   
33266     boxWidth : 450,
33267     
33268       /**
33269      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33270      */   
33271     boxHeight : 0,
33272     
33273     /**
33274      * @cfg {Number} padWidth padding below box..
33275      */   
33276     padWidth : 10, 
33277     
33278     /**
33279      * @cfg {Number} gutter gutter width..
33280      */   
33281     gutter : 10,
33282     
33283      /**
33284      * @cfg {Number} maxCols maximum number of columns
33285      */   
33286     
33287     maxCols: 0,
33288     
33289     /**
33290      * @cfg {Boolean} isAutoInitial defalut true
33291      */   
33292     isAutoInitial : true, 
33293     
33294     containerWidth: 0,
33295     
33296     /**
33297      * @cfg {Boolean} isHorizontal defalut false
33298      */   
33299     isHorizontal : false, 
33300
33301     currentSize : null,
33302     
33303     tag: 'div',
33304     
33305     cls: '',
33306     
33307     bricks: null, //CompositeElement
33308     
33309     cols : 1,
33310     
33311     _isLayoutInited : false,
33312     
33313 //    isAlternative : false, // only use for vertical layout...
33314     
33315     /**
33316      * @cfg {Number} alternativePadWidth padding below box..
33317      */   
33318     alternativePadWidth : 50,
33319     
33320     selectedBrick : [],
33321     
33322     getAutoCreate : function(){
33323         
33324         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33325         
33326         var cfg = {
33327             tag: this.tag,
33328             cls: 'blog-masonary-wrapper ' + this.cls,
33329             cn : {
33330                 cls : 'mas-boxes masonary'
33331             }
33332         };
33333         
33334         return cfg;
33335     },
33336     
33337     getChildContainer: function( )
33338     {
33339         if (this.boxesEl) {
33340             return this.boxesEl;
33341         }
33342         
33343         this.boxesEl = this.el.select('.mas-boxes').first();
33344         
33345         return this.boxesEl;
33346     },
33347     
33348     
33349     initEvents : function()
33350     {
33351         var _this = this;
33352         
33353         if(this.isAutoInitial){
33354             Roo.log('hook children rendered');
33355             this.on('childrenrendered', function() {
33356                 Roo.log('children rendered');
33357                 _this.initial();
33358             } ,this);
33359         }
33360     },
33361     
33362     initial : function()
33363     {
33364         this.selectedBrick = [];
33365         
33366         this.currentSize = this.el.getBox(true);
33367         
33368         Roo.EventManager.onWindowResize(this.resize, this); 
33369
33370         if(!this.isAutoInitial){
33371             this.layout();
33372             return;
33373         }
33374         
33375         this.layout();
33376         
33377         return;
33378         //this.layout.defer(500,this);
33379         
33380     },
33381     
33382     resize : function()
33383     {
33384         var cs = this.el.getBox(true);
33385         
33386         if (
33387                 this.currentSize.width == cs.width && 
33388                 this.currentSize.x == cs.x && 
33389                 this.currentSize.height == cs.height && 
33390                 this.currentSize.y == cs.y 
33391         ) {
33392             Roo.log("no change in with or X or Y");
33393             return;
33394         }
33395         
33396         this.currentSize = cs;
33397         
33398         this.layout();
33399         
33400     },
33401     
33402     layout : function()
33403     {   
33404         this._resetLayout();
33405         
33406         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33407         
33408         this.layoutItems( isInstant );
33409       
33410         this._isLayoutInited = true;
33411         
33412         this.fireEvent('layout', this);
33413         
33414     },
33415     
33416     _resetLayout : function()
33417     {
33418         if(this.isHorizontal){
33419             this.horizontalMeasureColumns();
33420             return;
33421         }
33422         
33423         this.verticalMeasureColumns();
33424         
33425     },
33426     
33427     verticalMeasureColumns : function()
33428     {
33429         this.getContainerWidth();
33430         
33431 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33432 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33433 //            return;
33434 //        }
33435         
33436         var boxWidth = this.boxWidth + this.padWidth;
33437         
33438         if(this.containerWidth < this.boxWidth){
33439             boxWidth = this.containerWidth
33440         }
33441         
33442         var containerWidth = this.containerWidth;
33443         
33444         var cols = Math.floor(containerWidth / boxWidth);
33445         
33446         this.cols = Math.max( cols, 1 );
33447         
33448         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33449         
33450         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33451         
33452         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33453         
33454         this.colWidth = boxWidth + avail - this.padWidth;
33455         
33456         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33457         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33458     },
33459     
33460     horizontalMeasureColumns : function()
33461     {
33462         this.getContainerWidth();
33463         
33464         var boxWidth = this.boxWidth;
33465         
33466         if(this.containerWidth < boxWidth){
33467             boxWidth = this.containerWidth;
33468         }
33469         
33470         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33471         
33472         this.el.setHeight(boxWidth);
33473         
33474     },
33475     
33476     getContainerWidth : function()
33477     {
33478         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33479     },
33480     
33481     layoutItems : function( isInstant )
33482     {
33483         Roo.log(this.bricks);
33484         
33485         var items = Roo.apply([], this.bricks);
33486         
33487         if(this.isHorizontal){
33488             this._horizontalLayoutItems( items , isInstant );
33489             return;
33490         }
33491         
33492 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33493 //            this._verticalAlternativeLayoutItems( items , isInstant );
33494 //            return;
33495 //        }
33496         
33497         this._verticalLayoutItems( items , isInstant );
33498         
33499     },
33500     
33501     _verticalLayoutItems : function ( items , isInstant)
33502     {
33503         if ( !items || !items.length ) {
33504             return;
33505         }
33506         
33507         var standard = [
33508             ['xs', 'xs', 'xs', 'tall'],
33509             ['xs', 'xs', 'tall'],
33510             ['xs', 'xs', 'sm'],
33511             ['xs', 'xs', 'xs'],
33512             ['xs', 'tall'],
33513             ['xs', 'sm'],
33514             ['xs', 'xs'],
33515             ['xs'],
33516             
33517             ['sm', 'xs', 'xs'],
33518             ['sm', 'xs'],
33519             ['sm'],
33520             
33521             ['tall', 'xs', 'xs', 'xs'],
33522             ['tall', 'xs', 'xs'],
33523             ['tall', 'xs'],
33524             ['tall']
33525             
33526         ];
33527         
33528         var queue = [];
33529         
33530         var boxes = [];
33531         
33532         var box = [];
33533         
33534         Roo.each(items, function(item, k){
33535             
33536             switch (item.size) {
33537                 // these layouts take up a full box,
33538                 case 'md' :
33539                 case 'md-left' :
33540                 case 'md-right' :
33541                 case 'wide' :
33542                     
33543                     if(box.length){
33544                         boxes.push(box);
33545                         box = [];
33546                     }
33547                     
33548                     boxes.push([item]);
33549                     
33550                     break;
33551                     
33552                 case 'xs' :
33553                 case 'sm' :
33554                 case 'tall' :
33555                     
33556                     box.push(item);
33557                     
33558                     break;
33559                 default :
33560                     break;
33561                     
33562             }
33563             
33564         }, this);
33565         
33566         if(box.length){
33567             boxes.push(box);
33568             box = [];
33569         }
33570         
33571         var filterPattern = function(box, length)
33572         {
33573             if(!box.length){
33574                 return;
33575             }
33576             
33577             var match = false;
33578             
33579             var pattern = box.slice(0, length);
33580             
33581             var format = [];
33582             
33583             Roo.each(pattern, function(i){
33584                 format.push(i.size);
33585             }, this);
33586             
33587             Roo.each(standard, function(s){
33588                 
33589                 if(String(s) != String(format)){
33590                     return;
33591                 }
33592                 
33593                 match = true;
33594                 return false;
33595                 
33596             }, this);
33597             
33598             if(!match && length == 1){
33599                 return;
33600             }
33601             
33602             if(!match){
33603                 filterPattern(box, length - 1);
33604                 return;
33605             }
33606                 
33607             queue.push(pattern);
33608
33609             box = box.slice(length, box.length);
33610
33611             filterPattern(box, 4);
33612
33613             return;
33614             
33615         }
33616         
33617         Roo.each(boxes, function(box, k){
33618             
33619             if(!box.length){
33620                 return;
33621             }
33622             
33623             if(box.length == 1){
33624                 queue.push(box);
33625                 return;
33626             }
33627             
33628             filterPattern(box, 4);
33629             
33630         }, this);
33631         
33632         this._processVerticalLayoutQueue( queue, isInstant );
33633         
33634     },
33635     
33636 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33637 //    {
33638 //        if ( !items || !items.length ) {
33639 //            return;
33640 //        }
33641 //
33642 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33643 //        
33644 //    },
33645     
33646     _horizontalLayoutItems : function ( items , isInstant)
33647     {
33648         if ( !items || !items.length || items.length < 3) {
33649             return;
33650         }
33651         
33652         items.reverse();
33653         
33654         var eItems = items.slice(0, 3);
33655         
33656         items = items.slice(3, items.length);
33657         
33658         var standard = [
33659             ['xs', 'xs', 'xs', 'wide'],
33660             ['xs', 'xs', 'wide'],
33661             ['xs', 'xs', 'sm'],
33662             ['xs', 'xs', 'xs'],
33663             ['xs', 'wide'],
33664             ['xs', 'sm'],
33665             ['xs', 'xs'],
33666             ['xs'],
33667             
33668             ['sm', 'xs', 'xs'],
33669             ['sm', 'xs'],
33670             ['sm'],
33671             
33672             ['wide', 'xs', 'xs', 'xs'],
33673             ['wide', 'xs', 'xs'],
33674             ['wide', 'xs'],
33675             ['wide'],
33676             
33677             ['wide-thin']
33678         ];
33679         
33680         var queue = [];
33681         
33682         var boxes = [];
33683         
33684         var box = [];
33685         
33686         Roo.each(items, function(item, k){
33687             
33688             switch (item.size) {
33689                 case 'md' :
33690                 case 'md-left' :
33691                 case 'md-right' :
33692                 case 'tall' :
33693                     
33694                     if(box.length){
33695                         boxes.push(box);
33696                         box = [];
33697                     }
33698                     
33699                     boxes.push([item]);
33700                     
33701                     break;
33702                     
33703                 case 'xs' :
33704                 case 'sm' :
33705                 case 'wide' :
33706                 case 'wide-thin' :
33707                     
33708                     box.push(item);
33709                     
33710                     break;
33711                 default :
33712                     break;
33713                     
33714             }
33715             
33716         }, this);
33717         
33718         if(box.length){
33719             boxes.push(box);
33720             box = [];
33721         }
33722         
33723         var filterPattern = function(box, length)
33724         {
33725             if(!box.length){
33726                 return;
33727             }
33728             
33729             var match = false;
33730             
33731             var pattern = box.slice(0, length);
33732             
33733             var format = [];
33734             
33735             Roo.each(pattern, function(i){
33736                 format.push(i.size);
33737             }, this);
33738             
33739             Roo.each(standard, function(s){
33740                 
33741                 if(String(s) != String(format)){
33742                     return;
33743                 }
33744                 
33745                 match = true;
33746                 return false;
33747                 
33748             }, this);
33749             
33750             if(!match && length == 1){
33751                 return;
33752             }
33753             
33754             if(!match){
33755                 filterPattern(box, length - 1);
33756                 return;
33757             }
33758                 
33759             queue.push(pattern);
33760
33761             box = box.slice(length, box.length);
33762
33763             filterPattern(box, 4);
33764
33765             return;
33766             
33767         }
33768         
33769         Roo.each(boxes, function(box, k){
33770             
33771             if(!box.length){
33772                 return;
33773             }
33774             
33775             if(box.length == 1){
33776                 queue.push(box);
33777                 return;
33778             }
33779             
33780             filterPattern(box, 4);
33781             
33782         }, this);
33783         
33784         
33785         var prune = [];
33786         
33787         var pos = this.el.getBox(true);
33788         
33789         var minX = pos.x;
33790         
33791         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33792         
33793         var hit_end = false;
33794         
33795         Roo.each(queue, function(box){
33796             
33797             if(hit_end){
33798                 
33799                 Roo.each(box, function(b){
33800                 
33801                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33802                     b.el.hide();
33803
33804                 }, this);
33805
33806                 return;
33807             }
33808             
33809             var mx = 0;
33810             
33811             Roo.each(box, function(b){
33812                 
33813                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33814                 b.el.show();
33815
33816                 mx = Math.max(mx, b.x);
33817                 
33818             }, this);
33819             
33820             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33821             
33822             if(maxX < minX){
33823                 
33824                 Roo.each(box, function(b){
33825                 
33826                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33827                     b.el.hide();
33828                     
33829                 }, this);
33830                 
33831                 hit_end = true;
33832                 
33833                 return;
33834             }
33835             
33836             prune.push(box);
33837             
33838         }, this);
33839         
33840         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33841     },
33842     
33843     /** Sets position of item in DOM
33844     * @param {Element} item
33845     * @param {Number} x - horizontal position
33846     * @param {Number} y - vertical position
33847     * @param {Boolean} isInstant - disables transitions
33848     */
33849     _processVerticalLayoutQueue : function( queue, isInstant )
33850     {
33851         var pos = this.el.getBox(true);
33852         var x = pos.x;
33853         var y = pos.y;
33854         var maxY = [];
33855         
33856         for (var i = 0; i < this.cols; i++){
33857             maxY[i] = pos.y;
33858         }
33859         
33860         Roo.each(queue, function(box, k){
33861             
33862             var col = k % this.cols;
33863             
33864             Roo.each(box, function(b,kk){
33865                 
33866                 b.el.position('absolute');
33867                 
33868                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33869                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33870                 
33871                 if(b.size == 'md-left' || b.size == 'md-right'){
33872                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33873                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33874                 }
33875                 
33876                 b.el.setWidth(width);
33877                 b.el.setHeight(height);
33878                 // iframe?
33879                 b.el.select('iframe',true).setSize(width,height);
33880                 
33881             }, this);
33882             
33883             for (var i = 0; i < this.cols; i++){
33884                 
33885                 if(maxY[i] < maxY[col]){
33886                     col = i;
33887                     continue;
33888                 }
33889                 
33890                 col = Math.min(col, i);
33891                 
33892             }
33893             
33894             x = pos.x + col * (this.colWidth + this.padWidth);
33895             
33896             y = maxY[col];
33897             
33898             var positions = [];
33899             
33900             switch (box.length){
33901                 case 1 :
33902                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33903                     break;
33904                 case 2 :
33905                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33906                     break;
33907                 case 3 :
33908                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33909                     break;
33910                 case 4 :
33911                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33912                     break;
33913                 default :
33914                     break;
33915             }
33916             
33917             Roo.each(box, function(b,kk){
33918                 
33919                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33920                 
33921                 var sz = b.el.getSize();
33922                 
33923                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33924                 
33925             }, this);
33926             
33927         }, this);
33928         
33929         var mY = 0;
33930         
33931         for (var i = 0; i < this.cols; i++){
33932             mY = Math.max(mY, maxY[i]);
33933         }
33934         
33935         this.el.setHeight(mY - pos.y);
33936         
33937     },
33938     
33939 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33940 //    {
33941 //        var pos = this.el.getBox(true);
33942 //        var x = pos.x;
33943 //        var y = pos.y;
33944 //        var maxX = pos.right;
33945 //        
33946 //        var maxHeight = 0;
33947 //        
33948 //        Roo.each(items, function(item, k){
33949 //            
33950 //            var c = k % 2;
33951 //            
33952 //            item.el.position('absolute');
33953 //                
33954 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33955 //
33956 //            item.el.setWidth(width);
33957 //
33958 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33959 //
33960 //            item.el.setHeight(height);
33961 //            
33962 //            if(c == 0){
33963 //                item.el.setXY([x, y], isInstant ? false : true);
33964 //            } else {
33965 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33966 //            }
33967 //            
33968 //            y = y + height + this.alternativePadWidth;
33969 //            
33970 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33971 //            
33972 //        }, this);
33973 //        
33974 //        this.el.setHeight(maxHeight);
33975 //        
33976 //    },
33977     
33978     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33979     {
33980         var pos = this.el.getBox(true);
33981         
33982         var minX = pos.x;
33983         var minY = pos.y;
33984         
33985         var maxX = pos.right;
33986         
33987         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33988         
33989         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33990         
33991         Roo.each(queue, function(box, k){
33992             
33993             Roo.each(box, function(b, kk){
33994                 
33995                 b.el.position('absolute');
33996                 
33997                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33998                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33999                 
34000                 if(b.size == 'md-left' || b.size == 'md-right'){
34001                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34002                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34003                 }
34004                 
34005                 b.el.setWidth(width);
34006                 b.el.setHeight(height);
34007                 
34008             }, this);
34009             
34010             if(!box.length){
34011                 return;
34012             }
34013             
34014             var positions = [];
34015             
34016             switch (box.length){
34017                 case 1 :
34018                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34019                     break;
34020                 case 2 :
34021                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34022                     break;
34023                 case 3 :
34024                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34025                     break;
34026                 case 4 :
34027                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34028                     break;
34029                 default :
34030                     break;
34031             }
34032             
34033             Roo.each(box, function(b,kk){
34034                 
34035                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34036                 
34037                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34038                 
34039             }, this);
34040             
34041         }, this);
34042         
34043     },
34044     
34045     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34046     {
34047         Roo.each(eItems, function(b,k){
34048             
34049             b.size = (k == 0) ? 'sm' : 'xs';
34050             b.x = (k == 0) ? 2 : 1;
34051             b.y = (k == 0) ? 2 : 1;
34052             
34053             b.el.position('absolute');
34054             
34055             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34056                 
34057             b.el.setWidth(width);
34058             
34059             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34060             
34061             b.el.setHeight(height);
34062             
34063         }, this);
34064
34065         var positions = [];
34066         
34067         positions.push({
34068             x : maxX - this.unitWidth * 2 - this.gutter,
34069             y : minY
34070         });
34071         
34072         positions.push({
34073             x : maxX - this.unitWidth,
34074             y : minY + (this.unitWidth + this.gutter) * 2
34075         });
34076         
34077         positions.push({
34078             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34079             y : minY
34080         });
34081         
34082         Roo.each(eItems, function(b,k){
34083             
34084             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34085
34086         }, this);
34087         
34088     },
34089     
34090     getVerticalOneBoxColPositions : function(x, y, box)
34091     {
34092         var pos = [];
34093         
34094         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34095         
34096         if(box[0].size == 'md-left'){
34097             rand = 0;
34098         }
34099         
34100         if(box[0].size == 'md-right'){
34101             rand = 1;
34102         }
34103         
34104         pos.push({
34105             x : x + (this.unitWidth + this.gutter) * rand,
34106             y : y
34107         });
34108         
34109         return pos;
34110     },
34111     
34112     getVerticalTwoBoxColPositions : function(x, y, box)
34113     {
34114         var pos = [];
34115         
34116         if(box[0].size == 'xs'){
34117             
34118             pos.push({
34119                 x : x,
34120                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34121             });
34122
34123             pos.push({
34124                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34125                 y : y
34126             });
34127             
34128             return pos;
34129             
34130         }
34131         
34132         pos.push({
34133             x : x,
34134             y : y
34135         });
34136
34137         pos.push({
34138             x : x + (this.unitWidth + this.gutter) * 2,
34139             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34140         });
34141         
34142         return pos;
34143         
34144     },
34145     
34146     getVerticalThreeBoxColPositions : function(x, y, box)
34147     {
34148         var pos = [];
34149         
34150         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34151             
34152             pos.push({
34153                 x : x,
34154                 y : y
34155             });
34156
34157             pos.push({
34158                 x : x + (this.unitWidth + this.gutter) * 1,
34159                 y : y
34160             });
34161             
34162             pos.push({
34163                 x : x + (this.unitWidth + this.gutter) * 2,
34164                 y : y
34165             });
34166             
34167             return pos;
34168             
34169         }
34170         
34171         if(box[0].size == 'xs' && box[1].size == 'xs'){
34172             
34173             pos.push({
34174                 x : x,
34175                 y : y
34176             });
34177
34178             pos.push({
34179                 x : x,
34180                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34181             });
34182             
34183             pos.push({
34184                 x : x + (this.unitWidth + this.gutter) * 1,
34185                 y : y
34186             });
34187             
34188             return pos;
34189             
34190         }
34191         
34192         pos.push({
34193             x : x,
34194             y : y
34195         });
34196
34197         pos.push({
34198             x : x + (this.unitWidth + this.gutter) * 2,
34199             y : y
34200         });
34201
34202         pos.push({
34203             x : x + (this.unitWidth + this.gutter) * 2,
34204             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34205         });
34206             
34207         return pos;
34208         
34209     },
34210     
34211     getVerticalFourBoxColPositions : function(x, y, box)
34212     {
34213         var pos = [];
34214         
34215         if(box[0].size == 'xs'){
34216             
34217             pos.push({
34218                 x : x,
34219                 y : y
34220             });
34221
34222             pos.push({
34223                 x : x,
34224                 y : y + (this.unitHeight + this.gutter) * 1
34225             });
34226             
34227             pos.push({
34228                 x : x,
34229                 y : y + (this.unitHeight + this.gutter) * 2
34230             });
34231             
34232             pos.push({
34233                 x : x + (this.unitWidth + this.gutter) * 1,
34234                 y : y
34235             });
34236             
34237             return pos;
34238             
34239         }
34240         
34241         pos.push({
34242             x : x,
34243             y : y
34244         });
34245
34246         pos.push({
34247             x : x + (this.unitWidth + this.gutter) * 2,
34248             y : y
34249         });
34250
34251         pos.push({
34252             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34253             y : y + (this.unitHeight + this.gutter) * 1
34254         });
34255
34256         pos.push({
34257             x : x + (this.unitWidth + this.gutter) * 2,
34258             y : y + (this.unitWidth + this.gutter) * 2
34259         });
34260
34261         return pos;
34262         
34263     },
34264     
34265     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34266     {
34267         var pos = [];
34268         
34269         if(box[0].size == 'md-left'){
34270             pos.push({
34271                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34272                 y : minY
34273             });
34274             
34275             return pos;
34276         }
34277         
34278         if(box[0].size == 'md-right'){
34279             pos.push({
34280                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34281                 y : minY + (this.unitWidth + this.gutter) * 1
34282             });
34283             
34284             return pos;
34285         }
34286         
34287         var rand = Math.floor(Math.random() * (4 - box[0].y));
34288         
34289         pos.push({
34290             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34291             y : minY + (this.unitWidth + this.gutter) * rand
34292         });
34293         
34294         return pos;
34295         
34296     },
34297     
34298     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34299     {
34300         var pos = [];
34301         
34302         if(box[0].size == 'xs'){
34303             
34304             pos.push({
34305                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34306                 y : minY
34307             });
34308
34309             pos.push({
34310                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34311                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34312             });
34313             
34314             return pos;
34315             
34316         }
34317         
34318         pos.push({
34319             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34320             y : minY
34321         });
34322
34323         pos.push({
34324             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34325             y : minY + (this.unitWidth + this.gutter) * 2
34326         });
34327         
34328         return pos;
34329         
34330     },
34331     
34332     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34333     {
34334         var pos = [];
34335         
34336         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34337             
34338             pos.push({
34339                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34340                 y : minY
34341             });
34342
34343             pos.push({
34344                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34345                 y : minY + (this.unitWidth + this.gutter) * 1
34346             });
34347             
34348             pos.push({
34349                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34350                 y : minY + (this.unitWidth + this.gutter) * 2
34351             });
34352             
34353             return pos;
34354             
34355         }
34356         
34357         if(box[0].size == 'xs' && box[1].size == 'xs'){
34358             
34359             pos.push({
34360                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34361                 y : minY
34362             });
34363
34364             pos.push({
34365                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34366                 y : minY
34367             });
34368             
34369             pos.push({
34370                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34371                 y : minY + (this.unitWidth + this.gutter) * 1
34372             });
34373             
34374             return pos;
34375             
34376         }
34377         
34378         pos.push({
34379             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34380             y : minY
34381         });
34382
34383         pos.push({
34384             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34385             y : minY + (this.unitWidth + this.gutter) * 2
34386         });
34387
34388         pos.push({
34389             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34390             y : minY + (this.unitWidth + this.gutter) * 2
34391         });
34392             
34393         return pos;
34394         
34395     },
34396     
34397     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34398     {
34399         var pos = [];
34400         
34401         if(box[0].size == 'xs'){
34402             
34403             pos.push({
34404                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34405                 y : minY
34406             });
34407
34408             pos.push({
34409                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34410                 y : minY
34411             });
34412             
34413             pos.push({
34414                 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),
34415                 y : minY
34416             });
34417             
34418             pos.push({
34419                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34420                 y : minY + (this.unitWidth + this.gutter) * 1
34421             });
34422             
34423             return pos;
34424             
34425         }
34426         
34427         pos.push({
34428             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34429             y : minY
34430         });
34431         
34432         pos.push({
34433             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34434             y : minY + (this.unitWidth + this.gutter) * 2
34435         });
34436         
34437         pos.push({
34438             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34439             y : minY + (this.unitWidth + this.gutter) * 2
34440         });
34441         
34442         pos.push({
34443             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),
34444             y : minY + (this.unitWidth + this.gutter) * 2
34445         });
34446
34447         return pos;
34448         
34449     },
34450     
34451     /**
34452     * remove a Masonry Brick
34453     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34454     */
34455     removeBrick : function(brick_id)
34456     {
34457         if (!brick_id) {
34458             return;
34459         }
34460         
34461         for (var i = 0; i<this.bricks.length; i++) {
34462             if (this.bricks[i].id == brick_id) {
34463                 this.bricks.splice(i,1);
34464                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34465                 this.initial();
34466             }
34467         }
34468     },
34469     
34470     /**
34471     * adds a Masonry Brick
34472     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34473     */
34474     addBrick : function(cfg)
34475     {
34476         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34477         //this.register(cn);
34478         cn.parentId = this.id;
34479         cn.render(this.el);
34480         return cn;
34481     },
34482     
34483     /**
34484     * register a Masonry Brick
34485     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34486     */
34487     
34488     register : function(brick)
34489     {
34490         this.bricks.push(brick);
34491         brick.masonryId = this.id;
34492     },
34493     
34494     /**
34495     * clear all the Masonry Brick
34496     */
34497     clearAll : function()
34498     {
34499         this.bricks = [];
34500         //this.getChildContainer().dom.innerHTML = "";
34501         this.el.dom.innerHTML = '';
34502     },
34503     
34504     getSelected : function()
34505     {
34506         if (!this.selectedBrick) {
34507             return false;
34508         }
34509         
34510         return this.selectedBrick;
34511     }
34512 });
34513
34514 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34515     
34516     groups: {},
34517      /**
34518     * register a Masonry Layout
34519     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34520     */
34521     
34522     register : function(layout)
34523     {
34524         this.groups[layout.id] = layout;
34525     },
34526     /**
34527     * fetch a  Masonry Layout based on the masonry layout ID
34528     * @param {string} the masonry layout to add
34529     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34530     */
34531     
34532     get: function(layout_id) {
34533         if (typeof(this.groups[layout_id]) == 'undefined') {
34534             return false;
34535         }
34536         return this.groups[layout_id] ;
34537     }
34538     
34539     
34540     
34541 });
34542
34543  
34544
34545  /**
34546  *
34547  * This is based on 
34548  * http://masonry.desandro.com
34549  *
34550  * The idea is to render all the bricks based on vertical width...
34551  *
34552  * The original code extends 'outlayer' - we might need to use that....
34553  * 
34554  */
34555
34556
34557 /**
34558  * @class Roo.bootstrap.LayoutMasonryAuto
34559  * @extends Roo.bootstrap.Component
34560  * Bootstrap Layout Masonry class
34561  * 
34562  * @constructor
34563  * Create a new Element
34564  * @param {Object} config The config object
34565  */
34566
34567 Roo.bootstrap.LayoutMasonryAuto = function(config){
34568     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34569 };
34570
34571 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34572     
34573       /**
34574      * @cfg {Boolean} isFitWidth  - resize the width..
34575      */   
34576     isFitWidth : false,  // options..
34577     /**
34578      * @cfg {Boolean} isOriginLeft = left align?
34579      */   
34580     isOriginLeft : true,
34581     /**
34582      * @cfg {Boolean} isOriginTop = top align?
34583      */   
34584     isOriginTop : false,
34585     /**
34586      * @cfg {Boolean} isLayoutInstant = no animation?
34587      */   
34588     isLayoutInstant : false, // needed?
34589     /**
34590      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34591      */   
34592     isResizingContainer : true,
34593     /**
34594      * @cfg {Number} columnWidth  width of the columns 
34595      */   
34596     
34597     columnWidth : 0,
34598     
34599     /**
34600      * @cfg {Number} maxCols maximum number of columns
34601      */   
34602     
34603     maxCols: 0,
34604     /**
34605      * @cfg {Number} padHeight padding below box..
34606      */   
34607     
34608     padHeight : 10, 
34609     
34610     /**
34611      * @cfg {Boolean} isAutoInitial defalut true
34612      */   
34613     
34614     isAutoInitial : true, 
34615     
34616     // private?
34617     gutter : 0,
34618     
34619     containerWidth: 0,
34620     initialColumnWidth : 0,
34621     currentSize : null,
34622     
34623     colYs : null, // array.
34624     maxY : 0,
34625     padWidth: 10,
34626     
34627     
34628     tag: 'div',
34629     cls: '',
34630     bricks: null, //CompositeElement
34631     cols : 0, // array?
34632     // element : null, // wrapped now this.el
34633     _isLayoutInited : null, 
34634     
34635     
34636     getAutoCreate : function(){
34637         
34638         var cfg = {
34639             tag: this.tag,
34640             cls: 'blog-masonary-wrapper ' + this.cls,
34641             cn : {
34642                 cls : 'mas-boxes masonary'
34643             }
34644         };
34645         
34646         return cfg;
34647     },
34648     
34649     getChildContainer: function( )
34650     {
34651         if (this.boxesEl) {
34652             return this.boxesEl;
34653         }
34654         
34655         this.boxesEl = this.el.select('.mas-boxes').first();
34656         
34657         return this.boxesEl;
34658     },
34659     
34660     
34661     initEvents : function()
34662     {
34663         var _this = this;
34664         
34665         if(this.isAutoInitial){
34666             Roo.log('hook children rendered');
34667             this.on('childrenrendered', function() {
34668                 Roo.log('children rendered');
34669                 _this.initial();
34670             } ,this);
34671         }
34672         
34673     },
34674     
34675     initial : function()
34676     {
34677         this.reloadItems();
34678
34679         this.currentSize = this.el.getBox(true);
34680
34681         /// was window resize... - let's see if this works..
34682         Roo.EventManager.onWindowResize(this.resize, this); 
34683
34684         if(!this.isAutoInitial){
34685             this.layout();
34686             return;
34687         }
34688         
34689         this.layout.defer(500,this);
34690     },
34691     
34692     reloadItems: function()
34693     {
34694         this.bricks = this.el.select('.masonry-brick', true);
34695         
34696         this.bricks.each(function(b) {
34697             //Roo.log(b.getSize());
34698             if (!b.attr('originalwidth')) {
34699                 b.attr('originalwidth',  b.getSize().width);
34700             }
34701             
34702         });
34703         
34704         Roo.log(this.bricks.elements.length);
34705     },
34706     
34707     resize : function()
34708     {
34709         Roo.log('resize');
34710         var cs = this.el.getBox(true);
34711         
34712         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34713             Roo.log("no change in with or X");
34714             return;
34715         }
34716         this.currentSize = cs;
34717         this.layout();
34718     },
34719     
34720     layout : function()
34721     {
34722          Roo.log('layout');
34723         this._resetLayout();
34724         //this._manageStamps();
34725       
34726         // don't animate first layout
34727         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34728         this.layoutItems( isInstant );
34729       
34730         // flag for initalized
34731         this._isLayoutInited = true;
34732     },
34733     
34734     layoutItems : function( isInstant )
34735     {
34736         //var items = this._getItemsForLayout( this.items );
34737         // original code supports filtering layout items.. we just ignore it..
34738         
34739         this._layoutItems( this.bricks , isInstant );
34740       
34741         this._postLayout();
34742     },
34743     _layoutItems : function ( items , isInstant)
34744     {
34745        //this.fireEvent( 'layout', this, items );
34746     
34747
34748         if ( !items || !items.elements.length ) {
34749           // no items, emit event with empty array
34750             return;
34751         }
34752
34753         var queue = [];
34754         items.each(function(item) {
34755             Roo.log("layout item");
34756             Roo.log(item);
34757             // get x/y object from method
34758             var position = this._getItemLayoutPosition( item );
34759             // enqueue
34760             position.item = item;
34761             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34762             queue.push( position );
34763         }, this);
34764       
34765         this._processLayoutQueue( queue );
34766     },
34767     /** Sets position of item in DOM
34768     * @param {Element} item
34769     * @param {Number} x - horizontal position
34770     * @param {Number} y - vertical position
34771     * @param {Boolean} isInstant - disables transitions
34772     */
34773     _processLayoutQueue : function( queue )
34774     {
34775         for ( var i=0, len = queue.length; i < len; i++ ) {
34776             var obj = queue[i];
34777             obj.item.position('absolute');
34778             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34779         }
34780     },
34781       
34782     
34783     /**
34784     * Any logic you want to do after each layout,
34785     * i.e. size the container
34786     */
34787     _postLayout : function()
34788     {
34789         this.resizeContainer();
34790     },
34791     
34792     resizeContainer : function()
34793     {
34794         if ( !this.isResizingContainer ) {
34795             return;
34796         }
34797         var size = this._getContainerSize();
34798         if ( size ) {
34799             this.el.setSize(size.width,size.height);
34800             this.boxesEl.setSize(size.width,size.height);
34801         }
34802     },
34803     
34804     
34805     
34806     _resetLayout : function()
34807     {
34808         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34809         this.colWidth = this.el.getWidth();
34810         //this.gutter = this.el.getWidth(); 
34811         
34812         this.measureColumns();
34813
34814         // reset column Y
34815         var i = this.cols;
34816         this.colYs = [];
34817         while (i--) {
34818             this.colYs.push( 0 );
34819         }
34820     
34821         this.maxY = 0;
34822     },
34823
34824     measureColumns : function()
34825     {
34826         this.getContainerWidth();
34827       // if columnWidth is 0, default to outerWidth of first item
34828         if ( !this.columnWidth ) {
34829             var firstItem = this.bricks.first();
34830             Roo.log(firstItem);
34831             this.columnWidth  = this.containerWidth;
34832             if (firstItem && firstItem.attr('originalwidth') ) {
34833                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34834             }
34835             // columnWidth fall back to item of first element
34836             Roo.log("set column width?");
34837                         this.initialColumnWidth = this.columnWidth  ;
34838
34839             // if first elem has no width, default to size of container
34840             
34841         }
34842         
34843         
34844         if (this.initialColumnWidth) {
34845             this.columnWidth = this.initialColumnWidth;
34846         }
34847         
34848         
34849             
34850         // column width is fixed at the top - however if container width get's smaller we should
34851         // reduce it...
34852         
34853         // this bit calcs how man columns..
34854             
34855         var columnWidth = this.columnWidth += this.gutter;
34856       
34857         // calculate columns
34858         var containerWidth = this.containerWidth + this.gutter;
34859         
34860         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34861         // fix rounding errors, typically with gutters
34862         var excess = columnWidth - containerWidth % columnWidth;
34863         
34864         
34865         // if overshoot is less than a pixel, round up, otherwise floor it
34866         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34867         cols = Math[ mathMethod ]( cols );
34868         this.cols = Math.max( cols, 1 );
34869         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34870         
34871          // padding positioning..
34872         var totalColWidth = this.cols * this.columnWidth;
34873         var padavail = this.containerWidth - totalColWidth;
34874         // so for 2 columns - we need 3 'pads'
34875         
34876         var padNeeded = (1+this.cols) * this.padWidth;
34877         
34878         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34879         
34880         this.columnWidth += padExtra
34881         //this.padWidth = Math.floor(padavail /  ( this.cols));
34882         
34883         // adjust colum width so that padding is fixed??
34884         
34885         // we have 3 columns ... total = width * 3
34886         // we have X left over... that should be used by 
34887         
34888         //if (this.expandC) {
34889             
34890         //}
34891         
34892         
34893         
34894     },
34895     
34896     getContainerWidth : function()
34897     {
34898        /* // container is parent if fit width
34899         var container = this.isFitWidth ? this.element.parentNode : this.element;
34900         // check that this.size and size are there
34901         // IE8 triggers resize on body size change, so they might not be
34902         
34903         var size = getSize( container );  //FIXME
34904         this.containerWidth = size && size.innerWidth; //FIXME
34905         */
34906          
34907         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34908         
34909     },
34910     
34911     _getItemLayoutPosition : function( item )  // what is item?
34912     {
34913         // we resize the item to our columnWidth..
34914       
34915         item.setWidth(this.columnWidth);
34916         item.autoBoxAdjust  = false;
34917         
34918         var sz = item.getSize();
34919  
34920         // how many columns does this brick span
34921         var remainder = this.containerWidth % this.columnWidth;
34922         
34923         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34924         // round if off by 1 pixel, otherwise use ceil
34925         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34926         colSpan = Math.min( colSpan, this.cols );
34927         
34928         // normally this should be '1' as we dont' currently allow multi width columns..
34929         
34930         var colGroup = this._getColGroup( colSpan );
34931         // get the minimum Y value from the columns
34932         var minimumY = Math.min.apply( Math, colGroup );
34933         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34934         
34935         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34936          
34937         // position the brick
34938         var position = {
34939             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34940             y: this.currentSize.y + minimumY + this.padHeight
34941         };
34942         
34943         Roo.log(position);
34944         // apply setHeight to necessary columns
34945         var setHeight = minimumY + sz.height + this.padHeight;
34946         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34947         
34948         var setSpan = this.cols + 1 - colGroup.length;
34949         for ( var i = 0; i < setSpan; i++ ) {
34950           this.colYs[ shortColIndex + i ] = setHeight ;
34951         }
34952       
34953         return position;
34954     },
34955     
34956     /**
34957      * @param {Number} colSpan - number of columns the element spans
34958      * @returns {Array} colGroup
34959      */
34960     _getColGroup : function( colSpan )
34961     {
34962         if ( colSpan < 2 ) {
34963           // if brick spans only one column, use all the column Ys
34964           return this.colYs;
34965         }
34966       
34967         var colGroup = [];
34968         // how many different places could this brick fit horizontally
34969         var groupCount = this.cols + 1 - colSpan;
34970         // for each group potential horizontal position
34971         for ( var i = 0; i < groupCount; i++ ) {
34972           // make an array of colY values for that one group
34973           var groupColYs = this.colYs.slice( i, i + colSpan );
34974           // and get the max value of the array
34975           colGroup[i] = Math.max.apply( Math, groupColYs );
34976         }
34977         return colGroup;
34978     },
34979     /*
34980     _manageStamp : function( stamp )
34981     {
34982         var stampSize =  stamp.getSize();
34983         var offset = stamp.getBox();
34984         // get the columns that this stamp affects
34985         var firstX = this.isOriginLeft ? offset.x : offset.right;
34986         var lastX = firstX + stampSize.width;
34987         var firstCol = Math.floor( firstX / this.columnWidth );
34988         firstCol = Math.max( 0, firstCol );
34989         
34990         var lastCol = Math.floor( lastX / this.columnWidth );
34991         // lastCol should not go over if multiple of columnWidth #425
34992         lastCol -= lastX % this.columnWidth ? 0 : 1;
34993         lastCol = Math.min( this.cols - 1, lastCol );
34994         
34995         // set colYs to bottom of the stamp
34996         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34997             stampSize.height;
34998             
34999         for ( var i = firstCol; i <= lastCol; i++ ) {
35000           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35001         }
35002     },
35003     */
35004     
35005     _getContainerSize : function()
35006     {
35007         this.maxY = Math.max.apply( Math, this.colYs );
35008         var size = {
35009             height: this.maxY
35010         };
35011       
35012         if ( this.isFitWidth ) {
35013             size.width = this._getContainerFitWidth();
35014         }
35015       
35016         return size;
35017     },
35018     
35019     _getContainerFitWidth : function()
35020     {
35021         var unusedCols = 0;
35022         // count unused columns
35023         var i = this.cols;
35024         while ( --i ) {
35025           if ( this.colYs[i] !== 0 ) {
35026             break;
35027           }
35028           unusedCols++;
35029         }
35030         // fit container to columns that have been used
35031         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35032     },
35033     
35034     needsResizeLayout : function()
35035     {
35036         var previousWidth = this.containerWidth;
35037         this.getContainerWidth();
35038         return previousWidth !== this.containerWidth;
35039     }
35040  
35041 });
35042
35043  
35044
35045  /*
35046  * - LGPL
35047  *
35048  * element
35049  * 
35050  */
35051
35052 /**
35053  * @class Roo.bootstrap.MasonryBrick
35054  * @extends Roo.bootstrap.Component
35055  * Bootstrap MasonryBrick class
35056  * 
35057  * @constructor
35058  * Create a new MasonryBrick
35059  * @param {Object} config The config object
35060  */
35061
35062 Roo.bootstrap.MasonryBrick = function(config){
35063     
35064     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35065     
35066     Roo.bootstrap.MasonryBrick.register(this);
35067     
35068     this.addEvents({
35069         // raw events
35070         /**
35071          * @event click
35072          * When a MasonryBrick is clcik
35073          * @param {Roo.bootstrap.MasonryBrick} this
35074          * @param {Roo.EventObject} e
35075          */
35076         "click" : true
35077     });
35078 };
35079
35080 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35081     
35082     /**
35083      * @cfg {String} title
35084      */   
35085     title : '',
35086     /**
35087      * @cfg {String} html
35088      */   
35089     html : '',
35090     /**
35091      * @cfg {String} bgimage
35092      */   
35093     bgimage : '',
35094     /**
35095      * @cfg {String} videourl
35096      */   
35097     videourl : '',
35098     /**
35099      * @cfg {String} cls
35100      */   
35101     cls : '',
35102     /**
35103      * @cfg {String} href
35104      */   
35105     href : '',
35106     /**
35107      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35108      */   
35109     size : 'xs',
35110     
35111     /**
35112      * @cfg {String} placetitle (center|bottom)
35113      */   
35114     placetitle : '',
35115     
35116     /**
35117      * @cfg {Boolean} isFitContainer defalut true
35118      */   
35119     isFitContainer : true, 
35120     
35121     /**
35122      * @cfg {Boolean} preventDefault defalut false
35123      */   
35124     preventDefault : false, 
35125     
35126     /**
35127      * @cfg {Boolean} inverse defalut false
35128      */   
35129     maskInverse : false, 
35130     
35131     getAutoCreate : function()
35132     {
35133         if(!this.isFitContainer){
35134             return this.getSplitAutoCreate();
35135         }
35136         
35137         var cls = 'masonry-brick masonry-brick-full';
35138         
35139         if(this.href.length){
35140             cls += ' masonry-brick-link';
35141         }
35142         
35143         if(this.bgimage.length){
35144             cls += ' masonry-brick-image';
35145         }
35146         
35147         if(this.maskInverse){
35148             cls += ' mask-inverse';
35149         }
35150         
35151         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35152             cls += ' enable-mask';
35153         }
35154         
35155         if(this.size){
35156             cls += ' masonry-' + this.size + '-brick';
35157         }
35158         
35159         if(this.placetitle.length){
35160             
35161             switch (this.placetitle) {
35162                 case 'center' :
35163                     cls += ' masonry-center-title';
35164                     break;
35165                 case 'bottom' :
35166                     cls += ' masonry-bottom-title';
35167                     break;
35168                 default:
35169                     break;
35170             }
35171             
35172         } else {
35173             if(!this.html.length && !this.bgimage.length){
35174                 cls += ' masonry-center-title';
35175             }
35176
35177             if(!this.html.length && this.bgimage.length){
35178                 cls += ' masonry-bottom-title';
35179             }
35180         }
35181         
35182         if(this.cls){
35183             cls += ' ' + this.cls;
35184         }
35185         
35186         var cfg = {
35187             tag: (this.href.length) ? 'a' : 'div',
35188             cls: cls,
35189             cn: [
35190                 {
35191                     tag: 'div',
35192                     cls: 'masonry-brick-mask'
35193                 },
35194                 {
35195                     tag: 'div',
35196                     cls: 'masonry-brick-paragraph',
35197                     cn: []
35198                 }
35199             ]
35200         };
35201         
35202         if(this.href.length){
35203             cfg.href = this.href;
35204         }
35205         
35206         var cn = cfg.cn[1].cn;
35207         
35208         if(this.title.length){
35209             cn.push({
35210                 tag: 'h4',
35211                 cls: 'masonry-brick-title',
35212                 html: this.title
35213             });
35214         }
35215         
35216         if(this.html.length){
35217             cn.push({
35218                 tag: 'p',
35219                 cls: 'masonry-brick-text',
35220                 html: this.html
35221             });
35222         }
35223         
35224         if (!this.title.length && !this.html.length) {
35225             cfg.cn[1].cls += ' hide';
35226         }
35227         
35228         if(this.bgimage.length){
35229             cfg.cn.push({
35230                 tag: 'img',
35231                 cls: 'masonry-brick-image-view',
35232                 src: this.bgimage
35233             });
35234         }
35235         
35236         if(this.videourl.length){
35237             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35238             // youtube support only?
35239             cfg.cn.push({
35240                 tag: 'iframe',
35241                 cls: 'masonry-brick-image-view',
35242                 src: vurl,
35243                 frameborder : 0,
35244                 allowfullscreen : true
35245             });
35246         }
35247         
35248         return cfg;
35249         
35250     },
35251     
35252     getSplitAutoCreate : function()
35253     {
35254         var cls = 'masonry-brick masonry-brick-split';
35255         
35256         if(this.href.length){
35257             cls += ' masonry-brick-link';
35258         }
35259         
35260         if(this.bgimage.length){
35261             cls += ' masonry-brick-image';
35262         }
35263         
35264         if(this.size){
35265             cls += ' masonry-' + this.size + '-brick';
35266         }
35267         
35268         switch (this.placetitle) {
35269             case 'center' :
35270                 cls += ' masonry-center-title';
35271                 break;
35272             case 'bottom' :
35273                 cls += ' masonry-bottom-title';
35274                 break;
35275             default:
35276                 if(!this.bgimage.length){
35277                     cls += ' masonry-center-title';
35278                 }
35279
35280                 if(this.bgimage.length){
35281                     cls += ' masonry-bottom-title';
35282                 }
35283                 break;
35284         }
35285         
35286         if(this.cls){
35287             cls += ' ' + this.cls;
35288         }
35289         
35290         var cfg = {
35291             tag: (this.href.length) ? 'a' : 'div',
35292             cls: cls,
35293             cn: [
35294                 {
35295                     tag: 'div',
35296                     cls: 'masonry-brick-split-head',
35297                     cn: [
35298                         {
35299                             tag: 'div',
35300                             cls: 'masonry-brick-paragraph',
35301                             cn: []
35302                         }
35303                     ]
35304                 },
35305                 {
35306                     tag: 'div',
35307                     cls: 'masonry-brick-split-body',
35308                     cn: []
35309                 }
35310             ]
35311         };
35312         
35313         if(this.href.length){
35314             cfg.href = this.href;
35315         }
35316         
35317         if(this.title.length){
35318             cfg.cn[0].cn[0].cn.push({
35319                 tag: 'h4',
35320                 cls: 'masonry-brick-title',
35321                 html: this.title
35322             });
35323         }
35324         
35325         if(this.html.length){
35326             cfg.cn[1].cn.push({
35327                 tag: 'p',
35328                 cls: 'masonry-brick-text',
35329                 html: this.html
35330             });
35331         }
35332
35333         if(this.bgimage.length){
35334             cfg.cn[0].cn.push({
35335                 tag: 'img',
35336                 cls: 'masonry-brick-image-view',
35337                 src: this.bgimage
35338             });
35339         }
35340         
35341         if(this.videourl.length){
35342             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35343             // youtube support only?
35344             cfg.cn[0].cn.cn.push({
35345                 tag: 'iframe',
35346                 cls: 'masonry-brick-image-view',
35347                 src: vurl,
35348                 frameborder : 0,
35349                 allowfullscreen : true
35350             });
35351         }
35352         
35353         return cfg;
35354     },
35355     
35356     initEvents: function() 
35357     {
35358         switch (this.size) {
35359             case 'xs' :
35360                 this.x = 1;
35361                 this.y = 1;
35362                 break;
35363             case 'sm' :
35364                 this.x = 2;
35365                 this.y = 2;
35366                 break;
35367             case 'md' :
35368             case 'md-left' :
35369             case 'md-right' :
35370                 this.x = 3;
35371                 this.y = 3;
35372                 break;
35373             case 'tall' :
35374                 this.x = 2;
35375                 this.y = 3;
35376                 break;
35377             case 'wide' :
35378                 this.x = 3;
35379                 this.y = 2;
35380                 break;
35381             case 'wide-thin' :
35382                 this.x = 3;
35383                 this.y = 1;
35384                 break;
35385                         
35386             default :
35387                 break;
35388         }
35389         
35390         if(Roo.isTouch){
35391             this.el.on('touchstart', this.onTouchStart, this);
35392             this.el.on('touchmove', this.onTouchMove, this);
35393             this.el.on('touchend', this.onTouchEnd, this);
35394             this.el.on('contextmenu', this.onContextMenu, this);
35395         } else {
35396             this.el.on('mouseenter'  ,this.enter, this);
35397             this.el.on('mouseleave', this.leave, this);
35398             this.el.on('click', this.onClick, this);
35399         }
35400         
35401         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35402             this.parent().bricks.push(this);   
35403         }
35404         
35405     },
35406     
35407     onClick: function(e, el)
35408     {
35409         var time = this.endTimer - this.startTimer;
35410         // Roo.log(e.preventDefault());
35411         if(Roo.isTouch){
35412             if(time > 1000){
35413                 e.preventDefault();
35414                 return;
35415             }
35416         }
35417         
35418         if(!this.preventDefault){
35419             return;
35420         }
35421         
35422         e.preventDefault();
35423         
35424         if (this.activeClass != '') {
35425             this.selectBrick();
35426         }
35427         
35428         this.fireEvent('click', this, e);
35429     },
35430     
35431     enter: function(e, el)
35432     {
35433         e.preventDefault();
35434         
35435         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35436             return;
35437         }
35438         
35439         if(this.bgimage.length && this.html.length){
35440             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35441         }
35442     },
35443     
35444     leave: function(e, el)
35445     {
35446         e.preventDefault();
35447         
35448         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35449             return;
35450         }
35451         
35452         if(this.bgimage.length && this.html.length){
35453             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35454         }
35455     },
35456     
35457     onTouchStart: function(e, el)
35458     {
35459 //        e.preventDefault();
35460         
35461         this.touchmoved = false;
35462         
35463         if(!this.isFitContainer){
35464             return;
35465         }
35466         
35467         if(!this.bgimage.length || !this.html.length){
35468             return;
35469         }
35470         
35471         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35472         
35473         this.timer = new Date().getTime();
35474         
35475     },
35476     
35477     onTouchMove: function(e, el)
35478     {
35479         this.touchmoved = true;
35480     },
35481     
35482     onContextMenu : function(e,el)
35483     {
35484         e.preventDefault();
35485         e.stopPropagation();
35486         return false;
35487     },
35488     
35489     onTouchEnd: function(e, el)
35490     {
35491 //        e.preventDefault();
35492         
35493         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35494         
35495             this.leave(e,el);
35496             
35497             return;
35498         }
35499         
35500         if(!this.bgimage.length || !this.html.length){
35501             
35502             if(this.href.length){
35503                 window.location.href = this.href;
35504             }
35505             
35506             return;
35507         }
35508         
35509         if(!this.isFitContainer){
35510             return;
35511         }
35512         
35513         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35514         
35515         window.location.href = this.href;
35516     },
35517     
35518     //selection on single brick only
35519     selectBrick : function() {
35520         
35521         if (!this.parentId) {
35522             return;
35523         }
35524         
35525         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35526         var index = m.selectedBrick.indexOf(this.id);
35527         
35528         if ( index > -1) {
35529             m.selectedBrick.splice(index,1);
35530             this.el.removeClass(this.activeClass);
35531             return;
35532         }
35533         
35534         for(var i = 0; i < m.selectedBrick.length; i++) {
35535             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35536             b.el.removeClass(b.activeClass);
35537         }
35538         
35539         m.selectedBrick = [];
35540         
35541         m.selectedBrick.push(this.id);
35542         this.el.addClass(this.activeClass);
35543         return;
35544     },
35545     
35546     isSelected : function(){
35547         return this.el.hasClass(this.activeClass);
35548         
35549     }
35550 });
35551
35552 Roo.apply(Roo.bootstrap.MasonryBrick, {
35553     
35554     //groups: {},
35555     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35556      /**
35557     * register a Masonry Brick
35558     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35559     */
35560     
35561     register : function(brick)
35562     {
35563         //this.groups[brick.id] = brick;
35564         this.groups.add(brick.id, brick);
35565     },
35566     /**
35567     * fetch a  masonry brick based on the masonry brick ID
35568     * @param {string} the masonry brick to add
35569     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35570     */
35571     
35572     get: function(brick_id) 
35573     {
35574         // if (typeof(this.groups[brick_id]) == 'undefined') {
35575         //     return false;
35576         // }
35577         // return this.groups[brick_id] ;
35578         
35579         if(this.groups.key(brick_id)) {
35580             return this.groups.key(brick_id);
35581         }
35582         
35583         return false;
35584     }
35585     
35586     
35587     
35588 });
35589
35590  /*
35591  * - LGPL
35592  *
35593  * element
35594  * 
35595  */
35596
35597 /**
35598  * @class Roo.bootstrap.Brick
35599  * @extends Roo.bootstrap.Component
35600  * Bootstrap Brick class
35601  * 
35602  * @constructor
35603  * Create a new Brick
35604  * @param {Object} config The config object
35605  */
35606
35607 Roo.bootstrap.Brick = function(config){
35608     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35609     
35610     this.addEvents({
35611         // raw events
35612         /**
35613          * @event click
35614          * When a Brick is click
35615          * @param {Roo.bootstrap.Brick} this
35616          * @param {Roo.EventObject} e
35617          */
35618         "click" : true
35619     });
35620 };
35621
35622 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35623     
35624     /**
35625      * @cfg {String} title
35626      */   
35627     title : '',
35628     /**
35629      * @cfg {String} html
35630      */   
35631     html : '',
35632     /**
35633      * @cfg {String} bgimage
35634      */   
35635     bgimage : '',
35636     /**
35637      * @cfg {String} cls
35638      */   
35639     cls : '',
35640     /**
35641      * @cfg {String} href
35642      */   
35643     href : '',
35644     /**
35645      * @cfg {String} video
35646      */   
35647     video : '',
35648     /**
35649      * @cfg {Boolean} square
35650      */   
35651     square : true,
35652     
35653     getAutoCreate : function()
35654     {
35655         var cls = 'roo-brick';
35656         
35657         if(this.href.length){
35658             cls += ' roo-brick-link';
35659         }
35660         
35661         if(this.bgimage.length){
35662             cls += ' roo-brick-image';
35663         }
35664         
35665         if(!this.html.length && !this.bgimage.length){
35666             cls += ' roo-brick-center-title';
35667         }
35668         
35669         if(!this.html.length && this.bgimage.length){
35670             cls += ' roo-brick-bottom-title';
35671         }
35672         
35673         if(this.cls){
35674             cls += ' ' + this.cls;
35675         }
35676         
35677         var cfg = {
35678             tag: (this.href.length) ? 'a' : 'div',
35679             cls: cls,
35680             cn: [
35681                 {
35682                     tag: 'div',
35683                     cls: 'roo-brick-paragraph',
35684                     cn: []
35685                 }
35686             ]
35687         };
35688         
35689         if(this.href.length){
35690             cfg.href = this.href;
35691         }
35692         
35693         var cn = cfg.cn[0].cn;
35694         
35695         if(this.title.length){
35696             cn.push({
35697                 tag: 'h4',
35698                 cls: 'roo-brick-title',
35699                 html: this.title
35700             });
35701         }
35702         
35703         if(this.html.length){
35704             cn.push({
35705                 tag: 'p',
35706                 cls: 'roo-brick-text',
35707                 html: this.html
35708             });
35709         } else {
35710             cn.cls += ' hide';
35711         }
35712         
35713         if(this.bgimage.length){
35714             cfg.cn.push({
35715                 tag: 'img',
35716                 cls: 'roo-brick-image-view',
35717                 src: this.bgimage
35718             });
35719         }
35720         
35721         return cfg;
35722     },
35723     
35724     initEvents: function() 
35725     {
35726         if(this.title.length || this.html.length){
35727             this.el.on('mouseenter'  ,this.enter, this);
35728             this.el.on('mouseleave', this.leave, this);
35729         }
35730         
35731         Roo.EventManager.onWindowResize(this.resize, this); 
35732         
35733         if(this.bgimage.length){
35734             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35735             this.imageEl.on('load', this.onImageLoad, this);
35736             return;
35737         }
35738         
35739         this.resize();
35740     },
35741     
35742     onImageLoad : function()
35743     {
35744         this.resize();
35745     },
35746     
35747     resize : function()
35748     {
35749         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35750         
35751         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35752         
35753         if(this.bgimage.length){
35754             var image = this.el.select('.roo-brick-image-view', true).first();
35755             
35756             image.setWidth(paragraph.getWidth());
35757             
35758             if(this.square){
35759                 image.setHeight(paragraph.getWidth());
35760             }
35761             
35762             this.el.setHeight(image.getHeight());
35763             paragraph.setHeight(image.getHeight());
35764             
35765         }
35766         
35767     },
35768     
35769     enter: function(e, el)
35770     {
35771         e.preventDefault();
35772         
35773         if(this.bgimage.length){
35774             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35775             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35776         }
35777     },
35778     
35779     leave: function(e, el)
35780     {
35781         e.preventDefault();
35782         
35783         if(this.bgimage.length){
35784             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35785             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35786         }
35787     }
35788     
35789 });
35790
35791  
35792
35793  /*
35794  * - LGPL
35795  *
35796  * Number field 
35797  */
35798
35799 /**
35800  * @class Roo.bootstrap.NumberField
35801  * @extends Roo.bootstrap.Input
35802  * Bootstrap NumberField class
35803  * 
35804  * 
35805  * 
35806  * 
35807  * @constructor
35808  * Create a new NumberField
35809  * @param {Object} config The config object
35810  */
35811
35812 Roo.bootstrap.NumberField = function(config){
35813     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35814 };
35815
35816 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35817     
35818     /**
35819      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35820      */
35821     allowDecimals : true,
35822     /**
35823      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35824      */
35825     decimalSeparator : ".",
35826     /**
35827      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35828      */
35829     decimalPrecision : 2,
35830     /**
35831      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35832      */
35833     allowNegative : true,
35834     
35835     /**
35836      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35837      */
35838     allowZero: true,
35839     /**
35840      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35841      */
35842     minValue : Number.NEGATIVE_INFINITY,
35843     /**
35844      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35845      */
35846     maxValue : Number.MAX_VALUE,
35847     /**
35848      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35849      */
35850     minText : "The minimum value for this field is {0}",
35851     /**
35852      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35853      */
35854     maxText : "The maximum value for this field is {0}",
35855     /**
35856      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35857      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35858      */
35859     nanText : "{0} is not a valid number",
35860     /**
35861      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35862      */
35863     thousandsDelimiter : false,
35864     /**
35865      * @cfg {String} valueAlign alignment of value
35866      */
35867     valueAlign : "left",
35868
35869     getAutoCreate : function()
35870     {
35871         var hiddenInput = {
35872             tag: 'input',
35873             type: 'hidden',
35874             id: Roo.id(),
35875             cls: 'hidden-number-input'
35876         };
35877         
35878         if (this.name) {
35879             hiddenInput.name = this.name;
35880         }
35881         
35882         this.name = '';
35883         
35884         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35885         
35886         this.name = hiddenInput.name;
35887         
35888         if(cfg.cn.length > 0) {
35889             cfg.cn.push(hiddenInput);
35890         }
35891         
35892         return cfg;
35893     },
35894
35895     // private
35896     initEvents : function()
35897     {   
35898         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35899         
35900         var allowed = "0123456789";
35901         
35902         if(this.allowDecimals){
35903             allowed += this.decimalSeparator;
35904         }
35905         
35906         if(this.allowNegative){
35907             allowed += "-";
35908         }
35909         
35910         if(this.thousandsDelimiter) {
35911             allowed += ",";
35912         }
35913         
35914         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35915         
35916         var keyPress = function(e){
35917             
35918             var k = e.getKey();
35919             
35920             var c = e.getCharCode();
35921             
35922             if(
35923                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35924                     allowed.indexOf(String.fromCharCode(c)) === -1
35925             ){
35926                 e.stopEvent();
35927                 return;
35928             }
35929             
35930             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35931                 return;
35932             }
35933             
35934             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35935                 e.stopEvent();
35936             }
35937         };
35938         
35939         this.el.on("keypress", keyPress, this);
35940     },
35941     
35942     validateValue : function(value)
35943     {
35944         
35945         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35946             return false;
35947         }
35948         
35949         var num = this.parseValue(value);
35950         
35951         if(isNaN(num)){
35952             this.markInvalid(String.format(this.nanText, value));
35953             return false;
35954         }
35955         
35956         if(num < this.minValue){
35957             this.markInvalid(String.format(this.minText, this.minValue));
35958             return false;
35959         }
35960         
35961         if(num > this.maxValue){
35962             this.markInvalid(String.format(this.maxText, this.maxValue));
35963             return false;
35964         }
35965         
35966         return true;
35967     },
35968
35969     getValue : function()
35970     {
35971         var v = this.hiddenEl().getValue();
35972         
35973         return this.fixPrecision(this.parseValue(v));
35974     },
35975
35976     parseValue : function(value)
35977     {
35978         if(this.thousandsDelimiter) {
35979             value += "";
35980             r = new RegExp(",", "g");
35981             value = value.replace(r, "");
35982         }
35983         
35984         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35985         return isNaN(value) ? '' : value;
35986     },
35987
35988     fixPrecision : function(value)
35989     {
35990         if(this.thousandsDelimiter) {
35991             value += "";
35992             r = new RegExp(",", "g");
35993             value = value.replace(r, "");
35994         }
35995         
35996         var nan = isNaN(value);
35997         
35998         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35999             return nan ? '' : value;
36000         }
36001         return parseFloat(value).toFixed(this.decimalPrecision);
36002     },
36003
36004     setValue : function(v)
36005     {
36006         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36007         
36008         this.value = v;
36009         
36010         if(this.rendered){
36011             
36012             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36013             
36014             this.inputEl().dom.value = (v == '') ? '' :
36015                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36016             
36017             if(!this.allowZero && v === '0') {
36018                 this.hiddenEl().dom.value = '';
36019                 this.inputEl().dom.value = '';
36020             }
36021             
36022             this.validate();
36023         }
36024     },
36025
36026     decimalPrecisionFcn : function(v)
36027     {
36028         return Math.floor(v);
36029     },
36030
36031     beforeBlur : function()
36032     {
36033         var v = this.parseValue(this.getRawValue());
36034         
36035         if(v || v === 0 || v === ''){
36036             this.setValue(v);
36037         }
36038     },
36039     
36040     hiddenEl : function()
36041     {
36042         return this.el.select('input.hidden-number-input',true).first();
36043     }
36044     
36045 });
36046
36047  
36048
36049 /*
36050 * Licence: LGPL
36051 */
36052
36053 /**
36054  * @class Roo.bootstrap.DocumentSlider
36055  * @extends Roo.bootstrap.Component
36056  * Bootstrap DocumentSlider class
36057  * 
36058  * @constructor
36059  * Create a new DocumentViewer
36060  * @param {Object} config The config object
36061  */
36062
36063 Roo.bootstrap.DocumentSlider = function(config){
36064     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36065     
36066     this.files = [];
36067     
36068     this.addEvents({
36069         /**
36070          * @event initial
36071          * Fire after initEvent
36072          * @param {Roo.bootstrap.DocumentSlider} this
36073          */
36074         "initial" : true,
36075         /**
36076          * @event update
36077          * Fire after update
36078          * @param {Roo.bootstrap.DocumentSlider} this
36079          */
36080         "update" : true,
36081         /**
36082          * @event click
36083          * Fire after click
36084          * @param {Roo.bootstrap.DocumentSlider} this
36085          */
36086         "click" : true
36087     });
36088 };
36089
36090 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36091     
36092     files : false,
36093     
36094     indicator : 0,
36095     
36096     getAutoCreate : function()
36097     {
36098         var cfg = {
36099             tag : 'div',
36100             cls : 'roo-document-slider',
36101             cn : [
36102                 {
36103                     tag : 'div',
36104                     cls : 'roo-document-slider-header',
36105                     cn : [
36106                         {
36107                             tag : 'div',
36108                             cls : 'roo-document-slider-header-title'
36109                         }
36110                     ]
36111                 },
36112                 {
36113                     tag : 'div',
36114                     cls : 'roo-document-slider-body',
36115                     cn : [
36116                         {
36117                             tag : 'div',
36118                             cls : 'roo-document-slider-prev',
36119                             cn : [
36120                                 {
36121                                     tag : 'i',
36122                                     cls : 'fa fa-chevron-left'
36123                                 }
36124                             ]
36125                         },
36126                         {
36127                             tag : 'div',
36128                             cls : 'roo-document-slider-thumb',
36129                             cn : [
36130                                 {
36131                                     tag : 'img',
36132                                     cls : 'roo-document-slider-image'
36133                                 }
36134                             ]
36135                         },
36136                         {
36137                             tag : 'div',
36138                             cls : 'roo-document-slider-next',
36139                             cn : [
36140                                 {
36141                                     tag : 'i',
36142                                     cls : 'fa fa-chevron-right'
36143                                 }
36144                             ]
36145                         }
36146                     ]
36147                 }
36148             ]
36149         };
36150         
36151         return cfg;
36152     },
36153     
36154     initEvents : function()
36155     {
36156         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36157         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36158         
36159         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36160         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36161         
36162         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36163         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36164         
36165         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36166         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36167         
36168         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36169         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36170         
36171         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36172         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36173         
36174         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36175         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36176         
36177         this.thumbEl.on('click', this.onClick, this);
36178         
36179         this.prevIndicator.on('click', this.prev, this);
36180         
36181         this.nextIndicator.on('click', this.next, this);
36182         
36183     },
36184     
36185     initial : function()
36186     {
36187         if(this.files.length){
36188             this.indicator = 1;
36189             this.update()
36190         }
36191         
36192         this.fireEvent('initial', this);
36193     },
36194     
36195     update : function()
36196     {
36197         this.imageEl.attr('src', this.files[this.indicator - 1]);
36198         
36199         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36200         
36201         this.prevIndicator.show();
36202         
36203         if(this.indicator == 1){
36204             this.prevIndicator.hide();
36205         }
36206         
36207         this.nextIndicator.show();
36208         
36209         if(this.indicator == this.files.length){
36210             this.nextIndicator.hide();
36211         }
36212         
36213         this.thumbEl.scrollTo('top');
36214         
36215         this.fireEvent('update', this);
36216     },
36217     
36218     onClick : function(e)
36219     {
36220         e.preventDefault();
36221         
36222         this.fireEvent('click', this);
36223     },
36224     
36225     prev : function(e)
36226     {
36227         e.preventDefault();
36228         
36229         this.indicator = Math.max(1, this.indicator - 1);
36230         
36231         this.update();
36232     },
36233     
36234     next : function(e)
36235     {
36236         e.preventDefault();
36237         
36238         this.indicator = Math.min(this.files.length, this.indicator + 1);
36239         
36240         this.update();
36241     }
36242 });
36243 /*
36244  * - LGPL
36245  *
36246  * RadioSet
36247  *
36248  *
36249  */
36250
36251 /**
36252  * @class Roo.bootstrap.RadioSet
36253  * @extends Roo.bootstrap.Input
36254  * Bootstrap RadioSet class
36255  * @cfg {String} indicatorpos (left|right) default left
36256  * @cfg {Boolean} inline (true|false) inline the element (default true)
36257  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36258  * @constructor
36259  * Create a new RadioSet
36260  * @param {Object} config The config object
36261  */
36262
36263 Roo.bootstrap.RadioSet = function(config){
36264     
36265     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36266     
36267     this.radioes = [];
36268     
36269     Roo.bootstrap.RadioSet.register(this);
36270     
36271     this.addEvents({
36272         /**
36273         * @event check
36274         * Fires when the element is checked or unchecked.
36275         * @param {Roo.bootstrap.RadioSet} this This radio
36276         * @param {Roo.bootstrap.Radio} item The checked item
36277         */
36278        check : true,
36279        /**
36280         * @event click
36281         * Fires when the element is click.
36282         * @param {Roo.bootstrap.RadioSet} this This radio set
36283         * @param {Roo.bootstrap.Radio} item The checked item
36284         * @param {Roo.EventObject} e The event object
36285         */
36286        click : true
36287     });
36288     
36289 };
36290
36291 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36292
36293     radioes : false,
36294     
36295     inline : true,
36296     
36297     weight : '',
36298     
36299     indicatorpos : 'left',
36300     
36301     getAutoCreate : function()
36302     {
36303         var label = {
36304             tag : 'label',
36305             cls : 'roo-radio-set-label',
36306             cn : [
36307                 {
36308                     tag : 'span',
36309                     html : this.fieldLabel
36310                 }
36311             ]
36312         };
36313         if (Roo.bootstrap.version == 3) {
36314             
36315             
36316             if(this.indicatorpos == 'left'){
36317                 label.cn.unshift({
36318                     tag : 'i',
36319                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36320                     tooltip : 'This field is required'
36321                 });
36322             } else {
36323                 label.cn.push({
36324                     tag : 'i',
36325                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36326                     tooltip : 'This field is required'
36327                 });
36328             }
36329         }
36330         var items = {
36331             tag : 'div',
36332             cls : 'roo-radio-set-items'
36333         };
36334         
36335         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36336         
36337         if (align === 'left' && this.fieldLabel.length) {
36338             
36339             items = {
36340                 cls : "roo-radio-set-right", 
36341                 cn: [
36342                     items
36343                 ]
36344             };
36345             
36346             if(this.labelWidth > 12){
36347                 label.style = "width: " + this.labelWidth + 'px';
36348             }
36349             
36350             if(this.labelWidth < 13 && this.labelmd == 0){
36351                 this.labelmd = this.labelWidth;
36352             }
36353             
36354             if(this.labellg > 0){
36355                 label.cls += ' col-lg-' + this.labellg;
36356                 items.cls += ' col-lg-' + (12 - this.labellg);
36357             }
36358             
36359             if(this.labelmd > 0){
36360                 label.cls += ' col-md-' + this.labelmd;
36361                 items.cls += ' col-md-' + (12 - this.labelmd);
36362             }
36363             
36364             if(this.labelsm > 0){
36365                 label.cls += ' col-sm-' + this.labelsm;
36366                 items.cls += ' col-sm-' + (12 - this.labelsm);
36367             }
36368             
36369             if(this.labelxs > 0){
36370                 label.cls += ' col-xs-' + this.labelxs;
36371                 items.cls += ' col-xs-' + (12 - this.labelxs);
36372             }
36373         }
36374         
36375         var cfg = {
36376             tag : 'div',
36377             cls : 'roo-radio-set',
36378             cn : [
36379                 {
36380                     tag : 'input',
36381                     cls : 'roo-radio-set-input',
36382                     type : 'hidden',
36383                     name : this.name,
36384                     value : this.value ? this.value :  ''
36385                 },
36386                 label,
36387                 items
36388             ]
36389         };
36390         
36391         if(this.weight.length){
36392             cfg.cls += ' roo-radio-' + this.weight;
36393         }
36394         
36395         if(this.inline) {
36396             cfg.cls += ' roo-radio-set-inline';
36397         }
36398         
36399         var settings=this;
36400         ['xs','sm','md','lg'].map(function(size){
36401             if (settings[size]) {
36402                 cfg.cls += ' col-' + size + '-' + settings[size];
36403             }
36404         });
36405         
36406         return cfg;
36407         
36408     },
36409
36410     initEvents : function()
36411     {
36412         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36413         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36414         
36415         if(!this.fieldLabel.length){
36416             this.labelEl.hide();
36417         }
36418         
36419         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36420         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36421         
36422         this.indicator = this.indicatorEl();
36423         
36424         if(this.indicator){
36425             this.indicator.addClass('invisible');
36426         }
36427         
36428         this.originalValue = this.getValue();
36429         
36430     },
36431     
36432     inputEl: function ()
36433     {
36434         return this.el.select('.roo-radio-set-input', true).first();
36435     },
36436     
36437     getChildContainer : function()
36438     {
36439         return this.itemsEl;
36440     },
36441     
36442     register : function(item)
36443     {
36444         this.radioes.push(item);
36445         
36446     },
36447     
36448     validate : function()
36449     {   
36450         if(this.getVisibilityEl().hasClass('hidden')){
36451             return true;
36452         }
36453         
36454         var valid = false;
36455         
36456         Roo.each(this.radioes, function(i){
36457             if(!i.checked){
36458                 return;
36459             }
36460             
36461             valid = true;
36462             return false;
36463         });
36464         
36465         if(this.allowBlank) {
36466             return true;
36467         }
36468         
36469         if(this.disabled || valid){
36470             this.markValid();
36471             return true;
36472         }
36473         
36474         this.markInvalid();
36475         return false;
36476         
36477     },
36478     
36479     markValid : function()
36480     {
36481         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36482             this.indicatorEl().removeClass('visible');
36483             this.indicatorEl().addClass('invisible');
36484         }
36485         
36486         
36487         if (Roo.bootstrap.version == 3) {
36488             this.el.removeClass([this.invalidClass, this.validClass]);
36489             this.el.addClass(this.validClass);
36490         } else {
36491             this.el.removeClass(['is-invalid','is-valid']);
36492             this.el.addClass(['is-valid']);
36493         }
36494         this.fireEvent('valid', this);
36495     },
36496     
36497     markInvalid : function(msg)
36498     {
36499         if(this.allowBlank || this.disabled){
36500             return;
36501         }
36502         
36503         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36504             this.indicatorEl().removeClass('invisible');
36505             this.indicatorEl().addClass('visible');
36506         }
36507         if (Roo.bootstrap.version == 3) {
36508             this.el.removeClass([this.invalidClass, this.validClass]);
36509             this.el.addClass(this.invalidClass);
36510         } else {
36511             this.el.removeClass(['is-invalid','is-valid']);
36512             this.el.addClass(['is-invalid']);
36513         }
36514         
36515         this.fireEvent('invalid', this, msg);
36516         
36517     },
36518     
36519     setValue : function(v, suppressEvent)
36520     {   
36521         if(this.value === v){
36522             return;
36523         }
36524         
36525         this.value = v;
36526         
36527         if(this.rendered){
36528             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36529         }
36530         
36531         Roo.each(this.radioes, function(i){
36532             i.checked = false;
36533             i.el.removeClass('checked');
36534         });
36535         
36536         Roo.each(this.radioes, function(i){
36537             
36538             if(i.value === v || i.value.toString() === v.toString()){
36539                 i.checked = true;
36540                 i.el.addClass('checked');
36541                 
36542                 if(suppressEvent !== true){
36543                     this.fireEvent('check', this, i);
36544                 }
36545                 
36546                 return false;
36547             }
36548             
36549         }, this);
36550         
36551         this.validate();
36552     },
36553     
36554     clearInvalid : function(){
36555         
36556         if(!this.el || this.preventMark){
36557             return;
36558         }
36559         
36560         this.el.removeClass([this.invalidClass]);
36561         
36562         this.fireEvent('valid', this);
36563     }
36564     
36565 });
36566
36567 Roo.apply(Roo.bootstrap.RadioSet, {
36568     
36569     groups: {},
36570     
36571     register : function(set)
36572     {
36573         this.groups[set.name] = set;
36574     },
36575     
36576     get: function(name) 
36577     {
36578         if (typeof(this.groups[name]) == 'undefined') {
36579             return false;
36580         }
36581         
36582         return this.groups[name] ;
36583     }
36584     
36585 });
36586 /*
36587  * Based on:
36588  * Ext JS Library 1.1.1
36589  * Copyright(c) 2006-2007, Ext JS, LLC.
36590  *
36591  * Originally Released Under LGPL - original licence link has changed is not relivant.
36592  *
36593  * Fork - LGPL
36594  * <script type="text/javascript">
36595  */
36596
36597
36598 /**
36599  * @class Roo.bootstrap.SplitBar
36600  * @extends Roo.util.Observable
36601  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36602  * <br><br>
36603  * Usage:
36604  * <pre><code>
36605 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36606                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36607 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36608 split.minSize = 100;
36609 split.maxSize = 600;
36610 split.animate = true;
36611 split.on('moved', splitterMoved);
36612 </code></pre>
36613  * @constructor
36614  * Create a new SplitBar
36615  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36616  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36617  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36618  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36619                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36620                         position of the SplitBar).
36621  */
36622 Roo.bootstrap.SplitBar = function(cfg){
36623     
36624     /** @private */
36625     
36626     //{
36627     //  dragElement : elm
36628     //  resizingElement: el,
36629         // optional..
36630     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36631     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36632         // existingProxy ???
36633     //}
36634     
36635     this.el = Roo.get(cfg.dragElement, true);
36636     this.el.dom.unselectable = "on";
36637     /** @private */
36638     this.resizingEl = Roo.get(cfg.resizingElement, true);
36639
36640     /**
36641      * @private
36642      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36643      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36644      * @type Number
36645      */
36646     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36647     
36648     /**
36649      * The minimum size of the resizing element. (Defaults to 0)
36650      * @type Number
36651      */
36652     this.minSize = 0;
36653     
36654     /**
36655      * The maximum size of the resizing element. (Defaults to 2000)
36656      * @type Number
36657      */
36658     this.maxSize = 2000;
36659     
36660     /**
36661      * Whether to animate the transition to the new size
36662      * @type Boolean
36663      */
36664     this.animate = false;
36665     
36666     /**
36667      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36668      * @type Boolean
36669      */
36670     this.useShim = false;
36671     
36672     /** @private */
36673     this.shim = null;
36674     
36675     if(!cfg.existingProxy){
36676         /** @private */
36677         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36678     }else{
36679         this.proxy = Roo.get(cfg.existingProxy).dom;
36680     }
36681     /** @private */
36682     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36683     
36684     /** @private */
36685     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36686     
36687     /** @private */
36688     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36689     
36690     /** @private */
36691     this.dragSpecs = {};
36692     
36693     /**
36694      * @private The adapter to use to positon and resize elements
36695      */
36696     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36697     this.adapter.init(this);
36698     
36699     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36700         /** @private */
36701         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36702         this.el.addClass("roo-splitbar-h");
36703     }else{
36704         /** @private */
36705         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36706         this.el.addClass("roo-splitbar-v");
36707     }
36708     
36709     this.addEvents({
36710         /**
36711          * @event resize
36712          * Fires when the splitter is moved (alias for {@link #event-moved})
36713          * @param {Roo.bootstrap.SplitBar} this
36714          * @param {Number} newSize the new width or height
36715          */
36716         "resize" : true,
36717         /**
36718          * @event moved
36719          * Fires when the splitter is moved
36720          * @param {Roo.bootstrap.SplitBar} this
36721          * @param {Number} newSize the new width or height
36722          */
36723         "moved" : true,
36724         /**
36725          * @event beforeresize
36726          * Fires before the splitter is dragged
36727          * @param {Roo.bootstrap.SplitBar} this
36728          */
36729         "beforeresize" : true,
36730
36731         "beforeapply" : true
36732     });
36733
36734     Roo.util.Observable.call(this);
36735 };
36736
36737 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36738     onStartProxyDrag : function(x, y){
36739         this.fireEvent("beforeresize", this);
36740         if(!this.overlay){
36741             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36742             o.unselectable();
36743             o.enableDisplayMode("block");
36744             // all splitbars share the same overlay
36745             Roo.bootstrap.SplitBar.prototype.overlay = o;
36746         }
36747         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36748         this.overlay.show();
36749         Roo.get(this.proxy).setDisplayed("block");
36750         var size = this.adapter.getElementSize(this);
36751         this.activeMinSize = this.getMinimumSize();;
36752         this.activeMaxSize = this.getMaximumSize();;
36753         var c1 = size - this.activeMinSize;
36754         var c2 = Math.max(this.activeMaxSize - size, 0);
36755         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36756             this.dd.resetConstraints();
36757             this.dd.setXConstraint(
36758                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36759                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36760             );
36761             this.dd.setYConstraint(0, 0);
36762         }else{
36763             this.dd.resetConstraints();
36764             this.dd.setXConstraint(0, 0);
36765             this.dd.setYConstraint(
36766                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36767                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36768             );
36769          }
36770         this.dragSpecs.startSize = size;
36771         this.dragSpecs.startPoint = [x, y];
36772         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36773     },
36774     
36775     /** 
36776      * @private Called after the drag operation by the DDProxy
36777      */
36778     onEndProxyDrag : function(e){
36779         Roo.get(this.proxy).setDisplayed(false);
36780         var endPoint = Roo.lib.Event.getXY(e);
36781         if(this.overlay){
36782             this.overlay.hide();
36783         }
36784         var newSize;
36785         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36786             newSize = this.dragSpecs.startSize + 
36787                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36788                     endPoint[0] - this.dragSpecs.startPoint[0] :
36789                     this.dragSpecs.startPoint[0] - endPoint[0]
36790                 );
36791         }else{
36792             newSize = this.dragSpecs.startSize + 
36793                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36794                     endPoint[1] - this.dragSpecs.startPoint[1] :
36795                     this.dragSpecs.startPoint[1] - endPoint[1]
36796                 );
36797         }
36798         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36799         if(newSize != this.dragSpecs.startSize){
36800             if(this.fireEvent('beforeapply', this, newSize) !== false){
36801                 this.adapter.setElementSize(this, newSize);
36802                 this.fireEvent("moved", this, newSize);
36803                 this.fireEvent("resize", this, newSize);
36804             }
36805         }
36806     },
36807     
36808     /**
36809      * Get the adapter this SplitBar uses
36810      * @return The adapter object
36811      */
36812     getAdapter : function(){
36813         return this.adapter;
36814     },
36815     
36816     /**
36817      * Set the adapter this SplitBar uses
36818      * @param {Object} adapter A SplitBar adapter object
36819      */
36820     setAdapter : function(adapter){
36821         this.adapter = adapter;
36822         this.adapter.init(this);
36823     },
36824     
36825     /**
36826      * Gets the minimum size for the resizing element
36827      * @return {Number} The minimum size
36828      */
36829     getMinimumSize : function(){
36830         return this.minSize;
36831     },
36832     
36833     /**
36834      * Sets the minimum size for the resizing element
36835      * @param {Number} minSize The minimum size
36836      */
36837     setMinimumSize : function(minSize){
36838         this.minSize = minSize;
36839     },
36840     
36841     /**
36842      * Gets the maximum size for the resizing element
36843      * @return {Number} The maximum size
36844      */
36845     getMaximumSize : function(){
36846         return this.maxSize;
36847     },
36848     
36849     /**
36850      * Sets the maximum size for the resizing element
36851      * @param {Number} maxSize The maximum size
36852      */
36853     setMaximumSize : function(maxSize){
36854         this.maxSize = maxSize;
36855     },
36856     
36857     /**
36858      * Sets the initialize size for the resizing element
36859      * @param {Number} size The initial size
36860      */
36861     setCurrentSize : function(size){
36862         var oldAnimate = this.animate;
36863         this.animate = false;
36864         this.adapter.setElementSize(this, size);
36865         this.animate = oldAnimate;
36866     },
36867     
36868     /**
36869      * Destroy this splitbar. 
36870      * @param {Boolean} removeEl True to remove the element
36871      */
36872     destroy : function(removeEl){
36873         if(this.shim){
36874             this.shim.remove();
36875         }
36876         this.dd.unreg();
36877         this.proxy.parentNode.removeChild(this.proxy);
36878         if(removeEl){
36879             this.el.remove();
36880         }
36881     }
36882 });
36883
36884 /**
36885  * @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.
36886  */
36887 Roo.bootstrap.SplitBar.createProxy = function(dir){
36888     var proxy = new Roo.Element(document.createElement("div"));
36889     proxy.unselectable();
36890     var cls = 'roo-splitbar-proxy';
36891     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36892     document.body.appendChild(proxy.dom);
36893     return proxy.dom;
36894 };
36895
36896 /** 
36897  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36898  * Default Adapter. It assumes the splitter and resizing element are not positioned
36899  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36900  */
36901 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36902 };
36903
36904 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36905     // do nothing for now
36906     init : function(s){
36907     
36908     },
36909     /**
36910      * Called before drag operations to get the current size of the resizing element. 
36911      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36912      */
36913      getElementSize : function(s){
36914         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36915             return s.resizingEl.getWidth();
36916         }else{
36917             return s.resizingEl.getHeight();
36918         }
36919     },
36920     
36921     /**
36922      * Called after drag operations to set the size of the resizing element.
36923      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36924      * @param {Number} newSize The new size to set
36925      * @param {Function} onComplete A function to be invoked when resizing is complete
36926      */
36927     setElementSize : function(s, newSize, onComplete){
36928         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36929             if(!s.animate){
36930                 s.resizingEl.setWidth(newSize);
36931                 if(onComplete){
36932                     onComplete(s, newSize);
36933                 }
36934             }else{
36935                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36936             }
36937         }else{
36938             
36939             if(!s.animate){
36940                 s.resizingEl.setHeight(newSize);
36941                 if(onComplete){
36942                     onComplete(s, newSize);
36943                 }
36944             }else{
36945                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36946             }
36947         }
36948     }
36949 };
36950
36951 /** 
36952  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36953  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36954  * Adapter that  moves the splitter element to align with the resized sizing element. 
36955  * Used with an absolute positioned SplitBar.
36956  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36957  * document.body, make sure you assign an id to the body element.
36958  */
36959 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36960     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36961     this.container = Roo.get(container);
36962 };
36963
36964 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36965     init : function(s){
36966         this.basic.init(s);
36967     },
36968     
36969     getElementSize : function(s){
36970         return this.basic.getElementSize(s);
36971     },
36972     
36973     setElementSize : function(s, newSize, onComplete){
36974         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36975     },
36976     
36977     moveSplitter : function(s){
36978         var yes = Roo.bootstrap.SplitBar;
36979         switch(s.placement){
36980             case yes.LEFT:
36981                 s.el.setX(s.resizingEl.getRight());
36982                 break;
36983             case yes.RIGHT:
36984                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36985                 break;
36986             case yes.TOP:
36987                 s.el.setY(s.resizingEl.getBottom());
36988                 break;
36989             case yes.BOTTOM:
36990                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36991                 break;
36992         }
36993     }
36994 };
36995
36996 /**
36997  * Orientation constant - Create a vertical SplitBar
36998  * @static
36999  * @type Number
37000  */
37001 Roo.bootstrap.SplitBar.VERTICAL = 1;
37002
37003 /**
37004  * Orientation constant - Create a horizontal SplitBar
37005  * @static
37006  * @type Number
37007  */
37008 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37009
37010 /**
37011  * Placement constant - The resizing element is to the left of the splitter element
37012  * @static
37013  * @type Number
37014  */
37015 Roo.bootstrap.SplitBar.LEFT = 1;
37016
37017 /**
37018  * Placement constant - The resizing element is to the right of the splitter element
37019  * @static
37020  * @type Number
37021  */
37022 Roo.bootstrap.SplitBar.RIGHT = 2;
37023
37024 /**
37025  * Placement constant - The resizing element is positioned above the splitter element
37026  * @static
37027  * @type Number
37028  */
37029 Roo.bootstrap.SplitBar.TOP = 3;
37030
37031 /**
37032  * Placement constant - The resizing element is positioned under splitter element
37033  * @static
37034  * @type Number
37035  */
37036 Roo.bootstrap.SplitBar.BOTTOM = 4;
37037 Roo.namespace("Roo.bootstrap.layout");/*
37038  * Based on:
37039  * Ext JS Library 1.1.1
37040  * Copyright(c) 2006-2007, Ext JS, LLC.
37041  *
37042  * Originally Released Under LGPL - original licence link has changed is not relivant.
37043  *
37044  * Fork - LGPL
37045  * <script type="text/javascript">
37046  */
37047
37048 /**
37049  * @class Roo.bootstrap.layout.Manager
37050  * @extends Roo.bootstrap.Component
37051  * Base class for layout managers.
37052  */
37053 Roo.bootstrap.layout.Manager = function(config)
37054 {
37055     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37056
37057
37058
37059
37060
37061     /** false to disable window resize monitoring @type Boolean */
37062     this.monitorWindowResize = true;
37063     this.regions = {};
37064     this.addEvents({
37065         /**
37066          * @event layout
37067          * Fires when a layout is performed.
37068          * @param {Roo.LayoutManager} this
37069          */
37070         "layout" : true,
37071         /**
37072          * @event regionresized
37073          * Fires when the user resizes a region.
37074          * @param {Roo.LayoutRegion} region The resized region
37075          * @param {Number} newSize The new size (width for east/west, height for north/south)
37076          */
37077         "regionresized" : true,
37078         /**
37079          * @event regioncollapsed
37080          * Fires when a region is collapsed.
37081          * @param {Roo.LayoutRegion} region The collapsed region
37082          */
37083         "regioncollapsed" : true,
37084         /**
37085          * @event regionexpanded
37086          * Fires when a region is expanded.
37087          * @param {Roo.LayoutRegion} region The expanded region
37088          */
37089         "regionexpanded" : true
37090     });
37091     this.updating = false;
37092
37093     if (config.el) {
37094         this.el = Roo.get(config.el);
37095         this.initEvents();
37096     }
37097
37098 };
37099
37100 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37101
37102
37103     regions : null,
37104
37105     monitorWindowResize : true,
37106
37107
37108     updating : false,
37109
37110
37111     onRender : function(ct, position)
37112     {
37113         if(!this.el){
37114             this.el = Roo.get(ct);
37115             this.initEvents();
37116         }
37117         //this.fireEvent('render',this);
37118     },
37119
37120
37121     initEvents: function()
37122     {
37123
37124
37125         // ie scrollbar fix
37126         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37127             document.body.scroll = "no";
37128         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37129             this.el.position('relative');
37130         }
37131         this.id = this.el.id;
37132         this.el.addClass("roo-layout-container");
37133         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37134         if(this.el.dom != document.body ) {
37135             this.el.on('resize', this.layout,this);
37136             this.el.on('show', this.layout,this);
37137         }
37138
37139     },
37140
37141     /**
37142      * Returns true if this layout is currently being updated
37143      * @return {Boolean}
37144      */
37145     isUpdating : function(){
37146         return this.updating;
37147     },
37148
37149     /**
37150      * Suspend the LayoutManager from doing auto-layouts while
37151      * making multiple add or remove calls
37152      */
37153     beginUpdate : function(){
37154         this.updating = true;
37155     },
37156
37157     /**
37158      * Restore auto-layouts and optionally disable the manager from performing a layout
37159      * @param {Boolean} noLayout true to disable a layout update
37160      */
37161     endUpdate : function(noLayout){
37162         this.updating = false;
37163         if(!noLayout){
37164             this.layout();
37165         }
37166     },
37167
37168     layout: function(){
37169         // abstract...
37170     },
37171
37172     onRegionResized : function(region, newSize){
37173         this.fireEvent("regionresized", region, newSize);
37174         this.layout();
37175     },
37176
37177     onRegionCollapsed : function(region){
37178         this.fireEvent("regioncollapsed", region);
37179     },
37180
37181     onRegionExpanded : function(region){
37182         this.fireEvent("regionexpanded", region);
37183     },
37184
37185     /**
37186      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37187      * performs box-model adjustments.
37188      * @return {Object} The size as an object {width: (the width), height: (the height)}
37189      */
37190     getViewSize : function()
37191     {
37192         var size;
37193         if(this.el.dom != document.body){
37194             size = this.el.getSize();
37195         }else{
37196             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37197         }
37198         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37199         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37200         return size;
37201     },
37202
37203     /**
37204      * Returns the Element this layout is bound to.
37205      * @return {Roo.Element}
37206      */
37207     getEl : function(){
37208         return this.el;
37209     },
37210
37211     /**
37212      * Returns the specified region.
37213      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37214      * @return {Roo.LayoutRegion}
37215      */
37216     getRegion : function(target){
37217         return this.regions[target.toLowerCase()];
37218     },
37219
37220     onWindowResize : function(){
37221         if(this.monitorWindowResize){
37222             this.layout();
37223         }
37224     }
37225 });
37226 /*
37227  * Based on:
37228  * Ext JS Library 1.1.1
37229  * Copyright(c) 2006-2007, Ext JS, LLC.
37230  *
37231  * Originally Released Under LGPL - original licence link has changed is not relivant.
37232  *
37233  * Fork - LGPL
37234  * <script type="text/javascript">
37235  */
37236 /**
37237  * @class Roo.bootstrap.layout.Border
37238  * @extends Roo.bootstrap.layout.Manager
37239  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37240  * please see: examples/bootstrap/nested.html<br><br>
37241  
37242 <b>The container the layout is rendered into can be either the body element or any other element.
37243 If it is not the body element, the container needs to either be an absolute positioned element,
37244 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37245 the container size if it is not the body element.</b>
37246
37247 * @constructor
37248 * Create a new Border
37249 * @param {Object} config Configuration options
37250  */
37251 Roo.bootstrap.layout.Border = function(config){
37252     config = config || {};
37253     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37254     
37255     
37256     
37257     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37258         if(config[region]){
37259             config[region].region = region;
37260             this.addRegion(config[region]);
37261         }
37262     },this);
37263     
37264 };
37265
37266 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37267
37268 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37269     
37270     parent : false, // this might point to a 'nest' or a ???
37271     
37272     /**
37273      * Creates and adds a new region if it doesn't already exist.
37274      * @param {String} target The target region key (north, south, east, west or center).
37275      * @param {Object} config The regions config object
37276      * @return {BorderLayoutRegion} The new region
37277      */
37278     addRegion : function(config)
37279     {
37280         if(!this.regions[config.region]){
37281             var r = this.factory(config);
37282             this.bindRegion(r);
37283         }
37284         return this.regions[config.region];
37285     },
37286
37287     // private (kinda)
37288     bindRegion : function(r){
37289         this.regions[r.config.region] = r;
37290         
37291         r.on("visibilitychange",    this.layout, this);
37292         r.on("paneladded",          this.layout, this);
37293         r.on("panelremoved",        this.layout, this);
37294         r.on("invalidated",         this.layout, this);
37295         r.on("resized",             this.onRegionResized, this);
37296         r.on("collapsed",           this.onRegionCollapsed, this);
37297         r.on("expanded",            this.onRegionExpanded, this);
37298     },
37299
37300     /**
37301      * Performs a layout update.
37302      */
37303     layout : function()
37304     {
37305         if(this.updating) {
37306             return;
37307         }
37308         
37309         // render all the rebions if they have not been done alreayd?
37310         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37311             if(this.regions[region] && !this.regions[region].bodyEl){
37312                 this.regions[region].onRender(this.el)
37313             }
37314         },this);
37315         
37316         var size = this.getViewSize();
37317         var w = size.width;
37318         var h = size.height;
37319         var centerW = w;
37320         var centerH = h;
37321         var centerY = 0;
37322         var centerX = 0;
37323         //var x = 0, y = 0;
37324
37325         var rs = this.regions;
37326         var north = rs["north"];
37327         var south = rs["south"]; 
37328         var west = rs["west"];
37329         var east = rs["east"];
37330         var center = rs["center"];
37331         //if(this.hideOnLayout){ // not supported anymore
37332             //c.el.setStyle("display", "none");
37333         //}
37334         if(north && north.isVisible()){
37335             var b = north.getBox();
37336             var m = north.getMargins();
37337             b.width = w - (m.left+m.right);
37338             b.x = m.left;
37339             b.y = m.top;
37340             centerY = b.height + b.y + m.bottom;
37341             centerH -= centerY;
37342             north.updateBox(this.safeBox(b));
37343         }
37344         if(south && south.isVisible()){
37345             var b = south.getBox();
37346             var m = south.getMargins();
37347             b.width = w - (m.left+m.right);
37348             b.x = m.left;
37349             var totalHeight = (b.height + m.top + m.bottom);
37350             b.y = h - totalHeight + m.top;
37351             centerH -= totalHeight;
37352             south.updateBox(this.safeBox(b));
37353         }
37354         if(west && west.isVisible()){
37355             var b = west.getBox();
37356             var m = west.getMargins();
37357             b.height = centerH - (m.top+m.bottom);
37358             b.x = m.left;
37359             b.y = centerY + m.top;
37360             var totalWidth = (b.width + m.left + m.right);
37361             centerX += totalWidth;
37362             centerW -= totalWidth;
37363             west.updateBox(this.safeBox(b));
37364         }
37365         if(east && east.isVisible()){
37366             var b = east.getBox();
37367             var m = east.getMargins();
37368             b.height = centerH - (m.top+m.bottom);
37369             var totalWidth = (b.width + m.left + m.right);
37370             b.x = w - totalWidth + m.left;
37371             b.y = centerY + m.top;
37372             centerW -= totalWidth;
37373             east.updateBox(this.safeBox(b));
37374         }
37375         if(center){
37376             var m = center.getMargins();
37377             var centerBox = {
37378                 x: centerX + m.left,
37379                 y: centerY + m.top,
37380                 width: centerW - (m.left+m.right),
37381                 height: centerH - (m.top+m.bottom)
37382             };
37383             //if(this.hideOnLayout){
37384                 //center.el.setStyle("display", "block");
37385             //}
37386             center.updateBox(this.safeBox(centerBox));
37387         }
37388         this.el.repaint();
37389         this.fireEvent("layout", this);
37390     },
37391
37392     // private
37393     safeBox : function(box){
37394         box.width = Math.max(0, box.width);
37395         box.height = Math.max(0, box.height);
37396         return box;
37397     },
37398
37399     /**
37400      * Adds a ContentPanel (or subclass) to this layout.
37401      * @param {String} target The target region key (north, south, east, west or center).
37402      * @param {Roo.ContentPanel} panel The panel to add
37403      * @return {Roo.ContentPanel} The added panel
37404      */
37405     add : function(target, panel){
37406          
37407         target = target.toLowerCase();
37408         return this.regions[target].add(panel);
37409     },
37410
37411     /**
37412      * Remove a ContentPanel (or subclass) to this layout.
37413      * @param {String} target The target region key (north, south, east, west or center).
37414      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37415      * @return {Roo.ContentPanel} The removed panel
37416      */
37417     remove : function(target, panel){
37418         target = target.toLowerCase();
37419         return this.regions[target].remove(panel);
37420     },
37421
37422     /**
37423      * Searches all regions for a panel with the specified id
37424      * @param {String} panelId
37425      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37426      */
37427     findPanel : function(panelId){
37428         var rs = this.regions;
37429         for(var target in rs){
37430             if(typeof rs[target] != "function"){
37431                 var p = rs[target].getPanel(panelId);
37432                 if(p){
37433                     return p;
37434                 }
37435             }
37436         }
37437         return null;
37438     },
37439
37440     /**
37441      * Searches all regions for a panel with the specified id and activates (shows) it.
37442      * @param {String/ContentPanel} panelId The panels id or the panel itself
37443      * @return {Roo.ContentPanel} The shown panel or null
37444      */
37445     showPanel : function(panelId) {
37446       var rs = this.regions;
37447       for(var target in rs){
37448          var r = rs[target];
37449          if(typeof r != "function"){
37450             if(r.hasPanel(panelId)){
37451                return r.showPanel(panelId);
37452             }
37453          }
37454       }
37455       return null;
37456    },
37457
37458    /**
37459      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37460      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37461      */
37462    /*
37463     restoreState : function(provider){
37464         if(!provider){
37465             provider = Roo.state.Manager;
37466         }
37467         var sm = new Roo.LayoutStateManager();
37468         sm.init(this, provider);
37469     },
37470 */
37471  
37472  
37473     /**
37474      * Adds a xtype elements to the layout.
37475      * <pre><code>
37476
37477 layout.addxtype({
37478        xtype : 'ContentPanel',
37479        region: 'west',
37480        items: [ .... ]
37481    }
37482 );
37483
37484 layout.addxtype({
37485         xtype : 'NestedLayoutPanel',
37486         region: 'west',
37487         layout: {
37488            center: { },
37489            west: { }   
37490         },
37491         items : [ ... list of content panels or nested layout panels.. ]
37492    }
37493 );
37494 </code></pre>
37495      * @param {Object} cfg Xtype definition of item to add.
37496      */
37497     addxtype : function(cfg)
37498     {
37499         // basically accepts a pannel...
37500         // can accept a layout region..!?!?
37501         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37502         
37503         
37504         // theory?  children can only be panels??
37505         
37506         //if (!cfg.xtype.match(/Panel$/)) {
37507         //    return false;
37508         //}
37509         var ret = false;
37510         
37511         if (typeof(cfg.region) == 'undefined') {
37512             Roo.log("Failed to add Panel, region was not set");
37513             Roo.log(cfg);
37514             return false;
37515         }
37516         var region = cfg.region;
37517         delete cfg.region;
37518         
37519           
37520         var xitems = [];
37521         if (cfg.items) {
37522             xitems = cfg.items;
37523             delete cfg.items;
37524         }
37525         var nb = false;
37526         
37527         if ( region == 'center') {
37528             Roo.log("Center: " + cfg.title);
37529         }
37530         
37531         
37532         switch(cfg.xtype) 
37533         {
37534             case 'Content':  // ContentPanel (el, cfg)
37535             case 'Scroll':  // ContentPanel (el, cfg)
37536             case 'View': 
37537                 cfg.autoCreate = cfg.autoCreate || true;
37538                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37539                 //} else {
37540                 //    var el = this.el.createChild();
37541                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37542                 //}
37543                 
37544                 this.add(region, ret);
37545                 break;
37546             
37547             /*
37548             case 'TreePanel': // our new panel!
37549                 cfg.el = this.el.createChild();
37550                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37551                 this.add(region, ret);
37552                 break;
37553             */
37554             
37555             case 'Nest': 
37556                 // create a new Layout (which is  a Border Layout...
37557                 
37558                 var clayout = cfg.layout;
37559                 clayout.el  = this.el.createChild();
37560                 clayout.items   = clayout.items  || [];
37561                 
37562                 delete cfg.layout;
37563                 
37564                 // replace this exitems with the clayout ones..
37565                 xitems = clayout.items;
37566                  
37567                 // force background off if it's in center...
37568                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37569                     cfg.background = false;
37570                 }
37571                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37572                 
37573                 
37574                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37575                 //console.log('adding nested layout panel '  + cfg.toSource());
37576                 this.add(region, ret);
37577                 nb = {}; /// find first...
37578                 break;
37579             
37580             case 'Grid':
37581                 
37582                 // needs grid and region
37583                 
37584                 //var el = this.getRegion(region).el.createChild();
37585                 /*
37586                  *var el = this.el.createChild();
37587                 // create the grid first...
37588                 cfg.grid.container = el;
37589                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37590                 */
37591                 
37592                 if (region == 'center' && this.active ) {
37593                     cfg.background = false;
37594                 }
37595                 
37596                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37597                 
37598                 this.add(region, ret);
37599                 /*
37600                 if (cfg.background) {
37601                     // render grid on panel activation (if panel background)
37602                     ret.on('activate', function(gp) {
37603                         if (!gp.grid.rendered) {
37604                     //        gp.grid.render(el);
37605                         }
37606                     });
37607                 } else {
37608                   //  cfg.grid.render(el);
37609                 }
37610                 */
37611                 break;
37612            
37613            
37614             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37615                 // it was the old xcomponent building that caused this before.
37616                 // espeically if border is the top element in the tree.
37617                 ret = this;
37618                 break; 
37619                 
37620                     
37621                 
37622                 
37623                 
37624             default:
37625                 /*
37626                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37627                     
37628                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37629                     this.add(region, ret);
37630                 } else {
37631                 */
37632                     Roo.log(cfg);
37633                     throw "Can not add '" + cfg.xtype + "' to Border";
37634                     return null;
37635              
37636                                 
37637              
37638         }
37639         this.beginUpdate();
37640         // add children..
37641         var region = '';
37642         var abn = {};
37643         Roo.each(xitems, function(i)  {
37644             region = nb && i.region ? i.region : false;
37645             
37646             var add = ret.addxtype(i);
37647            
37648             if (region) {
37649                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37650                 if (!i.background) {
37651                     abn[region] = nb[region] ;
37652                 }
37653             }
37654             
37655         });
37656         this.endUpdate();
37657
37658         // make the last non-background panel active..
37659         //if (nb) { Roo.log(abn); }
37660         if (nb) {
37661             
37662             for(var r in abn) {
37663                 region = this.getRegion(r);
37664                 if (region) {
37665                     // tried using nb[r], but it does not work..
37666                      
37667                     region.showPanel(abn[r]);
37668                    
37669                 }
37670             }
37671         }
37672         return ret;
37673         
37674     },
37675     
37676     
37677 // private
37678     factory : function(cfg)
37679     {
37680         
37681         var validRegions = Roo.bootstrap.layout.Border.regions;
37682
37683         var target = cfg.region;
37684         cfg.mgr = this;
37685         
37686         var r = Roo.bootstrap.layout;
37687         Roo.log(target);
37688         switch(target){
37689             case "north":
37690                 return new r.North(cfg);
37691             case "south":
37692                 return new r.South(cfg);
37693             case "east":
37694                 return new r.East(cfg);
37695             case "west":
37696                 return new r.West(cfg);
37697             case "center":
37698                 return new r.Center(cfg);
37699         }
37700         throw 'Layout region "'+target+'" not supported.';
37701     }
37702     
37703     
37704 });
37705  /*
37706  * Based on:
37707  * Ext JS Library 1.1.1
37708  * Copyright(c) 2006-2007, Ext JS, LLC.
37709  *
37710  * Originally Released Under LGPL - original licence link has changed is not relivant.
37711  *
37712  * Fork - LGPL
37713  * <script type="text/javascript">
37714  */
37715  
37716 /**
37717  * @class Roo.bootstrap.layout.Basic
37718  * @extends Roo.util.Observable
37719  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37720  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37721  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37722  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37723  * @cfg {string}   region  the region that it inhabits..
37724  * @cfg {bool}   skipConfig skip config?
37725  * 
37726
37727  */
37728 Roo.bootstrap.layout.Basic = function(config){
37729     
37730     this.mgr = config.mgr;
37731     
37732     this.position = config.region;
37733     
37734     var skipConfig = config.skipConfig;
37735     
37736     this.events = {
37737         /**
37738          * @scope Roo.BasicLayoutRegion
37739          */
37740         
37741         /**
37742          * @event beforeremove
37743          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37744          * @param {Roo.LayoutRegion} this
37745          * @param {Roo.ContentPanel} panel The panel
37746          * @param {Object} e The cancel event object
37747          */
37748         "beforeremove" : true,
37749         /**
37750          * @event invalidated
37751          * Fires when the layout for this region is changed.
37752          * @param {Roo.LayoutRegion} this
37753          */
37754         "invalidated" : true,
37755         /**
37756          * @event visibilitychange
37757          * Fires when this region is shown or hidden 
37758          * @param {Roo.LayoutRegion} this
37759          * @param {Boolean} visibility true or false
37760          */
37761         "visibilitychange" : true,
37762         /**
37763          * @event paneladded
37764          * Fires when a panel is added. 
37765          * @param {Roo.LayoutRegion} this
37766          * @param {Roo.ContentPanel} panel The panel
37767          */
37768         "paneladded" : true,
37769         /**
37770          * @event panelremoved
37771          * Fires when a panel is removed. 
37772          * @param {Roo.LayoutRegion} this
37773          * @param {Roo.ContentPanel} panel The panel
37774          */
37775         "panelremoved" : true,
37776         /**
37777          * @event beforecollapse
37778          * Fires when this region before collapse.
37779          * @param {Roo.LayoutRegion} this
37780          */
37781         "beforecollapse" : true,
37782         /**
37783          * @event collapsed
37784          * Fires when this region is collapsed.
37785          * @param {Roo.LayoutRegion} this
37786          */
37787         "collapsed" : true,
37788         /**
37789          * @event expanded
37790          * Fires when this region is expanded.
37791          * @param {Roo.LayoutRegion} this
37792          */
37793         "expanded" : true,
37794         /**
37795          * @event slideshow
37796          * Fires when this region is slid into view.
37797          * @param {Roo.LayoutRegion} this
37798          */
37799         "slideshow" : true,
37800         /**
37801          * @event slidehide
37802          * Fires when this region slides out of view. 
37803          * @param {Roo.LayoutRegion} this
37804          */
37805         "slidehide" : true,
37806         /**
37807          * @event panelactivated
37808          * Fires when a panel is activated. 
37809          * @param {Roo.LayoutRegion} this
37810          * @param {Roo.ContentPanel} panel The activated panel
37811          */
37812         "panelactivated" : true,
37813         /**
37814          * @event resized
37815          * Fires when the user resizes this region. 
37816          * @param {Roo.LayoutRegion} this
37817          * @param {Number} newSize The new size (width for east/west, height for north/south)
37818          */
37819         "resized" : true
37820     };
37821     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37822     this.panels = new Roo.util.MixedCollection();
37823     this.panels.getKey = this.getPanelId.createDelegate(this);
37824     this.box = null;
37825     this.activePanel = null;
37826     // ensure listeners are added...
37827     
37828     if (config.listeners || config.events) {
37829         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37830             listeners : config.listeners || {},
37831             events : config.events || {}
37832         });
37833     }
37834     
37835     if(skipConfig !== true){
37836         this.applyConfig(config);
37837     }
37838 };
37839
37840 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37841 {
37842     getPanelId : function(p){
37843         return p.getId();
37844     },
37845     
37846     applyConfig : function(config){
37847         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37848         this.config = config;
37849         
37850     },
37851     
37852     /**
37853      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37854      * the width, for horizontal (north, south) the height.
37855      * @param {Number} newSize The new width or height
37856      */
37857     resizeTo : function(newSize){
37858         var el = this.el ? this.el :
37859                  (this.activePanel ? this.activePanel.getEl() : null);
37860         if(el){
37861             switch(this.position){
37862                 case "east":
37863                 case "west":
37864                     el.setWidth(newSize);
37865                     this.fireEvent("resized", this, newSize);
37866                 break;
37867                 case "north":
37868                 case "south":
37869                     el.setHeight(newSize);
37870                     this.fireEvent("resized", this, newSize);
37871                 break;                
37872             }
37873         }
37874     },
37875     
37876     getBox : function(){
37877         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37878     },
37879     
37880     getMargins : function(){
37881         return this.margins;
37882     },
37883     
37884     updateBox : function(box){
37885         this.box = box;
37886         var el = this.activePanel.getEl();
37887         el.dom.style.left = box.x + "px";
37888         el.dom.style.top = box.y + "px";
37889         this.activePanel.setSize(box.width, box.height);
37890     },
37891     
37892     /**
37893      * Returns the container element for this region.
37894      * @return {Roo.Element}
37895      */
37896     getEl : function(){
37897         return this.activePanel;
37898     },
37899     
37900     /**
37901      * Returns true if this region is currently visible.
37902      * @return {Boolean}
37903      */
37904     isVisible : function(){
37905         return this.activePanel ? true : false;
37906     },
37907     
37908     setActivePanel : function(panel){
37909         panel = this.getPanel(panel);
37910         if(this.activePanel && this.activePanel != panel){
37911             this.activePanel.setActiveState(false);
37912             this.activePanel.getEl().setLeftTop(-10000,-10000);
37913         }
37914         this.activePanel = panel;
37915         panel.setActiveState(true);
37916         if(this.box){
37917             panel.setSize(this.box.width, this.box.height);
37918         }
37919         this.fireEvent("panelactivated", this, panel);
37920         this.fireEvent("invalidated");
37921     },
37922     
37923     /**
37924      * Show the specified panel.
37925      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37926      * @return {Roo.ContentPanel} The shown panel or null
37927      */
37928     showPanel : function(panel){
37929         panel = this.getPanel(panel);
37930         if(panel){
37931             this.setActivePanel(panel);
37932         }
37933         return panel;
37934     },
37935     
37936     /**
37937      * Get the active panel for this region.
37938      * @return {Roo.ContentPanel} The active panel or null
37939      */
37940     getActivePanel : function(){
37941         return this.activePanel;
37942     },
37943     
37944     /**
37945      * Add the passed ContentPanel(s)
37946      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37947      * @return {Roo.ContentPanel} The panel added (if only one was added)
37948      */
37949     add : function(panel){
37950         if(arguments.length > 1){
37951             for(var i = 0, len = arguments.length; i < len; i++) {
37952                 this.add(arguments[i]);
37953             }
37954             return null;
37955         }
37956         if(this.hasPanel(panel)){
37957             this.showPanel(panel);
37958             return panel;
37959         }
37960         var el = panel.getEl();
37961         if(el.dom.parentNode != this.mgr.el.dom){
37962             this.mgr.el.dom.appendChild(el.dom);
37963         }
37964         if(panel.setRegion){
37965             panel.setRegion(this);
37966         }
37967         this.panels.add(panel);
37968         el.setStyle("position", "absolute");
37969         if(!panel.background){
37970             this.setActivePanel(panel);
37971             if(this.config.initialSize && this.panels.getCount()==1){
37972                 this.resizeTo(this.config.initialSize);
37973             }
37974         }
37975         this.fireEvent("paneladded", this, panel);
37976         return panel;
37977     },
37978     
37979     /**
37980      * Returns true if the panel is in this region.
37981      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37982      * @return {Boolean}
37983      */
37984     hasPanel : function(panel){
37985         if(typeof panel == "object"){ // must be panel obj
37986             panel = panel.getId();
37987         }
37988         return this.getPanel(panel) ? true : false;
37989     },
37990     
37991     /**
37992      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37993      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37994      * @param {Boolean} preservePanel Overrides the config preservePanel option
37995      * @return {Roo.ContentPanel} The panel that was removed
37996      */
37997     remove : function(panel, preservePanel){
37998         panel = this.getPanel(panel);
37999         if(!panel){
38000             return null;
38001         }
38002         var e = {};
38003         this.fireEvent("beforeremove", this, panel, e);
38004         if(e.cancel === true){
38005             return null;
38006         }
38007         var panelId = panel.getId();
38008         this.panels.removeKey(panelId);
38009         return panel;
38010     },
38011     
38012     /**
38013      * Returns the panel specified or null if it's not in this region.
38014      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38015      * @return {Roo.ContentPanel}
38016      */
38017     getPanel : function(id){
38018         if(typeof id == "object"){ // must be panel obj
38019             return id;
38020         }
38021         return this.panels.get(id);
38022     },
38023     
38024     /**
38025      * Returns this regions position (north/south/east/west/center).
38026      * @return {String} 
38027      */
38028     getPosition: function(){
38029         return this.position;    
38030     }
38031 });/*
38032  * Based on:
38033  * Ext JS Library 1.1.1
38034  * Copyright(c) 2006-2007, Ext JS, LLC.
38035  *
38036  * Originally Released Under LGPL - original licence link has changed is not relivant.
38037  *
38038  * Fork - LGPL
38039  * <script type="text/javascript">
38040  */
38041  
38042 /**
38043  * @class Roo.bootstrap.layout.Region
38044  * @extends Roo.bootstrap.layout.Basic
38045  * This class represents a region in a layout manager.
38046  
38047  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38048  * @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})
38049  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38050  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38051  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38052  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38053  * @cfg {String}    title           The title for the region (overrides panel titles)
38054  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38055  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38056  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38057  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38058  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38059  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38060  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38061  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38062  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38063  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38064
38065  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38066  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38067  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38068  * @cfg {Number}    width           For East/West panels
38069  * @cfg {Number}    height          For North/South panels
38070  * @cfg {Boolean}   split           To show the splitter
38071  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38072  * 
38073  * @cfg {string}   cls             Extra CSS classes to add to region
38074  * 
38075  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38076  * @cfg {string}   region  the region that it inhabits..
38077  *
38078
38079  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38080  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38081
38082  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38083  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38084  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38085  */
38086 Roo.bootstrap.layout.Region = function(config)
38087 {
38088     this.applyConfig(config);
38089
38090     var mgr = config.mgr;
38091     var pos = config.region;
38092     config.skipConfig = true;
38093     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38094     
38095     if (mgr.el) {
38096         this.onRender(mgr.el);   
38097     }
38098      
38099     this.visible = true;
38100     this.collapsed = false;
38101     this.unrendered_panels = [];
38102 };
38103
38104 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38105
38106     position: '', // set by wrapper (eg. north/south etc..)
38107     unrendered_panels : null,  // unrendered panels.
38108     
38109     tabPosition : false,
38110     
38111     mgr: false, // points to 'Border'
38112     
38113     
38114     createBody : function(){
38115         /** This region's body element 
38116         * @type Roo.Element */
38117         this.bodyEl = this.el.createChild({
38118                 tag: "div",
38119                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38120         });
38121     },
38122
38123     onRender: function(ctr, pos)
38124     {
38125         var dh = Roo.DomHelper;
38126         /** This region's container element 
38127         * @type Roo.Element */
38128         this.el = dh.append(ctr.dom, {
38129                 tag: "div",
38130                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38131             }, true);
38132         /** This region's title element 
38133         * @type Roo.Element */
38134     
38135         this.titleEl = dh.append(this.el.dom,  {
38136                 tag: "div",
38137                 unselectable: "on",
38138                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38139                 children:[
38140                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38141                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38142                 ]
38143             }, true);
38144         
38145         this.titleEl.enableDisplayMode();
38146         /** This region's title text element 
38147         * @type HTMLElement */
38148         this.titleTextEl = this.titleEl.dom.firstChild;
38149         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38150         /*
38151         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38152         this.closeBtn.enableDisplayMode();
38153         this.closeBtn.on("click", this.closeClicked, this);
38154         this.closeBtn.hide();
38155     */
38156         this.createBody(this.config);
38157         if(this.config.hideWhenEmpty){
38158             this.hide();
38159             this.on("paneladded", this.validateVisibility, this);
38160             this.on("panelremoved", this.validateVisibility, this);
38161         }
38162         if(this.autoScroll){
38163             this.bodyEl.setStyle("overflow", "auto");
38164         }else{
38165             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38166         }
38167         //if(c.titlebar !== false){
38168             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38169                 this.titleEl.hide();
38170             }else{
38171                 this.titleEl.show();
38172                 if(this.config.title){
38173                     this.titleTextEl.innerHTML = this.config.title;
38174                 }
38175             }
38176         //}
38177         if(this.config.collapsed){
38178             this.collapse(true);
38179         }
38180         if(this.config.hidden){
38181             this.hide();
38182         }
38183         
38184         if (this.unrendered_panels && this.unrendered_panels.length) {
38185             for (var i =0;i< this.unrendered_panels.length; i++) {
38186                 this.add(this.unrendered_panels[i]);
38187             }
38188             this.unrendered_panels = null;
38189             
38190         }
38191         
38192     },
38193     
38194     applyConfig : function(c)
38195     {
38196         /*
38197          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38198             var dh = Roo.DomHelper;
38199             if(c.titlebar !== false){
38200                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38201                 this.collapseBtn.on("click", this.collapse, this);
38202                 this.collapseBtn.enableDisplayMode();
38203                 /*
38204                 if(c.showPin === true || this.showPin){
38205                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38206                     this.stickBtn.enableDisplayMode();
38207                     this.stickBtn.on("click", this.expand, this);
38208                     this.stickBtn.hide();
38209                 }
38210                 
38211             }
38212             */
38213             /** This region's collapsed element
38214             * @type Roo.Element */
38215             /*
38216              *
38217             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38218                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38219             ]}, true);
38220             
38221             if(c.floatable !== false){
38222                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38223                this.collapsedEl.on("click", this.collapseClick, this);
38224             }
38225
38226             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38227                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38228                    id: "message", unselectable: "on", style:{"float":"left"}});
38229                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38230              }
38231             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38232             this.expandBtn.on("click", this.expand, this);
38233             
38234         }
38235         
38236         if(this.collapseBtn){
38237             this.collapseBtn.setVisible(c.collapsible == true);
38238         }
38239         
38240         this.cmargins = c.cmargins || this.cmargins ||
38241                          (this.position == "west" || this.position == "east" ?
38242                              {top: 0, left: 2, right:2, bottom: 0} :
38243                              {top: 2, left: 0, right:0, bottom: 2});
38244         */
38245         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38246         
38247         
38248         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38249         
38250         this.autoScroll = c.autoScroll || false;
38251         
38252         
38253        
38254         
38255         this.duration = c.duration || .30;
38256         this.slideDuration = c.slideDuration || .45;
38257         this.config = c;
38258        
38259     },
38260     /**
38261      * Returns true if this region is currently visible.
38262      * @return {Boolean}
38263      */
38264     isVisible : function(){
38265         return this.visible;
38266     },
38267
38268     /**
38269      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38270      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38271      */
38272     //setCollapsedTitle : function(title){
38273     //    title = title || "&#160;";
38274      //   if(this.collapsedTitleTextEl){
38275       //      this.collapsedTitleTextEl.innerHTML = title;
38276        // }
38277     //},
38278
38279     getBox : function(){
38280         var b;
38281       //  if(!this.collapsed){
38282             b = this.el.getBox(false, true);
38283        // }else{
38284           //  b = this.collapsedEl.getBox(false, true);
38285         //}
38286         return b;
38287     },
38288
38289     getMargins : function(){
38290         return this.margins;
38291         //return this.collapsed ? this.cmargins : this.margins;
38292     },
38293 /*
38294     highlight : function(){
38295         this.el.addClass("x-layout-panel-dragover");
38296     },
38297
38298     unhighlight : function(){
38299         this.el.removeClass("x-layout-panel-dragover");
38300     },
38301 */
38302     updateBox : function(box)
38303     {
38304         if (!this.bodyEl) {
38305             return; // not rendered yet..
38306         }
38307         
38308         this.box = box;
38309         if(!this.collapsed){
38310             this.el.dom.style.left = box.x + "px";
38311             this.el.dom.style.top = box.y + "px";
38312             this.updateBody(box.width, box.height);
38313         }else{
38314             this.collapsedEl.dom.style.left = box.x + "px";
38315             this.collapsedEl.dom.style.top = box.y + "px";
38316             this.collapsedEl.setSize(box.width, box.height);
38317         }
38318         if(this.tabs){
38319             this.tabs.autoSizeTabs();
38320         }
38321     },
38322
38323     updateBody : function(w, h)
38324     {
38325         if(w !== null){
38326             this.el.setWidth(w);
38327             w -= this.el.getBorderWidth("rl");
38328             if(this.config.adjustments){
38329                 w += this.config.adjustments[0];
38330             }
38331         }
38332         if(h !== null && h > 0){
38333             this.el.setHeight(h);
38334             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38335             h -= this.el.getBorderWidth("tb");
38336             if(this.config.adjustments){
38337                 h += this.config.adjustments[1];
38338             }
38339             this.bodyEl.setHeight(h);
38340             if(this.tabs){
38341                 h = this.tabs.syncHeight(h);
38342             }
38343         }
38344         if(this.panelSize){
38345             w = w !== null ? w : this.panelSize.width;
38346             h = h !== null ? h : this.panelSize.height;
38347         }
38348         if(this.activePanel){
38349             var el = this.activePanel.getEl();
38350             w = w !== null ? w : el.getWidth();
38351             h = h !== null ? h : el.getHeight();
38352             this.panelSize = {width: w, height: h};
38353             this.activePanel.setSize(w, h);
38354         }
38355         if(Roo.isIE && this.tabs){
38356             this.tabs.el.repaint();
38357         }
38358     },
38359
38360     /**
38361      * Returns the container element for this region.
38362      * @return {Roo.Element}
38363      */
38364     getEl : function(){
38365         return this.el;
38366     },
38367
38368     /**
38369      * Hides this region.
38370      */
38371     hide : function(){
38372         //if(!this.collapsed){
38373             this.el.dom.style.left = "-2000px";
38374             this.el.hide();
38375         //}else{
38376          //   this.collapsedEl.dom.style.left = "-2000px";
38377          //   this.collapsedEl.hide();
38378        // }
38379         this.visible = false;
38380         this.fireEvent("visibilitychange", this, false);
38381     },
38382
38383     /**
38384      * Shows this region if it was previously hidden.
38385      */
38386     show : function(){
38387         //if(!this.collapsed){
38388             this.el.show();
38389         //}else{
38390         //    this.collapsedEl.show();
38391        // }
38392         this.visible = true;
38393         this.fireEvent("visibilitychange", this, true);
38394     },
38395 /*
38396     closeClicked : function(){
38397         if(this.activePanel){
38398             this.remove(this.activePanel);
38399         }
38400     },
38401
38402     collapseClick : function(e){
38403         if(this.isSlid){
38404            e.stopPropagation();
38405            this.slideIn();
38406         }else{
38407            e.stopPropagation();
38408            this.slideOut();
38409         }
38410     },
38411 */
38412     /**
38413      * Collapses this region.
38414      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38415      */
38416     /*
38417     collapse : function(skipAnim, skipCheck = false){
38418         if(this.collapsed) {
38419             return;
38420         }
38421         
38422         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38423             
38424             this.collapsed = true;
38425             if(this.split){
38426                 this.split.el.hide();
38427             }
38428             if(this.config.animate && skipAnim !== true){
38429                 this.fireEvent("invalidated", this);
38430                 this.animateCollapse();
38431             }else{
38432                 this.el.setLocation(-20000,-20000);
38433                 this.el.hide();
38434                 this.collapsedEl.show();
38435                 this.fireEvent("collapsed", this);
38436                 this.fireEvent("invalidated", this);
38437             }
38438         }
38439         
38440     },
38441 */
38442     animateCollapse : function(){
38443         // overridden
38444     },
38445
38446     /**
38447      * Expands this region if it was previously collapsed.
38448      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38449      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38450      */
38451     /*
38452     expand : function(e, skipAnim){
38453         if(e) {
38454             e.stopPropagation();
38455         }
38456         if(!this.collapsed || this.el.hasActiveFx()) {
38457             return;
38458         }
38459         if(this.isSlid){
38460             this.afterSlideIn();
38461             skipAnim = true;
38462         }
38463         this.collapsed = false;
38464         if(this.config.animate && skipAnim !== true){
38465             this.animateExpand();
38466         }else{
38467             this.el.show();
38468             if(this.split){
38469                 this.split.el.show();
38470             }
38471             this.collapsedEl.setLocation(-2000,-2000);
38472             this.collapsedEl.hide();
38473             this.fireEvent("invalidated", this);
38474             this.fireEvent("expanded", this);
38475         }
38476     },
38477 */
38478     animateExpand : function(){
38479         // overridden
38480     },
38481
38482     initTabs : function()
38483     {
38484         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38485         
38486         var ts = new Roo.bootstrap.panel.Tabs({
38487             el: this.bodyEl.dom,
38488             region : this,
38489             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38490             disableTooltips: this.config.disableTabTips,
38491             toolbar : this.config.toolbar
38492         });
38493         
38494         if(this.config.hideTabs){
38495             ts.stripWrap.setDisplayed(false);
38496         }
38497         this.tabs = ts;
38498         ts.resizeTabs = this.config.resizeTabs === true;
38499         ts.minTabWidth = this.config.minTabWidth || 40;
38500         ts.maxTabWidth = this.config.maxTabWidth || 250;
38501         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38502         ts.monitorResize = false;
38503         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38504         ts.bodyEl.addClass('roo-layout-tabs-body');
38505         this.panels.each(this.initPanelAsTab, this);
38506     },
38507
38508     initPanelAsTab : function(panel){
38509         var ti = this.tabs.addTab(
38510             panel.getEl().id,
38511             panel.getTitle(),
38512             null,
38513             this.config.closeOnTab && panel.isClosable(),
38514             panel.tpl
38515         );
38516         if(panel.tabTip !== undefined){
38517             ti.setTooltip(panel.tabTip);
38518         }
38519         ti.on("activate", function(){
38520               this.setActivePanel(panel);
38521         }, this);
38522         
38523         if(this.config.closeOnTab){
38524             ti.on("beforeclose", function(t, e){
38525                 e.cancel = true;
38526                 this.remove(panel);
38527             }, this);
38528         }
38529         
38530         panel.tabItem = ti;
38531         
38532         return ti;
38533     },
38534
38535     updatePanelTitle : function(panel, title)
38536     {
38537         if(this.activePanel == panel){
38538             this.updateTitle(title);
38539         }
38540         if(this.tabs){
38541             var ti = this.tabs.getTab(panel.getEl().id);
38542             ti.setText(title);
38543             if(panel.tabTip !== undefined){
38544                 ti.setTooltip(panel.tabTip);
38545             }
38546         }
38547     },
38548
38549     updateTitle : function(title){
38550         if(this.titleTextEl && !this.config.title){
38551             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38552         }
38553     },
38554
38555     setActivePanel : function(panel)
38556     {
38557         panel = this.getPanel(panel);
38558         if(this.activePanel && this.activePanel != panel){
38559             if(this.activePanel.setActiveState(false) === false){
38560                 return;
38561             }
38562         }
38563         this.activePanel = panel;
38564         panel.setActiveState(true);
38565         if(this.panelSize){
38566             panel.setSize(this.panelSize.width, this.panelSize.height);
38567         }
38568         if(this.closeBtn){
38569             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38570         }
38571         this.updateTitle(panel.getTitle());
38572         if(this.tabs){
38573             this.fireEvent("invalidated", this);
38574         }
38575         this.fireEvent("panelactivated", this, panel);
38576     },
38577
38578     /**
38579      * Shows the specified panel.
38580      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38581      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38582      */
38583     showPanel : function(panel)
38584     {
38585         panel = this.getPanel(panel);
38586         if(panel){
38587             if(this.tabs){
38588                 var tab = this.tabs.getTab(panel.getEl().id);
38589                 if(tab.isHidden()){
38590                     this.tabs.unhideTab(tab.id);
38591                 }
38592                 tab.activate();
38593             }else{
38594                 this.setActivePanel(panel);
38595             }
38596         }
38597         return panel;
38598     },
38599
38600     /**
38601      * Get the active panel for this region.
38602      * @return {Roo.ContentPanel} The active panel or null
38603      */
38604     getActivePanel : function(){
38605         return this.activePanel;
38606     },
38607
38608     validateVisibility : function(){
38609         if(this.panels.getCount() < 1){
38610             this.updateTitle("&#160;");
38611             this.closeBtn.hide();
38612             this.hide();
38613         }else{
38614             if(!this.isVisible()){
38615                 this.show();
38616             }
38617         }
38618     },
38619
38620     /**
38621      * Adds the passed ContentPanel(s) to this region.
38622      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38623      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38624      */
38625     add : function(panel)
38626     {
38627         if(arguments.length > 1){
38628             for(var i = 0, len = arguments.length; i < len; i++) {
38629                 this.add(arguments[i]);
38630             }
38631             return null;
38632         }
38633         
38634         // if we have not been rendered yet, then we can not really do much of this..
38635         if (!this.bodyEl) {
38636             this.unrendered_panels.push(panel);
38637             return panel;
38638         }
38639         
38640         
38641         
38642         
38643         if(this.hasPanel(panel)){
38644             this.showPanel(panel);
38645             return panel;
38646         }
38647         panel.setRegion(this);
38648         this.panels.add(panel);
38649        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38650             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38651             // and hide them... ???
38652             this.bodyEl.dom.appendChild(panel.getEl().dom);
38653             if(panel.background !== true){
38654                 this.setActivePanel(panel);
38655             }
38656             this.fireEvent("paneladded", this, panel);
38657             return panel;
38658         }
38659         */
38660         if(!this.tabs){
38661             this.initTabs();
38662         }else{
38663             this.initPanelAsTab(panel);
38664         }
38665         
38666         
38667         if(panel.background !== true){
38668             this.tabs.activate(panel.getEl().id);
38669         }
38670         this.fireEvent("paneladded", this, panel);
38671         return panel;
38672     },
38673
38674     /**
38675      * Hides the tab for the specified panel.
38676      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38677      */
38678     hidePanel : function(panel){
38679         if(this.tabs && (panel = this.getPanel(panel))){
38680             this.tabs.hideTab(panel.getEl().id);
38681         }
38682     },
38683
38684     /**
38685      * Unhides the tab for a previously hidden panel.
38686      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38687      */
38688     unhidePanel : function(panel){
38689         if(this.tabs && (panel = this.getPanel(panel))){
38690             this.tabs.unhideTab(panel.getEl().id);
38691         }
38692     },
38693
38694     clearPanels : function(){
38695         while(this.panels.getCount() > 0){
38696              this.remove(this.panels.first());
38697         }
38698     },
38699
38700     /**
38701      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38702      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38703      * @param {Boolean} preservePanel Overrides the config preservePanel option
38704      * @return {Roo.ContentPanel} The panel that was removed
38705      */
38706     remove : function(panel, preservePanel)
38707     {
38708         panel = this.getPanel(panel);
38709         if(!panel){
38710             return null;
38711         }
38712         var e = {};
38713         this.fireEvent("beforeremove", this, panel, e);
38714         if(e.cancel === true){
38715             return null;
38716         }
38717         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38718         var panelId = panel.getId();
38719         this.panels.removeKey(panelId);
38720         if(preservePanel){
38721             document.body.appendChild(panel.getEl().dom);
38722         }
38723         if(this.tabs){
38724             this.tabs.removeTab(panel.getEl().id);
38725         }else if (!preservePanel){
38726             this.bodyEl.dom.removeChild(panel.getEl().dom);
38727         }
38728         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38729             var p = this.panels.first();
38730             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38731             tempEl.appendChild(p.getEl().dom);
38732             this.bodyEl.update("");
38733             this.bodyEl.dom.appendChild(p.getEl().dom);
38734             tempEl = null;
38735             this.updateTitle(p.getTitle());
38736             this.tabs = null;
38737             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38738             this.setActivePanel(p);
38739         }
38740         panel.setRegion(null);
38741         if(this.activePanel == panel){
38742             this.activePanel = null;
38743         }
38744         if(this.config.autoDestroy !== false && preservePanel !== true){
38745             try{panel.destroy();}catch(e){}
38746         }
38747         this.fireEvent("panelremoved", this, panel);
38748         return panel;
38749     },
38750
38751     /**
38752      * Returns the TabPanel component used by this region
38753      * @return {Roo.TabPanel}
38754      */
38755     getTabs : function(){
38756         return this.tabs;
38757     },
38758
38759     createTool : function(parentEl, className){
38760         var btn = Roo.DomHelper.append(parentEl, {
38761             tag: "div",
38762             cls: "x-layout-tools-button",
38763             children: [ {
38764                 tag: "div",
38765                 cls: "roo-layout-tools-button-inner " + className,
38766                 html: "&#160;"
38767             }]
38768         }, true);
38769         btn.addClassOnOver("roo-layout-tools-button-over");
38770         return btn;
38771     }
38772 });/*
38773  * Based on:
38774  * Ext JS Library 1.1.1
38775  * Copyright(c) 2006-2007, Ext JS, LLC.
38776  *
38777  * Originally Released Under LGPL - original licence link has changed is not relivant.
38778  *
38779  * Fork - LGPL
38780  * <script type="text/javascript">
38781  */
38782  
38783
38784
38785 /**
38786  * @class Roo.SplitLayoutRegion
38787  * @extends Roo.LayoutRegion
38788  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38789  */
38790 Roo.bootstrap.layout.Split = function(config){
38791     this.cursor = config.cursor;
38792     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38793 };
38794
38795 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38796 {
38797     splitTip : "Drag to resize.",
38798     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38799     useSplitTips : false,
38800
38801     applyConfig : function(config){
38802         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38803     },
38804     
38805     onRender : function(ctr,pos) {
38806         
38807         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38808         if(!this.config.split){
38809             return;
38810         }
38811         if(!this.split){
38812             
38813             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38814                             tag: "div",
38815                             id: this.el.id + "-split",
38816                             cls: "roo-layout-split roo-layout-split-"+this.position,
38817                             html: "&#160;"
38818             });
38819             /** The SplitBar for this region 
38820             * @type Roo.SplitBar */
38821             // does not exist yet...
38822             Roo.log([this.position, this.orientation]);
38823             
38824             this.split = new Roo.bootstrap.SplitBar({
38825                 dragElement : splitEl,
38826                 resizingElement: this.el,
38827                 orientation : this.orientation
38828             });
38829             
38830             this.split.on("moved", this.onSplitMove, this);
38831             this.split.useShim = this.config.useShim === true;
38832             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38833             if(this.useSplitTips){
38834                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38835             }
38836             //if(config.collapsible){
38837             //    this.split.el.on("dblclick", this.collapse,  this);
38838             //}
38839         }
38840         if(typeof this.config.minSize != "undefined"){
38841             this.split.minSize = this.config.minSize;
38842         }
38843         if(typeof this.config.maxSize != "undefined"){
38844             this.split.maxSize = this.config.maxSize;
38845         }
38846         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38847             this.hideSplitter();
38848         }
38849         
38850     },
38851
38852     getHMaxSize : function(){
38853          var cmax = this.config.maxSize || 10000;
38854          var center = this.mgr.getRegion("center");
38855          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38856     },
38857
38858     getVMaxSize : function(){
38859          var cmax = this.config.maxSize || 10000;
38860          var center = this.mgr.getRegion("center");
38861          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38862     },
38863
38864     onSplitMove : function(split, newSize){
38865         this.fireEvent("resized", this, newSize);
38866     },
38867     
38868     /** 
38869      * Returns the {@link Roo.SplitBar} for this region.
38870      * @return {Roo.SplitBar}
38871      */
38872     getSplitBar : function(){
38873         return this.split;
38874     },
38875     
38876     hide : function(){
38877         this.hideSplitter();
38878         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38879     },
38880
38881     hideSplitter : function(){
38882         if(this.split){
38883             this.split.el.setLocation(-2000,-2000);
38884             this.split.el.hide();
38885         }
38886     },
38887
38888     show : function(){
38889         if(this.split){
38890             this.split.el.show();
38891         }
38892         Roo.bootstrap.layout.Split.superclass.show.call(this);
38893     },
38894     
38895     beforeSlide: function(){
38896         if(Roo.isGecko){// firefox overflow auto bug workaround
38897             this.bodyEl.clip();
38898             if(this.tabs) {
38899                 this.tabs.bodyEl.clip();
38900             }
38901             if(this.activePanel){
38902                 this.activePanel.getEl().clip();
38903                 
38904                 if(this.activePanel.beforeSlide){
38905                     this.activePanel.beforeSlide();
38906                 }
38907             }
38908         }
38909     },
38910     
38911     afterSlide : function(){
38912         if(Roo.isGecko){// firefox overflow auto bug workaround
38913             this.bodyEl.unclip();
38914             if(this.tabs) {
38915                 this.tabs.bodyEl.unclip();
38916             }
38917             if(this.activePanel){
38918                 this.activePanel.getEl().unclip();
38919                 if(this.activePanel.afterSlide){
38920                     this.activePanel.afterSlide();
38921                 }
38922             }
38923         }
38924     },
38925
38926     initAutoHide : function(){
38927         if(this.autoHide !== false){
38928             if(!this.autoHideHd){
38929                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38930                 this.autoHideHd = {
38931                     "mouseout": function(e){
38932                         if(!e.within(this.el, true)){
38933                             st.delay(500);
38934                         }
38935                     },
38936                     "mouseover" : function(e){
38937                         st.cancel();
38938                     },
38939                     scope : this
38940                 };
38941             }
38942             this.el.on(this.autoHideHd);
38943         }
38944     },
38945
38946     clearAutoHide : function(){
38947         if(this.autoHide !== false){
38948             this.el.un("mouseout", this.autoHideHd.mouseout);
38949             this.el.un("mouseover", this.autoHideHd.mouseover);
38950         }
38951     },
38952
38953     clearMonitor : function(){
38954         Roo.get(document).un("click", this.slideInIf, this);
38955     },
38956
38957     // these names are backwards but not changed for compat
38958     slideOut : function(){
38959         if(this.isSlid || this.el.hasActiveFx()){
38960             return;
38961         }
38962         this.isSlid = true;
38963         if(this.collapseBtn){
38964             this.collapseBtn.hide();
38965         }
38966         this.closeBtnState = this.closeBtn.getStyle('display');
38967         this.closeBtn.hide();
38968         if(this.stickBtn){
38969             this.stickBtn.show();
38970         }
38971         this.el.show();
38972         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38973         this.beforeSlide();
38974         this.el.setStyle("z-index", 10001);
38975         this.el.slideIn(this.getSlideAnchor(), {
38976             callback: function(){
38977                 this.afterSlide();
38978                 this.initAutoHide();
38979                 Roo.get(document).on("click", this.slideInIf, this);
38980                 this.fireEvent("slideshow", this);
38981             },
38982             scope: this,
38983             block: true
38984         });
38985     },
38986
38987     afterSlideIn : function(){
38988         this.clearAutoHide();
38989         this.isSlid = false;
38990         this.clearMonitor();
38991         this.el.setStyle("z-index", "");
38992         if(this.collapseBtn){
38993             this.collapseBtn.show();
38994         }
38995         this.closeBtn.setStyle('display', this.closeBtnState);
38996         if(this.stickBtn){
38997             this.stickBtn.hide();
38998         }
38999         this.fireEvent("slidehide", this);
39000     },
39001
39002     slideIn : function(cb){
39003         if(!this.isSlid || this.el.hasActiveFx()){
39004             Roo.callback(cb);
39005             return;
39006         }
39007         this.isSlid = false;
39008         this.beforeSlide();
39009         this.el.slideOut(this.getSlideAnchor(), {
39010             callback: function(){
39011                 this.el.setLeftTop(-10000, -10000);
39012                 this.afterSlide();
39013                 this.afterSlideIn();
39014                 Roo.callback(cb);
39015             },
39016             scope: this,
39017             block: true
39018         });
39019     },
39020     
39021     slideInIf : function(e){
39022         if(!e.within(this.el)){
39023             this.slideIn();
39024         }
39025     },
39026
39027     animateCollapse : function(){
39028         this.beforeSlide();
39029         this.el.setStyle("z-index", 20000);
39030         var anchor = this.getSlideAnchor();
39031         this.el.slideOut(anchor, {
39032             callback : function(){
39033                 this.el.setStyle("z-index", "");
39034                 this.collapsedEl.slideIn(anchor, {duration:.3});
39035                 this.afterSlide();
39036                 this.el.setLocation(-10000,-10000);
39037                 this.el.hide();
39038                 this.fireEvent("collapsed", this);
39039             },
39040             scope: this,
39041             block: true
39042         });
39043     },
39044
39045     animateExpand : function(){
39046         this.beforeSlide();
39047         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39048         this.el.setStyle("z-index", 20000);
39049         this.collapsedEl.hide({
39050             duration:.1
39051         });
39052         this.el.slideIn(this.getSlideAnchor(), {
39053             callback : function(){
39054                 this.el.setStyle("z-index", "");
39055                 this.afterSlide();
39056                 if(this.split){
39057                     this.split.el.show();
39058                 }
39059                 this.fireEvent("invalidated", this);
39060                 this.fireEvent("expanded", this);
39061             },
39062             scope: this,
39063             block: true
39064         });
39065     },
39066
39067     anchors : {
39068         "west" : "left",
39069         "east" : "right",
39070         "north" : "top",
39071         "south" : "bottom"
39072     },
39073
39074     sanchors : {
39075         "west" : "l",
39076         "east" : "r",
39077         "north" : "t",
39078         "south" : "b"
39079     },
39080
39081     canchors : {
39082         "west" : "tl-tr",
39083         "east" : "tr-tl",
39084         "north" : "tl-bl",
39085         "south" : "bl-tl"
39086     },
39087
39088     getAnchor : function(){
39089         return this.anchors[this.position];
39090     },
39091
39092     getCollapseAnchor : function(){
39093         return this.canchors[this.position];
39094     },
39095
39096     getSlideAnchor : function(){
39097         return this.sanchors[this.position];
39098     },
39099
39100     getAlignAdj : function(){
39101         var cm = this.cmargins;
39102         switch(this.position){
39103             case "west":
39104                 return [0, 0];
39105             break;
39106             case "east":
39107                 return [0, 0];
39108             break;
39109             case "north":
39110                 return [0, 0];
39111             break;
39112             case "south":
39113                 return [0, 0];
39114             break;
39115         }
39116     },
39117
39118     getExpandAdj : function(){
39119         var c = this.collapsedEl, cm = this.cmargins;
39120         switch(this.position){
39121             case "west":
39122                 return [-(cm.right+c.getWidth()+cm.left), 0];
39123             break;
39124             case "east":
39125                 return [cm.right+c.getWidth()+cm.left, 0];
39126             break;
39127             case "north":
39128                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39129             break;
39130             case "south":
39131                 return [0, cm.top+cm.bottom+c.getHeight()];
39132             break;
39133         }
39134     }
39135 });/*
39136  * Based on:
39137  * Ext JS Library 1.1.1
39138  * Copyright(c) 2006-2007, Ext JS, LLC.
39139  *
39140  * Originally Released Under LGPL - original licence link has changed is not relivant.
39141  *
39142  * Fork - LGPL
39143  * <script type="text/javascript">
39144  */
39145 /*
39146  * These classes are private internal classes
39147  */
39148 Roo.bootstrap.layout.Center = function(config){
39149     config.region = "center";
39150     Roo.bootstrap.layout.Region.call(this, config);
39151     this.visible = true;
39152     this.minWidth = config.minWidth || 20;
39153     this.minHeight = config.minHeight || 20;
39154 };
39155
39156 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39157     hide : function(){
39158         // center panel can't be hidden
39159     },
39160     
39161     show : function(){
39162         // center panel can't be hidden
39163     },
39164     
39165     getMinWidth: function(){
39166         return this.minWidth;
39167     },
39168     
39169     getMinHeight: function(){
39170         return this.minHeight;
39171     }
39172 });
39173
39174
39175
39176
39177  
39178
39179
39180
39181
39182
39183
39184 Roo.bootstrap.layout.North = function(config)
39185 {
39186     config.region = 'north';
39187     config.cursor = 'n-resize';
39188     
39189     Roo.bootstrap.layout.Split.call(this, config);
39190     
39191     
39192     if(this.split){
39193         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39194         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39195         this.split.el.addClass("roo-layout-split-v");
39196     }
39197     var size = config.initialSize || config.height;
39198     if(typeof size != "undefined"){
39199         this.el.setHeight(size);
39200     }
39201 };
39202 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39203 {
39204     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39205     
39206     
39207     
39208     getBox : function(){
39209         if(this.collapsed){
39210             return this.collapsedEl.getBox();
39211         }
39212         var box = this.el.getBox();
39213         if(this.split){
39214             box.height += this.split.el.getHeight();
39215         }
39216         return box;
39217     },
39218     
39219     updateBox : function(box){
39220         if(this.split && !this.collapsed){
39221             box.height -= this.split.el.getHeight();
39222             this.split.el.setLeft(box.x);
39223             this.split.el.setTop(box.y+box.height);
39224             this.split.el.setWidth(box.width);
39225         }
39226         if(this.collapsed){
39227             this.updateBody(box.width, null);
39228         }
39229         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39230     }
39231 });
39232
39233
39234
39235
39236
39237 Roo.bootstrap.layout.South = function(config){
39238     config.region = 'south';
39239     config.cursor = 's-resize';
39240     Roo.bootstrap.layout.Split.call(this, config);
39241     if(this.split){
39242         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39243         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39244         this.split.el.addClass("roo-layout-split-v");
39245     }
39246     var size = config.initialSize || config.height;
39247     if(typeof size != "undefined"){
39248         this.el.setHeight(size);
39249     }
39250 };
39251
39252 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39253     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39254     getBox : function(){
39255         if(this.collapsed){
39256             return this.collapsedEl.getBox();
39257         }
39258         var box = this.el.getBox();
39259         if(this.split){
39260             var sh = this.split.el.getHeight();
39261             box.height += sh;
39262             box.y -= sh;
39263         }
39264         return box;
39265     },
39266     
39267     updateBox : function(box){
39268         if(this.split && !this.collapsed){
39269             var sh = this.split.el.getHeight();
39270             box.height -= sh;
39271             box.y += sh;
39272             this.split.el.setLeft(box.x);
39273             this.split.el.setTop(box.y-sh);
39274             this.split.el.setWidth(box.width);
39275         }
39276         if(this.collapsed){
39277             this.updateBody(box.width, null);
39278         }
39279         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39280     }
39281 });
39282
39283 Roo.bootstrap.layout.East = function(config){
39284     config.region = "east";
39285     config.cursor = "e-resize";
39286     Roo.bootstrap.layout.Split.call(this, config);
39287     if(this.split){
39288         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39289         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39290         this.split.el.addClass("roo-layout-split-h");
39291     }
39292     var size = config.initialSize || config.width;
39293     if(typeof size != "undefined"){
39294         this.el.setWidth(size);
39295     }
39296 };
39297 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39298     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39299     getBox : function(){
39300         if(this.collapsed){
39301             return this.collapsedEl.getBox();
39302         }
39303         var box = this.el.getBox();
39304         if(this.split){
39305             var sw = this.split.el.getWidth();
39306             box.width += sw;
39307             box.x -= sw;
39308         }
39309         return box;
39310     },
39311
39312     updateBox : function(box){
39313         if(this.split && !this.collapsed){
39314             var sw = this.split.el.getWidth();
39315             box.width -= sw;
39316             this.split.el.setLeft(box.x);
39317             this.split.el.setTop(box.y);
39318             this.split.el.setHeight(box.height);
39319             box.x += sw;
39320         }
39321         if(this.collapsed){
39322             this.updateBody(null, box.height);
39323         }
39324         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39325     }
39326 });
39327
39328 Roo.bootstrap.layout.West = function(config){
39329     config.region = "west";
39330     config.cursor = "w-resize";
39331     
39332     Roo.bootstrap.layout.Split.call(this, config);
39333     if(this.split){
39334         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39335         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39336         this.split.el.addClass("roo-layout-split-h");
39337     }
39338     
39339 };
39340 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39341     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39342     
39343     onRender: function(ctr, pos)
39344     {
39345         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39346         var size = this.config.initialSize || this.config.width;
39347         if(typeof size != "undefined"){
39348             this.el.setWidth(size);
39349         }
39350     },
39351     
39352     getBox : function(){
39353         if(this.collapsed){
39354             return this.collapsedEl.getBox();
39355         }
39356         var box = this.el.getBox();
39357         if(this.split){
39358             box.width += this.split.el.getWidth();
39359         }
39360         return box;
39361     },
39362     
39363     updateBox : function(box){
39364         if(this.split && !this.collapsed){
39365             var sw = this.split.el.getWidth();
39366             box.width -= sw;
39367             this.split.el.setLeft(box.x+box.width);
39368             this.split.el.setTop(box.y);
39369             this.split.el.setHeight(box.height);
39370         }
39371         if(this.collapsed){
39372             this.updateBody(null, box.height);
39373         }
39374         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39375     }
39376 });Roo.namespace("Roo.bootstrap.panel");/*
39377  * Based on:
39378  * Ext JS Library 1.1.1
39379  * Copyright(c) 2006-2007, Ext JS, LLC.
39380  *
39381  * Originally Released Under LGPL - original licence link has changed is not relivant.
39382  *
39383  * Fork - LGPL
39384  * <script type="text/javascript">
39385  */
39386 /**
39387  * @class Roo.ContentPanel
39388  * @extends Roo.util.Observable
39389  * A basic ContentPanel element.
39390  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39391  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39392  * @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
39393  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39394  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39395  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39396  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39397  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39398  * @cfg {String} title          The title for this panel
39399  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39400  * @cfg {String} url            Calls {@link #setUrl} with this value
39401  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39402  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39403  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39404  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39405  * @cfg {Boolean} badges render the badges
39406  * @cfg {String} cls  extra classes to use  
39407  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39408
39409  * @constructor
39410  * Create a new ContentPanel.
39411  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39412  * @param {String/Object} config A string to set only the title or a config object
39413  * @param {String} content (optional) Set the HTML content for this panel
39414  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39415  */
39416 Roo.bootstrap.panel.Content = function( config){
39417     
39418     this.tpl = config.tpl || false;
39419     
39420     var el = config.el;
39421     var content = config.content;
39422
39423     if(config.autoCreate){ // xtype is available if this is called from factory
39424         el = Roo.id();
39425     }
39426     this.el = Roo.get(el);
39427     if(!this.el && config && config.autoCreate){
39428         if(typeof config.autoCreate == "object"){
39429             if(!config.autoCreate.id){
39430                 config.autoCreate.id = config.id||el;
39431             }
39432             this.el = Roo.DomHelper.append(document.body,
39433                         config.autoCreate, true);
39434         }else{
39435             var elcfg =  {
39436                 tag: "div",
39437                 cls: (config.cls || '') +
39438                     (config.background ? ' bg-' + config.background : '') +
39439                     " roo-layout-inactive-content",
39440                 id: config.id||el
39441             };
39442             if (config.html) {
39443                 elcfg.html = config.html;
39444                 
39445             }
39446                         
39447             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39448         }
39449     } 
39450     this.closable = false;
39451     this.loaded = false;
39452     this.active = false;
39453    
39454       
39455     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39456         
39457         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39458         
39459         this.wrapEl = this.el; //this.el.wrap();
39460         var ti = [];
39461         if (config.toolbar.items) {
39462             ti = config.toolbar.items ;
39463             delete config.toolbar.items ;
39464         }
39465         
39466         var nitems = [];
39467         this.toolbar.render(this.wrapEl, 'before');
39468         for(var i =0;i < ti.length;i++) {
39469           //  Roo.log(['add child', items[i]]);
39470             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39471         }
39472         this.toolbar.items = nitems;
39473         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39474         delete config.toolbar;
39475         
39476     }
39477     /*
39478     // xtype created footer. - not sure if will work as we normally have to render first..
39479     if (this.footer && !this.footer.el && this.footer.xtype) {
39480         if (!this.wrapEl) {
39481             this.wrapEl = this.el.wrap();
39482         }
39483     
39484         this.footer.container = this.wrapEl.createChild();
39485          
39486         this.footer = Roo.factory(this.footer, Roo);
39487         
39488     }
39489     */
39490     
39491      if(typeof config == "string"){
39492         this.title = config;
39493     }else{
39494         Roo.apply(this, config);
39495     }
39496     
39497     if(this.resizeEl){
39498         this.resizeEl = Roo.get(this.resizeEl, true);
39499     }else{
39500         this.resizeEl = this.el;
39501     }
39502     // handle view.xtype
39503     
39504  
39505     
39506     
39507     this.addEvents({
39508         /**
39509          * @event activate
39510          * Fires when this panel is activated. 
39511          * @param {Roo.ContentPanel} this
39512          */
39513         "activate" : true,
39514         /**
39515          * @event deactivate
39516          * Fires when this panel is activated. 
39517          * @param {Roo.ContentPanel} this
39518          */
39519         "deactivate" : true,
39520
39521         /**
39522          * @event resize
39523          * Fires when this panel is resized if fitToFrame is true.
39524          * @param {Roo.ContentPanel} this
39525          * @param {Number} width The width after any component adjustments
39526          * @param {Number} height The height after any component adjustments
39527          */
39528         "resize" : true,
39529         
39530          /**
39531          * @event render
39532          * Fires when this tab is created
39533          * @param {Roo.ContentPanel} this
39534          */
39535         "render" : true
39536         
39537         
39538         
39539     });
39540     
39541
39542     
39543     
39544     if(this.autoScroll){
39545         this.resizeEl.setStyle("overflow", "auto");
39546     } else {
39547         // fix randome scrolling
39548         //this.el.on('scroll', function() {
39549         //    Roo.log('fix random scolling');
39550         //    this.scrollTo('top',0); 
39551         //});
39552     }
39553     content = content || this.content;
39554     if(content){
39555         this.setContent(content);
39556     }
39557     if(config && config.url){
39558         this.setUrl(this.url, this.params, this.loadOnce);
39559     }
39560     
39561     
39562     
39563     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39564     
39565     if (this.view && typeof(this.view.xtype) != 'undefined') {
39566         this.view.el = this.el.appendChild(document.createElement("div"));
39567         this.view = Roo.factory(this.view); 
39568         this.view.render  &&  this.view.render(false, '');  
39569     }
39570     
39571     
39572     this.fireEvent('render', this);
39573 };
39574
39575 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39576     
39577     cls : '',
39578     background : '',
39579     
39580     tabTip : '',
39581     
39582     setRegion : function(region){
39583         this.region = region;
39584         this.setActiveClass(region && !this.background);
39585     },
39586     
39587     
39588     setActiveClass: function(state)
39589     {
39590         if(state){
39591            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39592            this.el.setStyle('position','relative');
39593         }else{
39594            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39595            this.el.setStyle('position', 'absolute');
39596         } 
39597     },
39598     
39599     /**
39600      * Returns the toolbar for this Panel if one was configured. 
39601      * @return {Roo.Toolbar} 
39602      */
39603     getToolbar : function(){
39604         return this.toolbar;
39605     },
39606     
39607     setActiveState : function(active)
39608     {
39609         this.active = active;
39610         this.setActiveClass(active);
39611         if(!active){
39612             if(this.fireEvent("deactivate", this) === false){
39613                 return false;
39614             }
39615             return true;
39616         }
39617         this.fireEvent("activate", this);
39618         return true;
39619     },
39620     /**
39621      * Updates this panel's element
39622      * @param {String} content The new content
39623      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39624     */
39625     setContent : function(content, loadScripts){
39626         this.el.update(content, loadScripts);
39627     },
39628
39629     ignoreResize : function(w, h){
39630         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39631             return true;
39632         }else{
39633             this.lastSize = {width: w, height: h};
39634             return false;
39635         }
39636     },
39637     /**
39638      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39639      * @return {Roo.UpdateManager} The UpdateManager
39640      */
39641     getUpdateManager : function(){
39642         return this.el.getUpdateManager();
39643     },
39644      /**
39645      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39646      * @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:
39647 <pre><code>
39648 panel.load({
39649     url: "your-url.php",
39650     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39651     callback: yourFunction,
39652     scope: yourObject, //(optional scope)
39653     discardUrl: false,
39654     nocache: false,
39655     text: "Loading...",
39656     timeout: 30,
39657     scripts: false
39658 });
39659 </code></pre>
39660      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39661      * 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.
39662      * @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}
39663      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39664      * @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.
39665      * @return {Roo.ContentPanel} this
39666      */
39667     load : function(){
39668         var um = this.el.getUpdateManager();
39669         um.update.apply(um, arguments);
39670         return this;
39671     },
39672
39673
39674     /**
39675      * 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.
39676      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39677      * @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)
39678      * @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)
39679      * @return {Roo.UpdateManager} The UpdateManager
39680      */
39681     setUrl : function(url, params, loadOnce){
39682         if(this.refreshDelegate){
39683             this.removeListener("activate", this.refreshDelegate);
39684         }
39685         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39686         this.on("activate", this.refreshDelegate);
39687         return this.el.getUpdateManager();
39688     },
39689     
39690     _handleRefresh : function(url, params, loadOnce){
39691         if(!loadOnce || !this.loaded){
39692             var updater = this.el.getUpdateManager();
39693             updater.update(url, params, this._setLoaded.createDelegate(this));
39694         }
39695     },
39696     
39697     _setLoaded : function(){
39698         this.loaded = true;
39699     }, 
39700     
39701     /**
39702      * Returns this panel's id
39703      * @return {String} 
39704      */
39705     getId : function(){
39706         return this.el.id;
39707     },
39708     
39709     /** 
39710      * Returns this panel's element - used by regiosn to add.
39711      * @return {Roo.Element} 
39712      */
39713     getEl : function(){
39714         return this.wrapEl || this.el;
39715     },
39716     
39717    
39718     
39719     adjustForComponents : function(width, height)
39720     {
39721         //Roo.log('adjustForComponents ');
39722         if(this.resizeEl != this.el){
39723             width -= this.el.getFrameWidth('lr');
39724             height -= this.el.getFrameWidth('tb');
39725         }
39726         if(this.toolbar){
39727             var te = this.toolbar.getEl();
39728             te.setWidth(width);
39729             height -= te.getHeight();
39730         }
39731         if(this.footer){
39732             var te = this.footer.getEl();
39733             te.setWidth(width);
39734             height -= te.getHeight();
39735         }
39736         
39737         
39738         if(this.adjustments){
39739             width += this.adjustments[0];
39740             height += this.adjustments[1];
39741         }
39742         return {"width": width, "height": height};
39743     },
39744     
39745     setSize : function(width, height){
39746         if(this.fitToFrame && !this.ignoreResize(width, height)){
39747             if(this.fitContainer && this.resizeEl != this.el){
39748                 this.el.setSize(width, height);
39749             }
39750             var size = this.adjustForComponents(width, height);
39751             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39752             this.fireEvent('resize', this, size.width, size.height);
39753         }
39754     },
39755     
39756     /**
39757      * Returns this panel's title
39758      * @return {String} 
39759      */
39760     getTitle : function(){
39761         
39762         if (typeof(this.title) != 'object') {
39763             return this.title;
39764         }
39765         
39766         var t = '';
39767         for (var k in this.title) {
39768             if (!this.title.hasOwnProperty(k)) {
39769                 continue;
39770             }
39771             
39772             if (k.indexOf('-') >= 0) {
39773                 var s = k.split('-');
39774                 for (var i = 0; i<s.length; i++) {
39775                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39776                 }
39777             } else {
39778                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39779             }
39780         }
39781         return t;
39782     },
39783     
39784     /**
39785      * Set this panel's title
39786      * @param {String} title
39787      */
39788     setTitle : function(title){
39789         this.title = title;
39790         if(this.region){
39791             this.region.updatePanelTitle(this, title);
39792         }
39793     },
39794     
39795     /**
39796      * Returns true is this panel was configured to be closable
39797      * @return {Boolean} 
39798      */
39799     isClosable : function(){
39800         return this.closable;
39801     },
39802     
39803     beforeSlide : function(){
39804         this.el.clip();
39805         this.resizeEl.clip();
39806     },
39807     
39808     afterSlide : function(){
39809         this.el.unclip();
39810         this.resizeEl.unclip();
39811     },
39812     
39813     /**
39814      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39815      *   Will fail silently if the {@link #setUrl} method has not been called.
39816      *   This does not activate the panel, just updates its content.
39817      */
39818     refresh : function(){
39819         if(this.refreshDelegate){
39820            this.loaded = false;
39821            this.refreshDelegate();
39822         }
39823     },
39824     
39825     /**
39826      * Destroys this panel
39827      */
39828     destroy : function(){
39829         this.el.removeAllListeners();
39830         var tempEl = document.createElement("span");
39831         tempEl.appendChild(this.el.dom);
39832         tempEl.innerHTML = "";
39833         this.el.remove();
39834         this.el = null;
39835     },
39836     
39837     /**
39838      * form - if the content panel contains a form - this is a reference to it.
39839      * @type {Roo.form.Form}
39840      */
39841     form : false,
39842     /**
39843      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39844      *    This contains a reference to it.
39845      * @type {Roo.View}
39846      */
39847     view : false,
39848     
39849       /**
39850      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39851      * <pre><code>
39852
39853 layout.addxtype({
39854        xtype : 'Form',
39855        items: [ .... ]
39856    }
39857 );
39858
39859 </code></pre>
39860      * @param {Object} cfg Xtype definition of item to add.
39861      */
39862     
39863     
39864     getChildContainer: function () {
39865         return this.getEl();
39866     }
39867     
39868     
39869     /*
39870         var  ret = new Roo.factory(cfg);
39871         return ret;
39872         
39873         
39874         // add form..
39875         if (cfg.xtype.match(/^Form$/)) {
39876             
39877             var el;
39878             //if (this.footer) {
39879             //    el = this.footer.container.insertSibling(false, 'before');
39880             //} else {
39881                 el = this.el.createChild();
39882             //}
39883
39884             this.form = new  Roo.form.Form(cfg);
39885             
39886             
39887             if ( this.form.allItems.length) {
39888                 this.form.render(el.dom);
39889             }
39890             return this.form;
39891         }
39892         // should only have one of theses..
39893         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39894             // views.. should not be just added - used named prop 'view''
39895             
39896             cfg.el = this.el.appendChild(document.createElement("div"));
39897             // factory?
39898             
39899             var ret = new Roo.factory(cfg);
39900              
39901              ret.render && ret.render(false, ''); // render blank..
39902             this.view = ret;
39903             return ret;
39904         }
39905         return false;
39906     }
39907     \*/
39908 });
39909  
39910 /**
39911  * @class Roo.bootstrap.panel.Grid
39912  * @extends Roo.bootstrap.panel.Content
39913  * @constructor
39914  * Create a new GridPanel.
39915  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39916  * @param {Object} config A the config object
39917   
39918  */
39919
39920
39921
39922 Roo.bootstrap.panel.Grid = function(config)
39923 {
39924     
39925       
39926     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39927         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39928
39929     config.el = this.wrapper;
39930     //this.el = this.wrapper;
39931     
39932       if (config.container) {
39933         // ctor'ed from a Border/panel.grid
39934         
39935         
39936         this.wrapper.setStyle("overflow", "hidden");
39937         this.wrapper.addClass('roo-grid-container');
39938
39939     }
39940     
39941     
39942     if(config.toolbar){
39943         var tool_el = this.wrapper.createChild();    
39944         this.toolbar = Roo.factory(config.toolbar);
39945         var ti = [];
39946         if (config.toolbar.items) {
39947             ti = config.toolbar.items ;
39948             delete config.toolbar.items ;
39949         }
39950         
39951         var nitems = [];
39952         this.toolbar.render(tool_el);
39953         for(var i =0;i < ti.length;i++) {
39954           //  Roo.log(['add child', items[i]]);
39955             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39956         }
39957         this.toolbar.items = nitems;
39958         
39959         delete config.toolbar;
39960     }
39961     
39962     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39963     config.grid.scrollBody = true;;
39964     config.grid.monitorWindowResize = false; // turn off autosizing
39965     config.grid.autoHeight = false;
39966     config.grid.autoWidth = false;
39967     
39968     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39969     
39970     if (config.background) {
39971         // render grid on panel activation (if panel background)
39972         this.on('activate', function(gp) {
39973             if (!gp.grid.rendered) {
39974                 gp.grid.render(this.wrapper);
39975                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39976             }
39977         });
39978             
39979     } else {
39980         this.grid.render(this.wrapper);
39981         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39982
39983     }
39984     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39985     // ??? needed ??? config.el = this.wrapper;
39986     
39987     
39988     
39989   
39990     // xtype created footer. - not sure if will work as we normally have to render first..
39991     if (this.footer && !this.footer.el && this.footer.xtype) {
39992         
39993         var ctr = this.grid.getView().getFooterPanel(true);
39994         this.footer.dataSource = this.grid.dataSource;
39995         this.footer = Roo.factory(this.footer, Roo);
39996         this.footer.render(ctr);
39997         
39998     }
39999     
40000     
40001     
40002     
40003      
40004 };
40005
40006 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40007     getId : function(){
40008         return this.grid.id;
40009     },
40010     
40011     /**
40012      * Returns the grid for this panel
40013      * @return {Roo.bootstrap.Table} 
40014      */
40015     getGrid : function(){
40016         return this.grid;    
40017     },
40018     
40019     setSize : function(width, height){
40020         if(!this.ignoreResize(width, height)){
40021             var grid = this.grid;
40022             var size = this.adjustForComponents(width, height);
40023             // tfoot is not a footer?
40024           
40025             
40026             var gridel = grid.getGridEl();
40027             gridel.setSize(size.width, size.height);
40028             
40029             var tbd = grid.getGridEl().select('tbody', true).first();
40030             var thd = grid.getGridEl().select('thead',true).first();
40031             var tbf= grid.getGridEl().select('tfoot', true).first();
40032
40033             if (tbf) {
40034                 size.height -= thd.getHeight();
40035             }
40036             if (thd) {
40037                 size.height -= thd.getHeight();
40038             }
40039             
40040             tbd.setSize(size.width, size.height );
40041             // this is for the account management tab -seems to work there.
40042             var thd = grid.getGridEl().select('thead',true).first();
40043             //if (tbd) {
40044             //    tbd.setSize(size.width, size.height - thd.getHeight());
40045             //}
40046              
40047             grid.autoSize();
40048         }
40049     },
40050      
40051     
40052     
40053     beforeSlide : function(){
40054         this.grid.getView().scroller.clip();
40055     },
40056     
40057     afterSlide : function(){
40058         this.grid.getView().scroller.unclip();
40059     },
40060     
40061     destroy : function(){
40062         this.grid.destroy();
40063         delete this.grid;
40064         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40065     }
40066 });
40067
40068 /**
40069  * @class Roo.bootstrap.panel.Nest
40070  * @extends Roo.bootstrap.panel.Content
40071  * @constructor
40072  * Create a new Panel, that can contain a layout.Border.
40073  * 
40074  * 
40075  * @param {Roo.BorderLayout} layout The layout for this panel
40076  * @param {String/Object} config A string to set only the title or a config object
40077  */
40078 Roo.bootstrap.panel.Nest = function(config)
40079 {
40080     // construct with only one argument..
40081     /* FIXME - implement nicer consturctors
40082     if (layout.layout) {
40083         config = layout;
40084         layout = config.layout;
40085         delete config.layout;
40086     }
40087     if (layout.xtype && !layout.getEl) {
40088         // then layout needs constructing..
40089         layout = Roo.factory(layout, Roo);
40090     }
40091     */
40092     
40093     config.el =  config.layout.getEl();
40094     
40095     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40096     
40097     config.layout.monitorWindowResize = false; // turn off autosizing
40098     this.layout = config.layout;
40099     this.layout.getEl().addClass("roo-layout-nested-layout");
40100     this.layout.parent = this;
40101     
40102     
40103     
40104     
40105 };
40106
40107 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40108
40109     setSize : function(width, height){
40110         if(!this.ignoreResize(width, height)){
40111             var size = this.adjustForComponents(width, height);
40112             var el = this.layout.getEl();
40113             if (size.height < 1) {
40114                 el.setWidth(size.width);   
40115             } else {
40116                 el.setSize(size.width, size.height);
40117             }
40118             var touch = el.dom.offsetWidth;
40119             this.layout.layout();
40120             // ie requires a double layout on the first pass
40121             if(Roo.isIE && !this.initialized){
40122                 this.initialized = true;
40123                 this.layout.layout();
40124             }
40125         }
40126     },
40127     
40128     // activate all subpanels if not currently active..
40129     
40130     setActiveState : function(active){
40131         this.active = active;
40132         this.setActiveClass(active);
40133         
40134         if(!active){
40135             this.fireEvent("deactivate", this);
40136             return;
40137         }
40138         
40139         this.fireEvent("activate", this);
40140         // not sure if this should happen before or after..
40141         if (!this.layout) {
40142             return; // should not happen..
40143         }
40144         var reg = false;
40145         for (var r in this.layout.regions) {
40146             reg = this.layout.getRegion(r);
40147             if (reg.getActivePanel()) {
40148                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40149                 reg.setActivePanel(reg.getActivePanel());
40150                 continue;
40151             }
40152             if (!reg.panels.length) {
40153                 continue;
40154             }
40155             reg.showPanel(reg.getPanel(0));
40156         }
40157         
40158         
40159         
40160         
40161     },
40162     
40163     /**
40164      * Returns the nested BorderLayout for this panel
40165      * @return {Roo.BorderLayout} 
40166      */
40167     getLayout : function(){
40168         return this.layout;
40169     },
40170     
40171      /**
40172      * Adds a xtype elements to the layout of the nested panel
40173      * <pre><code>
40174
40175 panel.addxtype({
40176        xtype : 'ContentPanel',
40177        region: 'west',
40178        items: [ .... ]
40179    }
40180 );
40181
40182 panel.addxtype({
40183         xtype : 'NestedLayoutPanel',
40184         region: 'west',
40185         layout: {
40186            center: { },
40187            west: { }   
40188         },
40189         items : [ ... list of content panels or nested layout panels.. ]
40190    }
40191 );
40192 </code></pre>
40193      * @param {Object} cfg Xtype definition of item to add.
40194      */
40195     addxtype : function(cfg) {
40196         return this.layout.addxtype(cfg);
40197     
40198     }
40199 });/*
40200  * Based on:
40201  * Ext JS Library 1.1.1
40202  * Copyright(c) 2006-2007, Ext JS, LLC.
40203  *
40204  * Originally Released Under LGPL - original licence link has changed is not relivant.
40205  *
40206  * Fork - LGPL
40207  * <script type="text/javascript">
40208  */
40209 /**
40210  * @class Roo.TabPanel
40211  * @extends Roo.util.Observable
40212  * A lightweight tab container.
40213  * <br><br>
40214  * Usage:
40215  * <pre><code>
40216 // basic tabs 1, built from existing content
40217 var tabs = new Roo.TabPanel("tabs1");
40218 tabs.addTab("script", "View Script");
40219 tabs.addTab("markup", "View Markup");
40220 tabs.activate("script");
40221
40222 // more advanced tabs, built from javascript
40223 var jtabs = new Roo.TabPanel("jtabs");
40224 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40225
40226 // set up the UpdateManager
40227 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40228 var updater = tab2.getUpdateManager();
40229 updater.setDefaultUrl("ajax1.htm");
40230 tab2.on('activate', updater.refresh, updater, true);
40231
40232 // Use setUrl for Ajax loading
40233 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40234 tab3.setUrl("ajax2.htm", null, true);
40235
40236 // Disabled tab
40237 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40238 tab4.disable();
40239
40240 jtabs.activate("jtabs-1");
40241  * </code></pre>
40242  * @constructor
40243  * Create a new TabPanel.
40244  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40245  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40246  */
40247 Roo.bootstrap.panel.Tabs = function(config){
40248     /**
40249     * The container element for this TabPanel.
40250     * @type Roo.Element
40251     */
40252     this.el = Roo.get(config.el);
40253     delete config.el;
40254     if(config){
40255         if(typeof config == "boolean"){
40256             this.tabPosition = config ? "bottom" : "top";
40257         }else{
40258             Roo.apply(this, config);
40259         }
40260     }
40261     
40262     if(this.tabPosition == "bottom"){
40263         // if tabs are at the bottom = create the body first.
40264         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40265         this.el.addClass("roo-tabs-bottom");
40266     }
40267     // next create the tabs holders
40268     
40269     if (this.tabPosition == "west"){
40270         
40271         var reg = this.region; // fake it..
40272         while (reg) {
40273             if (!reg.mgr.parent) {
40274                 break;
40275             }
40276             reg = reg.mgr.parent.region;
40277         }
40278         Roo.log("got nest?");
40279         Roo.log(reg);
40280         if (reg.mgr.getRegion('west')) {
40281             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40282             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40283             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40284             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40285             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40286         
40287             
40288         }
40289         
40290         
40291     } else {
40292      
40293         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40294         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40295         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40296         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40297     }
40298     
40299     
40300     if(Roo.isIE){
40301         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40302     }
40303     
40304     // finally - if tabs are at the top, then create the body last..
40305     if(this.tabPosition != "bottom"){
40306         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40307          * @type Roo.Element
40308          */
40309         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40310         this.el.addClass("roo-tabs-top");
40311     }
40312     this.items = [];
40313
40314     this.bodyEl.setStyle("position", "relative");
40315
40316     this.active = null;
40317     this.activateDelegate = this.activate.createDelegate(this);
40318
40319     this.addEvents({
40320         /**
40321          * @event tabchange
40322          * Fires when the active tab changes
40323          * @param {Roo.TabPanel} this
40324          * @param {Roo.TabPanelItem} activePanel The new active tab
40325          */
40326         "tabchange": true,
40327         /**
40328          * @event beforetabchange
40329          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40330          * @param {Roo.TabPanel} this
40331          * @param {Object} e Set cancel to true on this object to cancel the tab change
40332          * @param {Roo.TabPanelItem} tab The tab being changed to
40333          */
40334         "beforetabchange" : true
40335     });
40336
40337     Roo.EventManager.onWindowResize(this.onResize, this);
40338     this.cpad = this.el.getPadding("lr");
40339     this.hiddenCount = 0;
40340
40341
40342     // toolbar on the tabbar support...
40343     if (this.toolbar) {
40344         alert("no toolbar support yet");
40345         this.toolbar  = false;
40346         /*
40347         var tcfg = this.toolbar;
40348         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40349         this.toolbar = new Roo.Toolbar(tcfg);
40350         if (Roo.isSafari) {
40351             var tbl = tcfg.container.child('table', true);
40352             tbl.setAttribute('width', '100%');
40353         }
40354         */
40355         
40356     }
40357    
40358
40359
40360     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40361 };
40362
40363 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40364     /*
40365      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40366      */
40367     tabPosition : "top",
40368     /*
40369      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40370      */
40371     currentTabWidth : 0,
40372     /*
40373      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40374      */
40375     minTabWidth : 40,
40376     /*
40377      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40378      */
40379     maxTabWidth : 250,
40380     /*
40381      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40382      */
40383     preferredTabWidth : 175,
40384     /*
40385      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40386      */
40387     resizeTabs : false,
40388     /*
40389      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40390      */
40391     monitorResize : true,
40392     /*
40393      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40394      */
40395     toolbar : false,  // set by caller..
40396     
40397     region : false, /// set by caller
40398     
40399     disableTooltips : true, // not used yet...
40400
40401     /**
40402      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40403      * @param {String} id The id of the div to use <b>or create</b>
40404      * @param {String} text The text for the tab
40405      * @param {String} content (optional) Content to put in the TabPanelItem body
40406      * @param {Boolean} closable (optional) True to create a close icon on the tab
40407      * @return {Roo.TabPanelItem} The created TabPanelItem
40408      */
40409     addTab : function(id, text, content, closable, tpl)
40410     {
40411         var item = new Roo.bootstrap.panel.TabItem({
40412             panel: this,
40413             id : id,
40414             text : text,
40415             closable : closable,
40416             tpl : tpl
40417         });
40418         this.addTabItem(item);
40419         if(content){
40420             item.setContent(content);
40421         }
40422         return item;
40423     },
40424
40425     /**
40426      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40427      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40428      * @return {Roo.TabPanelItem}
40429      */
40430     getTab : function(id){
40431         return this.items[id];
40432     },
40433
40434     /**
40435      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40436      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40437      */
40438     hideTab : function(id){
40439         var t = this.items[id];
40440         if(!t.isHidden()){
40441            t.setHidden(true);
40442            this.hiddenCount++;
40443            this.autoSizeTabs();
40444         }
40445     },
40446
40447     /**
40448      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40449      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40450      */
40451     unhideTab : function(id){
40452         var t = this.items[id];
40453         if(t.isHidden()){
40454            t.setHidden(false);
40455            this.hiddenCount--;
40456            this.autoSizeTabs();
40457         }
40458     },
40459
40460     /**
40461      * Adds an existing {@link Roo.TabPanelItem}.
40462      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40463      */
40464     addTabItem : function(item)
40465     {
40466         this.items[item.id] = item;
40467         this.items.push(item);
40468         this.autoSizeTabs();
40469       //  if(this.resizeTabs){
40470     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40471   //         this.autoSizeTabs();
40472 //        }else{
40473 //            item.autoSize();
40474        // }
40475     },
40476
40477     /**
40478      * Removes a {@link Roo.TabPanelItem}.
40479      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40480      */
40481     removeTab : function(id){
40482         var items = this.items;
40483         var tab = items[id];
40484         if(!tab) { return; }
40485         var index = items.indexOf(tab);
40486         if(this.active == tab && items.length > 1){
40487             var newTab = this.getNextAvailable(index);
40488             if(newTab) {
40489                 newTab.activate();
40490             }
40491         }
40492         this.stripEl.dom.removeChild(tab.pnode.dom);
40493         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40494             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40495         }
40496         items.splice(index, 1);
40497         delete this.items[tab.id];
40498         tab.fireEvent("close", tab);
40499         tab.purgeListeners();
40500         this.autoSizeTabs();
40501     },
40502
40503     getNextAvailable : function(start){
40504         var items = this.items;
40505         var index = start;
40506         // look for a next tab that will slide over to
40507         // replace the one being removed
40508         while(index < items.length){
40509             var item = items[++index];
40510             if(item && !item.isHidden()){
40511                 return item;
40512             }
40513         }
40514         // if one isn't found select the previous tab (on the left)
40515         index = start;
40516         while(index >= 0){
40517             var item = items[--index];
40518             if(item && !item.isHidden()){
40519                 return item;
40520             }
40521         }
40522         return null;
40523     },
40524
40525     /**
40526      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40527      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40528      */
40529     disableTab : function(id){
40530         var tab = this.items[id];
40531         if(tab && this.active != tab){
40532             tab.disable();
40533         }
40534     },
40535
40536     /**
40537      * Enables a {@link Roo.TabPanelItem} that is disabled.
40538      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40539      */
40540     enableTab : function(id){
40541         var tab = this.items[id];
40542         tab.enable();
40543     },
40544
40545     /**
40546      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40547      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40548      * @return {Roo.TabPanelItem} The TabPanelItem.
40549      */
40550     activate : function(id)
40551     {
40552         //Roo.log('activite:'  + id);
40553         
40554         var tab = this.items[id];
40555         if(!tab){
40556             return null;
40557         }
40558         if(tab == this.active || tab.disabled){
40559             return tab;
40560         }
40561         var e = {};
40562         this.fireEvent("beforetabchange", this, e, tab);
40563         if(e.cancel !== true && !tab.disabled){
40564             if(this.active){
40565                 this.active.hide();
40566             }
40567             this.active = this.items[id];
40568             this.active.show();
40569             this.fireEvent("tabchange", this, this.active);
40570         }
40571         return tab;
40572     },
40573
40574     /**
40575      * Gets the active {@link Roo.TabPanelItem}.
40576      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40577      */
40578     getActiveTab : function(){
40579         return this.active;
40580     },
40581
40582     /**
40583      * Updates the tab body element to fit the height of the container element
40584      * for overflow scrolling
40585      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40586      */
40587     syncHeight : function(targetHeight){
40588         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40589         var bm = this.bodyEl.getMargins();
40590         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40591         this.bodyEl.setHeight(newHeight);
40592         return newHeight;
40593     },
40594
40595     onResize : function(){
40596         if(this.monitorResize){
40597             this.autoSizeTabs();
40598         }
40599     },
40600
40601     /**
40602      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40603      */
40604     beginUpdate : function(){
40605         this.updating = true;
40606     },
40607
40608     /**
40609      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40610      */
40611     endUpdate : function(){
40612         this.updating = false;
40613         this.autoSizeTabs();
40614     },
40615
40616     /**
40617      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40618      */
40619     autoSizeTabs : function()
40620     {
40621         var count = this.items.length;
40622         var vcount = count - this.hiddenCount;
40623         
40624         if (vcount < 2) {
40625             this.stripEl.hide();
40626         } else {
40627             this.stripEl.show();
40628         }
40629         
40630         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40631             return;
40632         }
40633         
40634         
40635         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40636         var availWidth = Math.floor(w / vcount);
40637         var b = this.stripBody;
40638         if(b.getWidth() > w){
40639             var tabs = this.items;
40640             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40641             if(availWidth < this.minTabWidth){
40642                 /*if(!this.sleft){    // incomplete scrolling code
40643                     this.createScrollButtons();
40644                 }
40645                 this.showScroll();
40646                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40647             }
40648         }else{
40649             if(this.currentTabWidth < this.preferredTabWidth){
40650                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40651             }
40652         }
40653     },
40654
40655     /**
40656      * Returns the number of tabs in this TabPanel.
40657      * @return {Number}
40658      */
40659      getCount : function(){
40660          return this.items.length;
40661      },
40662
40663     /**
40664      * Resizes all the tabs to the passed width
40665      * @param {Number} The new width
40666      */
40667     setTabWidth : function(width){
40668         this.currentTabWidth = width;
40669         for(var i = 0, len = this.items.length; i < len; i++) {
40670                 if(!this.items[i].isHidden()) {
40671                 this.items[i].setWidth(width);
40672             }
40673         }
40674     },
40675
40676     /**
40677      * Destroys this TabPanel
40678      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40679      */
40680     destroy : function(removeEl){
40681         Roo.EventManager.removeResizeListener(this.onResize, this);
40682         for(var i = 0, len = this.items.length; i < len; i++){
40683             this.items[i].purgeListeners();
40684         }
40685         if(removeEl === true){
40686             this.el.update("");
40687             this.el.remove();
40688         }
40689     },
40690     
40691     createStrip : function(container)
40692     {
40693         var strip = document.createElement("nav");
40694         strip.className = Roo.bootstrap.version == 4 ?
40695             "navbar-light bg-light" : 
40696             "navbar navbar-default"; //"x-tabs-wrap";
40697         container.appendChild(strip);
40698         return strip;
40699     },
40700     
40701     createStripList : function(strip)
40702     {
40703         // div wrapper for retard IE
40704         // returns the "tr" element.
40705         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40706         //'<div class="x-tabs-strip-wrap">'+
40707           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40708           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40709         return strip.firstChild; //.firstChild.firstChild.firstChild;
40710     },
40711     createBody : function(container)
40712     {
40713         var body = document.createElement("div");
40714         Roo.id(body, "tab-body");
40715         //Roo.fly(body).addClass("x-tabs-body");
40716         Roo.fly(body).addClass("tab-content");
40717         container.appendChild(body);
40718         return body;
40719     },
40720     createItemBody :function(bodyEl, id){
40721         var body = Roo.getDom(id);
40722         if(!body){
40723             body = document.createElement("div");
40724             body.id = id;
40725         }
40726         //Roo.fly(body).addClass("x-tabs-item-body");
40727         Roo.fly(body).addClass("tab-pane");
40728          bodyEl.insertBefore(body, bodyEl.firstChild);
40729         return body;
40730     },
40731     /** @private */
40732     createStripElements :  function(stripEl, text, closable, tpl)
40733     {
40734         var td = document.createElement("li"); // was td..
40735         td.className = 'nav-item';
40736         
40737         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40738         
40739         
40740         stripEl.appendChild(td);
40741         /*if(closable){
40742             td.className = "x-tabs-closable";
40743             if(!this.closeTpl){
40744                 this.closeTpl = new Roo.Template(
40745                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40746                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40747                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40748                 );
40749             }
40750             var el = this.closeTpl.overwrite(td, {"text": text});
40751             var close = el.getElementsByTagName("div")[0];
40752             var inner = el.getElementsByTagName("em")[0];
40753             return {"el": el, "close": close, "inner": inner};
40754         } else {
40755         */
40756         // not sure what this is..
40757 //            if(!this.tabTpl){
40758                 //this.tabTpl = new Roo.Template(
40759                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40760                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40761                 //);
40762 //                this.tabTpl = new Roo.Template(
40763 //                   '<a href="#">' +
40764 //                   '<span unselectable="on"' +
40765 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40766 //                            ' >{text}</span></a>'
40767 //                );
40768 //                
40769 //            }
40770
40771
40772             var template = tpl || this.tabTpl || false;
40773             
40774             if(!template){
40775                 template =  new Roo.Template(
40776                         Roo.bootstrap.version == 4 ? 
40777                             (
40778                                 '<a class="nav-link" href="#" unselectable="on"' +
40779                                      (this.disableTooltips ? '' : ' title="{text}"') +
40780                                      ' >{text}</a>'
40781                             ) : (
40782                                 '<a class="nav-link" href="#">' +
40783                                 '<span unselectable="on"' +
40784                                          (this.disableTooltips ? '' : ' title="{text}"') +
40785                                     ' >{text}</span></a>'
40786                             )
40787                 );
40788             }
40789             
40790             switch (typeof(template)) {
40791                 case 'object' :
40792                     break;
40793                 case 'string' :
40794                     template = new Roo.Template(template);
40795                     break;
40796                 default :
40797                     break;
40798             }
40799             
40800             var el = template.overwrite(td, {"text": text});
40801             
40802             var inner = el.getElementsByTagName("span")[0];
40803             
40804             return {"el": el, "inner": inner};
40805             
40806     }
40807         
40808     
40809 });
40810
40811 /**
40812  * @class Roo.TabPanelItem
40813  * @extends Roo.util.Observable
40814  * Represents an individual item (tab plus body) in a TabPanel.
40815  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40816  * @param {String} id The id of this TabPanelItem
40817  * @param {String} text The text for the tab of this TabPanelItem
40818  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40819  */
40820 Roo.bootstrap.panel.TabItem = function(config){
40821     /**
40822      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40823      * @type Roo.TabPanel
40824      */
40825     this.tabPanel = config.panel;
40826     /**
40827      * The id for this TabPanelItem
40828      * @type String
40829      */
40830     this.id = config.id;
40831     /** @private */
40832     this.disabled = false;
40833     /** @private */
40834     this.text = config.text;
40835     /** @private */
40836     this.loaded = false;
40837     this.closable = config.closable;
40838
40839     /**
40840      * The body element for this TabPanelItem.
40841      * @type Roo.Element
40842      */
40843     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40844     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40845     this.bodyEl.setStyle("display", "block");
40846     this.bodyEl.setStyle("zoom", "1");
40847     //this.hideAction();
40848
40849     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40850     /** @private */
40851     this.el = Roo.get(els.el);
40852     this.inner = Roo.get(els.inner, true);
40853      this.textEl = Roo.bootstrap.version == 4 ?
40854         this.el : Roo.get(this.el.dom.firstChild, true);
40855
40856     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40857     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40858
40859     
40860 //    this.el.on("mousedown", this.onTabMouseDown, this);
40861     this.el.on("click", this.onTabClick, this);
40862     /** @private */
40863     if(config.closable){
40864         var c = Roo.get(els.close, true);
40865         c.dom.title = this.closeText;
40866         c.addClassOnOver("close-over");
40867         c.on("click", this.closeClick, this);
40868      }
40869
40870     this.addEvents({
40871          /**
40872          * @event activate
40873          * Fires when this tab becomes the active tab.
40874          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40875          * @param {Roo.TabPanelItem} this
40876          */
40877         "activate": true,
40878         /**
40879          * @event beforeclose
40880          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40881          * @param {Roo.TabPanelItem} this
40882          * @param {Object} e Set cancel to true on this object to cancel the close.
40883          */
40884         "beforeclose": true,
40885         /**
40886          * @event close
40887          * Fires when this tab is closed.
40888          * @param {Roo.TabPanelItem} this
40889          */
40890          "close": true,
40891         /**
40892          * @event deactivate
40893          * Fires when this tab is no longer the active tab.
40894          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40895          * @param {Roo.TabPanelItem} this
40896          */
40897          "deactivate" : true
40898     });
40899     this.hidden = false;
40900
40901     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40902 };
40903
40904 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40905            {
40906     purgeListeners : function(){
40907        Roo.util.Observable.prototype.purgeListeners.call(this);
40908        this.el.removeAllListeners();
40909     },
40910     /**
40911      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40912      */
40913     show : function(){
40914         this.status_node.addClass("active");
40915         this.showAction();
40916         if(Roo.isOpera){
40917             this.tabPanel.stripWrap.repaint();
40918         }
40919         this.fireEvent("activate", this.tabPanel, this);
40920     },
40921
40922     /**
40923      * Returns true if this tab is the active tab.
40924      * @return {Boolean}
40925      */
40926     isActive : function(){
40927         return this.tabPanel.getActiveTab() == this;
40928     },
40929
40930     /**
40931      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40932      */
40933     hide : function(){
40934         this.status_node.removeClass("active");
40935         this.hideAction();
40936         this.fireEvent("deactivate", this.tabPanel, this);
40937     },
40938
40939     hideAction : function(){
40940         this.bodyEl.hide();
40941         this.bodyEl.setStyle("position", "absolute");
40942         this.bodyEl.setLeft("-20000px");
40943         this.bodyEl.setTop("-20000px");
40944     },
40945
40946     showAction : function(){
40947         this.bodyEl.setStyle("position", "relative");
40948         this.bodyEl.setTop("");
40949         this.bodyEl.setLeft("");
40950         this.bodyEl.show();
40951     },
40952
40953     /**
40954      * Set the tooltip for the tab.
40955      * @param {String} tooltip The tab's tooltip
40956      */
40957     setTooltip : function(text){
40958         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40959             this.textEl.dom.qtip = text;
40960             this.textEl.dom.removeAttribute('title');
40961         }else{
40962             this.textEl.dom.title = text;
40963         }
40964     },
40965
40966     onTabClick : function(e){
40967         e.preventDefault();
40968         this.tabPanel.activate(this.id);
40969     },
40970
40971     onTabMouseDown : function(e){
40972         e.preventDefault();
40973         this.tabPanel.activate(this.id);
40974     },
40975 /*
40976     getWidth : function(){
40977         return this.inner.getWidth();
40978     },
40979
40980     setWidth : function(width){
40981         var iwidth = width - this.linode.getPadding("lr");
40982         this.inner.setWidth(iwidth);
40983         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40984         this.linode.setWidth(width);
40985     },
40986 */
40987     /**
40988      * Show or hide the tab
40989      * @param {Boolean} hidden True to hide or false to show.
40990      */
40991     setHidden : function(hidden){
40992         this.hidden = hidden;
40993         this.linode.setStyle("display", hidden ? "none" : "");
40994     },
40995
40996     /**
40997      * Returns true if this tab is "hidden"
40998      * @return {Boolean}
40999      */
41000     isHidden : function(){
41001         return this.hidden;
41002     },
41003
41004     /**
41005      * Returns the text for this tab
41006      * @return {String}
41007      */
41008     getText : function(){
41009         return this.text;
41010     },
41011     /*
41012     autoSize : function(){
41013         //this.el.beginMeasure();
41014         this.textEl.setWidth(1);
41015         /*
41016          *  #2804 [new] Tabs in Roojs
41017          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41018          */
41019         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41020         //this.el.endMeasure();
41021     //},
41022
41023     /**
41024      * Sets the text for the tab (Note: this also sets the tooltip text)
41025      * @param {String} text The tab's text and tooltip
41026      */
41027     setText : function(text){
41028         this.text = text;
41029         this.textEl.update(text);
41030         this.setTooltip(text);
41031         //if(!this.tabPanel.resizeTabs){
41032         //    this.autoSize();
41033         //}
41034     },
41035     /**
41036      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41037      */
41038     activate : function(){
41039         this.tabPanel.activate(this.id);
41040     },
41041
41042     /**
41043      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41044      */
41045     disable : function(){
41046         if(this.tabPanel.active != this){
41047             this.disabled = true;
41048             this.status_node.addClass("disabled");
41049         }
41050     },
41051
41052     /**
41053      * Enables this TabPanelItem if it was previously disabled.
41054      */
41055     enable : function(){
41056         this.disabled = false;
41057         this.status_node.removeClass("disabled");
41058     },
41059
41060     /**
41061      * Sets the content for this TabPanelItem.
41062      * @param {String} content The content
41063      * @param {Boolean} loadScripts true to look for and load scripts
41064      */
41065     setContent : function(content, loadScripts){
41066         this.bodyEl.update(content, loadScripts);
41067     },
41068
41069     /**
41070      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41071      * @return {Roo.UpdateManager} The UpdateManager
41072      */
41073     getUpdateManager : function(){
41074         return this.bodyEl.getUpdateManager();
41075     },
41076
41077     /**
41078      * Set a URL to be used to load the content for this TabPanelItem.
41079      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41080      * @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)
41081      * @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)
41082      * @return {Roo.UpdateManager} The UpdateManager
41083      */
41084     setUrl : function(url, params, loadOnce){
41085         if(this.refreshDelegate){
41086             this.un('activate', this.refreshDelegate);
41087         }
41088         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41089         this.on("activate", this.refreshDelegate);
41090         return this.bodyEl.getUpdateManager();
41091     },
41092
41093     /** @private */
41094     _handleRefresh : function(url, params, loadOnce){
41095         if(!loadOnce || !this.loaded){
41096             var updater = this.bodyEl.getUpdateManager();
41097             updater.update(url, params, this._setLoaded.createDelegate(this));
41098         }
41099     },
41100
41101     /**
41102      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41103      *   Will fail silently if the setUrl method has not been called.
41104      *   This does not activate the panel, just updates its content.
41105      */
41106     refresh : function(){
41107         if(this.refreshDelegate){
41108            this.loaded = false;
41109            this.refreshDelegate();
41110         }
41111     },
41112
41113     /** @private */
41114     _setLoaded : function(){
41115         this.loaded = true;
41116     },
41117
41118     /** @private */
41119     closeClick : function(e){
41120         var o = {};
41121         e.stopEvent();
41122         this.fireEvent("beforeclose", this, o);
41123         if(o.cancel !== true){
41124             this.tabPanel.removeTab(this.id);
41125         }
41126     },
41127     /**
41128      * The text displayed in the tooltip for the close icon.
41129      * @type String
41130      */
41131     closeText : "Close this tab"
41132 });
41133 /**
41134 *    This script refer to:
41135 *    Title: International Telephone Input
41136 *    Author: Jack O'Connor
41137 *    Code version:  v12.1.12
41138 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41139 **/
41140
41141 Roo.bootstrap.PhoneInputData = function() {
41142     var d = [
41143       [
41144         "Afghanistan (‫افغانستان‬‎)",
41145         "af",
41146         "93"
41147       ],
41148       [
41149         "Albania (Shqipëri)",
41150         "al",
41151         "355"
41152       ],
41153       [
41154         "Algeria (‫الجزائر‬‎)",
41155         "dz",
41156         "213"
41157       ],
41158       [
41159         "American Samoa",
41160         "as",
41161         "1684"
41162       ],
41163       [
41164         "Andorra",
41165         "ad",
41166         "376"
41167       ],
41168       [
41169         "Angola",
41170         "ao",
41171         "244"
41172       ],
41173       [
41174         "Anguilla",
41175         "ai",
41176         "1264"
41177       ],
41178       [
41179         "Antigua and Barbuda",
41180         "ag",
41181         "1268"
41182       ],
41183       [
41184         "Argentina",
41185         "ar",
41186         "54"
41187       ],
41188       [
41189         "Armenia (Հայաստան)",
41190         "am",
41191         "374"
41192       ],
41193       [
41194         "Aruba",
41195         "aw",
41196         "297"
41197       ],
41198       [
41199         "Australia",
41200         "au",
41201         "61",
41202         0
41203       ],
41204       [
41205         "Austria (Österreich)",
41206         "at",
41207         "43"
41208       ],
41209       [
41210         "Azerbaijan (Azərbaycan)",
41211         "az",
41212         "994"
41213       ],
41214       [
41215         "Bahamas",
41216         "bs",
41217         "1242"
41218       ],
41219       [
41220         "Bahrain (‫البحرين‬‎)",
41221         "bh",
41222         "973"
41223       ],
41224       [
41225         "Bangladesh (বাংলাদেশ)",
41226         "bd",
41227         "880"
41228       ],
41229       [
41230         "Barbados",
41231         "bb",
41232         "1246"
41233       ],
41234       [
41235         "Belarus (Беларусь)",
41236         "by",
41237         "375"
41238       ],
41239       [
41240         "Belgium (België)",
41241         "be",
41242         "32"
41243       ],
41244       [
41245         "Belize",
41246         "bz",
41247         "501"
41248       ],
41249       [
41250         "Benin (Bénin)",
41251         "bj",
41252         "229"
41253       ],
41254       [
41255         "Bermuda",
41256         "bm",
41257         "1441"
41258       ],
41259       [
41260         "Bhutan (འབྲུག)",
41261         "bt",
41262         "975"
41263       ],
41264       [
41265         "Bolivia",
41266         "bo",
41267         "591"
41268       ],
41269       [
41270         "Bosnia and Herzegovina (Босна и Херцеговина)",
41271         "ba",
41272         "387"
41273       ],
41274       [
41275         "Botswana",
41276         "bw",
41277         "267"
41278       ],
41279       [
41280         "Brazil (Brasil)",
41281         "br",
41282         "55"
41283       ],
41284       [
41285         "British Indian Ocean Territory",
41286         "io",
41287         "246"
41288       ],
41289       [
41290         "British Virgin Islands",
41291         "vg",
41292         "1284"
41293       ],
41294       [
41295         "Brunei",
41296         "bn",
41297         "673"
41298       ],
41299       [
41300         "Bulgaria (България)",
41301         "bg",
41302         "359"
41303       ],
41304       [
41305         "Burkina Faso",
41306         "bf",
41307         "226"
41308       ],
41309       [
41310         "Burundi (Uburundi)",
41311         "bi",
41312         "257"
41313       ],
41314       [
41315         "Cambodia (កម្ពុជា)",
41316         "kh",
41317         "855"
41318       ],
41319       [
41320         "Cameroon (Cameroun)",
41321         "cm",
41322         "237"
41323       ],
41324       [
41325         "Canada",
41326         "ca",
41327         "1",
41328         1,
41329         ["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"]
41330       ],
41331       [
41332         "Cape Verde (Kabu Verdi)",
41333         "cv",
41334         "238"
41335       ],
41336       [
41337         "Caribbean Netherlands",
41338         "bq",
41339         "599",
41340         1
41341       ],
41342       [
41343         "Cayman Islands",
41344         "ky",
41345         "1345"
41346       ],
41347       [
41348         "Central African Republic (République centrafricaine)",
41349         "cf",
41350         "236"
41351       ],
41352       [
41353         "Chad (Tchad)",
41354         "td",
41355         "235"
41356       ],
41357       [
41358         "Chile",
41359         "cl",
41360         "56"
41361       ],
41362       [
41363         "China (中国)",
41364         "cn",
41365         "86"
41366       ],
41367       [
41368         "Christmas Island",
41369         "cx",
41370         "61",
41371         2
41372       ],
41373       [
41374         "Cocos (Keeling) Islands",
41375         "cc",
41376         "61",
41377         1
41378       ],
41379       [
41380         "Colombia",
41381         "co",
41382         "57"
41383       ],
41384       [
41385         "Comoros (‫جزر القمر‬‎)",
41386         "km",
41387         "269"
41388       ],
41389       [
41390         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41391         "cd",
41392         "243"
41393       ],
41394       [
41395         "Congo (Republic) (Congo-Brazzaville)",
41396         "cg",
41397         "242"
41398       ],
41399       [
41400         "Cook Islands",
41401         "ck",
41402         "682"
41403       ],
41404       [
41405         "Costa Rica",
41406         "cr",
41407         "506"
41408       ],
41409       [
41410         "Côte d’Ivoire",
41411         "ci",
41412         "225"
41413       ],
41414       [
41415         "Croatia (Hrvatska)",
41416         "hr",
41417         "385"
41418       ],
41419       [
41420         "Cuba",
41421         "cu",
41422         "53"
41423       ],
41424       [
41425         "Curaçao",
41426         "cw",
41427         "599",
41428         0
41429       ],
41430       [
41431         "Cyprus (Κύπρος)",
41432         "cy",
41433         "357"
41434       ],
41435       [
41436         "Czech Republic (Česká republika)",
41437         "cz",
41438         "420"
41439       ],
41440       [
41441         "Denmark (Danmark)",
41442         "dk",
41443         "45"
41444       ],
41445       [
41446         "Djibouti",
41447         "dj",
41448         "253"
41449       ],
41450       [
41451         "Dominica",
41452         "dm",
41453         "1767"
41454       ],
41455       [
41456         "Dominican Republic (República Dominicana)",
41457         "do",
41458         "1",
41459         2,
41460         ["809", "829", "849"]
41461       ],
41462       [
41463         "Ecuador",
41464         "ec",
41465         "593"
41466       ],
41467       [
41468         "Egypt (‫مصر‬‎)",
41469         "eg",
41470         "20"
41471       ],
41472       [
41473         "El Salvador",
41474         "sv",
41475         "503"
41476       ],
41477       [
41478         "Equatorial Guinea (Guinea Ecuatorial)",
41479         "gq",
41480         "240"
41481       ],
41482       [
41483         "Eritrea",
41484         "er",
41485         "291"
41486       ],
41487       [
41488         "Estonia (Eesti)",
41489         "ee",
41490         "372"
41491       ],
41492       [
41493         "Ethiopia",
41494         "et",
41495         "251"
41496       ],
41497       [
41498         "Falkland Islands (Islas Malvinas)",
41499         "fk",
41500         "500"
41501       ],
41502       [
41503         "Faroe Islands (Føroyar)",
41504         "fo",
41505         "298"
41506       ],
41507       [
41508         "Fiji",
41509         "fj",
41510         "679"
41511       ],
41512       [
41513         "Finland (Suomi)",
41514         "fi",
41515         "358",
41516         0
41517       ],
41518       [
41519         "France",
41520         "fr",
41521         "33"
41522       ],
41523       [
41524         "French Guiana (Guyane française)",
41525         "gf",
41526         "594"
41527       ],
41528       [
41529         "French Polynesia (Polynésie française)",
41530         "pf",
41531         "689"
41532       ],
41533       [
41534         "Gabon",
41535         "ga",
41536         "241"
41537       ],
41538       [
41539         "Gambia",
41540         "gm",
41541         "220"
41542       ],
41543       [
41544         "Georgia (საქართველო)",
41545         "ge",
41546         "995"
41547       ],
41548       [
41549         "Germany (Deutschland)",
41550         "de",
41551         "49"
41552       ],
41553       [
41554         "Ghana (Gaana)",
41555         "gh",
41556         "233"
41557       ],
41558       [
41559         "Gibraltar",
41560         "gi",
41561         "350"
41562       ],
41563       [
41564         "Greece (Ελλάδα)",
41565         "gr",
41566         "30"
41567       ],
41568       [
41569         "Greenland (Kalaallit Nunaat)",
41570         "gl",
41571         "299"
41572       ],
41573       [
41574         "Grenada",
41575         "gd",
41576         "1473"
41577       ],
41578       [
41579         "Guadeloupe",
41580         "gp",
41581         "590",
41582         0
41583       ],
41584       [
41585         "Guam",
41586         "gu",
41587         "1671"
41588       ],
41589       [
41590         "Guatemala",
41591         "gt",
41592         "502"
41593       ],
41594       [
41595         "Guernsey",
41596         "gg",
41597         "44",
41598         1
41599       ],
41600       [
41601         "Guinea (Guinée)",
41602         "gn",
41603         "224"
41604       ],
41605       [
41606         "Guinea-Bissau (Guiné Bissau)",
41607         "gw",
41608         "245"
41609       ],
41610       [
41611         "Guyana",
41612         "gy",
41613         "592"
41614       ],
41615       [
41616         "Haiti",
41617         "ht",
41618         "509"
41619       ],
41620       [
41621         "Honduras",
41622         "hn",
41623         "504"
41624       ],
41625       [
41626         "Hong Kong (香港)",
41627         "hk",
41628         "852"
41629       ],
41630       [
41631         "Hungary (Magyarország)",
41632         "hu",
41633         "36"
41634       ],
41635       [
41636         "Iceland (Ísland)",
41637         "is",
41638         "354"
41639       ],
41640       [
41641         "India (भारत)",
41642         "in",
41643         "91"
41644       ],
41645       [
41646         "Indonesia",
41647         "id",
41648         "62"
41649       ],
41650       [
41651         "Iran (‫ایران‬‎)",
41652         "ir",
41653         "98"
41654       ],
41655       [
41656         "Iraq (‫العراق‬‎)",
41657         "iq",
41658         "964"
41659       ],
41660       [
41661         "Ireland",
41662         "ie",
41663         "353"
41664       ],
41665       [
41666         "Isle of Man",
41667         "im",
41668         "44",
41669         2
41670       ],
41671       [
41672         "Israel (‫ישראל‬‎)",
41673         "il",
41674         "972"
41675       ],
41676       [
41677         "Italy (Italia)",
41678         "it",
41679         "39",
41680         0
41681       ],
41682       [
41683         "Jamaica",
41684         "jm",
41685         "1876"
41686       ],
41687       [
41688         "Japan (日本)",
41689         "jp",
41690         "81"
41691       ],
41692       [
41693         "Jersey",
41694         "je",
41695         "44",
41696         3
41697       ],
41698       [
41699         "Jordan (‫الأردن‬‎)",
41700         "jo",
41701         "962"
41702       ],
41703       [
41704         "Kazakhstan (Казахстан)",
41705         "kz",
41706         "7",
41707         1
41708       ],
41709       [
41710         "Kenya",
41711         "ke",
41712         "254"
41713       ],
41714       [
41715         "Kiribati",
41716         "ki",
41717         "686"
41718       ],
41719       [
41720         "Kosovo",
41721         "xk",
41722         "383"
41723       ],
41724       [
41725         "Kuwait (‫الكويت‬‎)",
41726         "kw",
41727         "965"
41728       ],
41729       [
41730         "Kyrgyzstan (Кыргызстан)",
41731         "kg",
41732         "996"
41733       ],
41734       [
41735         "Laos (ລາວ)",
41736         "la",
41737         "856"
41738       ],
41739       [
41740         "Latvia (Latvija)",
41741         "lv",
41742         "371"
41743       ],
41744       [
41745         "Lebanon (‫لبنان‬‎)",
41746         "lb",
41747         "961"
41748       ],
41749       [
41750         "Lesotho",
41751         "ls",
41752         "266"
41753       ],
41754       [
41755         "Liberia",
41756         "lr",
41757         "231"
41758       ],
41759       [
41760         "Libya (‫ليبيا‬‎)",
41761         "ly",
41762         "218"
41763       ],
41764       [
41765         "Liechtenstein",
41766         "li",
41767         "423"
41768       ],
41769       [
41770         "Lithuania (Lietuva)",
41771         "lt",
41772         "370"
41773       ],
41774       [
41775         "Luxembourg",
41776         "lu",
41777         "352"
41778       ],
41779       [
41780         "Macau (澳門)",
41781         "mo",
41782         "853"
41783       ],
41784       [
41785         "Macedonia (FYROM) (Македонија)",
41786         "mk",
41787         "389"
41788       ],
41789       [
41790         "Madagascar (Madagasikara)",
41791         "mg",
41792         "261"
41793       ],
41794       [
41795         "Malawi",
41796         "mw",
41797         "265"
41798       ],
41799       [
41800         "Malaysia",
41801         "my",
41802         "60"
41803       ],
41804       [
41805         "Maldives",
41806         "mv",
41807         "960"
41808       ],
41809       [
41810         "Mali",
41811         "ml",
41812         "223"
41813       ],
41814       [
41815         "Malta",
41816         "mt",
41817         "356"
41818       ],
41819       [
41820         "Marshall Islands",
41821         "mh",
41822         "692"
41823       ],
41824       [
41825         "Martinique",
41826         "mq",
41827         "596"
41828       ],
41829       [
41830         "Mauritania (‫موريتانيا‬‎)",
41831         "mr",
41832         "222"
41833       ],
41834       [
41835         "Mauritius (Moris)",
41836         "mu",
41837         "230"
41838       ],
41839       [
41840         "Mayotte",
41841         "yt",
41842         "262",
41843         1
41844       ],
41845       [
41846         "Mexico (México)",
41847         "mx",
41848         "52"
41849       ],
41850       [
41851         "Micronesia",
41852         "fm",
41853         "691"
41854       ],
41855       [
41856         "Moldova (Republica Moldova)",
41857         "md",
41858         "373"
41859       ],
41860       [
41861         "Monaco",
41862         "mc",
41863         "377"
41864       ],
41865       [
41866         "Mongolia (Монгол)",
41867         "mn",
41868         "976"
41869       ],
41870       [
41871         "Montenegro (Crna Gora)",
41872         "me",
41873         "382"
41874       ],
41875       [
41876         "Montserrat",
41877         "ms",
41878         "1664"
41879       ],
41880       [
41881         "Morocco (‫المغرب‬‎)",
41882         "ma",
41883         "212",
41884         0
41885       ],
41886       [
41887         "Mozambique (Moçambique)",
41888         "mz",
41889         "258"
41890       ],
41891       [
41892         "Myanmar (Burma) (မြန်မာ)",
41893         "mm",
41894         "95"
41895       ],
41896       [
41897         "Namibia (Namibië)",
41898         "na",
41899         "264"
41900       ],
41901       [
41902         "Nauru",
41903         "nr",
41904         "674"
41905       ],
41906       [
41907         "Nepal (नेपाल)",
41908         "np",
41909         "977"
41910       ],
41911       [
41912         "Netherlands (Nederland)",
41913         "nl",
41914         "31"
41915       ],
41916       [
41917         "New Caledonia (Nouvelle-Calédonie)",
41918         "nc",
41919         "687"
41920       ],
41921       [
41922         "New Zealand",
41923         "nz",
41924         "64"
41925       ],
41926       [
41927         "Nicaragua",
41928         "ni",
41929         "505"
41930       ],
41931       [
41932         "Niger (Nijar)",
41933         "ne",
41934         "227"
41935       ],
41936       [
41937         "Nigeria",
41938         "ng",
41939         "234"
41940       ],
41941       [
41942         "Niue",
41943         "nu",
41944         "683"
41945       ],
41946       [
41947         "Norfolk Island",
41948         "nf",
41949         "672"
41950       ],
41951       [
41952         "North Korea (조선 민주주의 인민 공화국)",
41953         "kp",
41954         "850"
41955       ],
41956       [
41957         "Northern Mariana Islands",
41958         "mp",
41959         "1670"
41960       ],
41961       [
41962         "Norway (Norge)",
41963         "no",
41964         "47",
41965         0
41966       ],
41967       [
41968         "Oman (‫عُمان‬‎)",
41969         "om",
41970         "968"
41971       ],
41972       [
41973         "Pakistan (‫پاکستان‬‎)",
41974         "pk",
41975         "92"
41976       ],
41977       [
41978         "Palau",
41979         "pw",
41980         "680"
41981       ],
41982       [
41983         "Palestine (‫فلسطين‬‎)",
41984         "ps",
41985         "970"
41986       ],
41987       [
41988         "Panama (Panamá)",
41989         "pa",
41990         "507"
41991       ],
41992       [
41993         "Papua New Guinea",
41994         "pg",
41995         "675"
41996       ],
41997       [
41998         "Paraguay",
41999         "py",
42000         "595"
42001       ],
42002       [
42003         "Peru (Perú)",
42004         "pe",
42005         "51"
42006       ],
42007       [
42008         "Philippines",
42009         "ph",
42010         "63"
42011       ],
42012       [
42013         "Poland (Polska)",
42014         "pl",
42015         "48"
42016       ],
42017       [
42018         "Portugal",
42019         "pt",
42020         "351"
42021       ],
42022       [
42023         "Puerto Rico",
42024         "pr",
42025         "1",
42026         3,
42027         ["787", "939"]
42028       ],
42029       [
42030         "Qatar (‫قطر‬‎)",
42031         "qa",
42032         "974"
42033       ],
42034       [
42035         "Réunion (La Réunion)",
42036         "re",
42037         "262",
42038         0
42039       ],
42040       [
42041         "Romania (România)",
42042         "ro",
42043         "40"
42044       ],
42045       [
42046         "Russia (Россия)",
42047         "ru",
42048         "7",
42049         0
42050       ],
42051       [
42052         "Rwanda",
42053         "rw",
42054         "250"
42055       ],
42056       [
42057         "Saint Barthélemy",
42058         "bl",
42059         "590",
42060         1
42061       ],
42062       [
42063         "Saint Helena",
42064         "sh",
42065         "290"
42066       ],
42067       [
42068         "Saint Kitts and Nevis",
42069         "kn",
42070         "1869"
42071       ],
42072       [
42073         "Saint Lucia",
42074         "lc",
42075         "1758"
42076       ],
42077       [
42078         "Saint Martin (Saint-Martin (partie française))",
42079         "mf",
42080         "590",
42081         2
42082       ],
42083       [
42084         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42085         "pm",
42086         "508"
42087       ],
42088       [
42089         "Saint Vincent and the Grenadines",
42090         "vc",
42091         "1784"
42092       ],
42093       [
42094         "Samoa",
42095         "ws",
42096         "685"
42097       ],
42098       [
42099         "San Marino",
42100         "sm",
42101         "378"
42102       ],
42103       [
42104         "São Tomé and Príncipe (São Tomé e Príncipe)",
42105         "st",
42106         "239"
42107       ],
42108       [
42109         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42110         "sa",
42111         "966"
42112       ],
42113       [
42114         "Senegal (Sénégal)",
42115         "sn",
42116         "221"
42117       ],
42118       [
42119         "Serbia (Србија)",
42120         "rs",
42121         "381"
42122       ],
42123       [
42124         "Seychelles",
42125         "sc",
42126         "248"
42127       ],
42128       [
42129         "Sierra Leone",
42130         "sl",
42131         "232"
42132       ],
42133       [
42134         "Singapore",
42135         "sg",
42136         "65"
42137       ],
42138       [
42139         "Sint Maarten",
42140         "sx",
42141         "1721"
42142       ],
42143       [
42144         "Slovakia (Slovensko)",
42145         "sk",
42146         "421"
42147       ],
42148       [
42149         "Slovenia (Slovenija)",
42150         "si",
42151         "386"
42152       ],
42153       [
42154         "Solomon Islands",
42155         "sb",
42156         "677"
42157       ],
42158       [
42159         "Somalia (Soomaaliya)",
42160         "so",
42161         "252"
42162       ],
42163       [
42164         "South Africa",
42165         "za",
42166         "27"
42167       ],
42168       [
42169         "South Korea (대한민국)",
42170         "kr",
42171         "82"
42172       ],
42173       [
42174         "South Sudan (‫جنوب السودان‬‎)",
42175         "ss",
42176         "211"
42177       ],
42178       [
42179         "Spain (España)",
42180         "es",
42181         "34"
42182       ],
42183       [
42184         "Sri Lanka (ශ්‍රී ලංකාව)",
42185         "lk",
42186         "94"
42187       ],
42188       [
42189         "Sudan (‫السودان‬‎)",
42190         "sd",
42191         "249"
42192       ],
42193       [
42194         "Suriname",
42195         "sr",
42196         "597"
42197       ],
42198       [
42199         "Svalbard and Jan Mayen",
42200         "sj",
42201         "47",
42202         1
42203       ],
42204       [
42205         "Swaziland",
42206         "sz",
42207         "268"
42208       ],
42209       [
42210         "Sweden (Sverige)",
42211         "se",
42212         "46"
42213       ],
42214       [
42215         "Switzerland (Schweiz)",
42216         "ch",
42217         "41"
42218       ],
42219       [
42220         "Syria (‫سوريا‬‎)",
42221         "sy",
42222         "963"
42223       ],
42224       [
42225         "Taiwan (台灣)",
42226         "tw",
42227         "886"
42228       ],
42229       [
42230         "Tajikistan",
42231         "tj",
42232         "992"
42233       ],
42234       [
42235         "Tanzania",
42236         "tz",
42237         "255"
42238       ],
42239       [
42240         "Thailand (ไทย)",
42241         "th",
42242         "66"
42243       ],
42244       [
42245         "Timor-Leste",
42246         "tl",
42247         "670"
42248       ],
42249       [
42250         "Togo",
42251         "tg",
42252         "228"
42253       ],
42254       [
42255         "Tokelau",
42256         "tk",
42257         "690"
42258       ],
42259       [
42260         "Tonga",
42261         "to",
42262         "676"
42263       ],
42264       [
42265         "Trinidad and Tobago",
42266         "tt",
42267         "1868"
42268       ],
42269       [
42270         "Tunisia (‫تونس‬‎)",
42271         "tn",
42272         "216"
42273       ],
42274       [
42275         "Turkey (Türkiye)",
42276         "tr",
42277         "90"
42278       ],
42279       [
42280         "Turkmenistan",
42281         "tm",
42282         "993"
42283       ],
42284       [
42285         "Turks and Caicos Islands",
42286         "tc",
42287         "1649"
42288       ],
42289       [
42290         "Tuvalu",
42291         "tv",
42292         "688"
42293       ],
42294       [
42295         "U.S. Virgin Islands",
42296         "vi",
42297         "1340"
42298       ],
42299       [
42300         "Uganda",
42301         "ug",
42302         "256"
42303       ],
42304       [
42305         "Ukraine (Україна)",
42306         "ua",
42307         "380"
42308       ],
42309       [
42310         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42311         "ae",
42312         "971"
42313       ],
42314       [
42315         "United Kingdom",
42316         "gb",
42317         "44",
42318         0
42319       ],
42320       [
42321         "United States",
42322         "us",
42323         "1",
42324         0
42325       ],
42326       [
42327         "Uruguay",
42328         "uy",
42329         "598"
42330       ],
42331       [
42332         "Uzbekistan (Oʻzbekiston)",
42333         "uz",
42334         "998"
42335       ],
42336       [
42337         "Vanuatu",
42338         "vu",
42339         "678"
42340       ],
42341       [
42342         "Vatican City (Città del Vaticano)",
42343         "va",
42344         "39",
42345         1
42346       ],
42347       [
42348         "Venezuela",
42349         "ve",
42350         "58"
42351       ],
42352       [
42353         "Vietnam (Việt Nam)",
42354         "vn",
42355         "84"
42356       ],
42357       [
42358         "Wallis and Futuna (Wallis-et-Futuna)",
42359         "wf",
42360         "681"
42361       ],
42362       [
42363         "Western Sahara (‫الصحراء الغربية‬‎)",
42364         "eh",
42365         "212",
42366         1
42367       ],
42368       [
42369         "Yemen (‫اليمن‬‎)",
42370         "ye",
42371         "967"
42372       ],
42373       [
42374         "Zambia",
42375         "zm",
42376         "260"
42377       ],
42378       [
42379         "Zimbabwe",
42380         "zw",
42381         "263"
42382       ],
42383       [
42384         "Åland Islands",
42385         "ax",
42386         "358",
42387         1
42388       ]
42389   ];
42390   
42391   return d;
42392 }/**
42393 *    This script refer to:
42394 *    Title: International Telephone Input
42395 *    Author: Jack O'Connor
42396 *    Code version:  v12.1.12
42397 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42398 **/
42399
42400 /**
42401  * @class Roo.bootstrap.PhoneInput
42402  * @extends Roo.bootstrap.TriggerField
42403  * An input with International dial-code selection
42404  
42405  * @cfg {String} defaultDialCode default '+852'
42406  * @cfg {Array} preferedCountries default []
42407   
42408  * @constructor
42409  * Create a new PhoneInput.
42410  * @param {Object} config Configuration options
42411  */
42412
42413 Roo.bootstrap.PhoneInput = function(config) {
42414     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42415 };
42416
42417 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42418         
42419         listWidth: undefined,
42420         
42421         selectedClass: 'active',
42422         
42423         invalidClass : "has-warning",
42424         
42425         validClass: 'has-success',
42426         
42427         allowed: '0123456789',
42428         
42429         max_length: 15,
42430         
42431         /**
42432          * @cfg {String} defaultDialCode The default dial code when initializing the input
42433          */
42434         defaultDialCode: '+852',
42435         
42436         /**
42437          * @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
42438          */
42439         preferedCountries: false,
42440         
42441         getAutoCreate : function()
42442         {
42443             var data = Roo.bootstrap.PhoneInputData();
42444             var align = this.labelAlign || this.parentLabelAlign();
42445             var id = Roo.id();
42446             
42447             this.allCountries = [];
42448             this.dialCodeMapping = [];
42449             
42450             for (var i = 0; i < data.length; i++) {
42451               var c = data[i];
42452               this.allCountries[i] = {
42453                 name: c[0],
42454                 iso2: c[1],
42455                 dialCode: c[2],
42456                 priority: c[3] || 0,
42457                 areaCodes: c[4] || null
42458               };
42459               this.dialCodeMapping[c[2]] = {
42460                   name: c[0],
42461                   iso2: c[1],
42462                   priority: c[3] || 0,
42463                   areaCodes: c[4] || null
42464               };
42465             }
42466             
42467             var cfg = {
42468                 cls: 'form-group',
42469                 cn: []
42470             };
42471             
42472             var input =  {
42473                 tag: 'input',
42474                 id : id,
42475                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42476                 maxlength: this.max_length,
42477                 cls : 'form-control tel-input',
42478                 autocomplete: 'new-password'
42479             };
42480             
42481             var hiddenInput = {
42482                 tag: 'input',
42483                 type: 'hidden',
42484                 cls: 'hidden-tel-input'
42485             };
42486             
42487             if (this.name) {
42488                 hiddenInput.name = this.name;
42489             }
42490             
42491             if (this.disabled) {
42492                 input.disabled = true;
42493             }
42494             
42495             var flag_container = {
42496                 tag: 'div',
42497                 cls: 'flag-box',
42498                 cn: [
42499                     {
42500                         tag: 'div',
42501                         cls: 'flag'
42502                     },
42503                     {
42504                         tag: 'div',
42505                         cls: 'caret'
42506                     }
42507                 ]
42508             };
42509             
42510             var box = {
42511                 tag: 'div',
42512                 cls: this.hasFeedback ? 'has-feedback' : '',
42513                 cn: [
42514                     hiddenInput,
42515                     input,
42516                     {
42517                         tag: 'input',
42518                         cls: 'dial-code-holder',
42519                         disabled: true
42520                     }
42521                 ]
42522             };
42523             
42524             var container = {
42525                 cls: 'roo-select2-container input-group',
42526                 cn: [
42527                     flag_container,
42528                     box
42529                 ]
42530             };
42531             
42532             if (this.fieldLabel.length) {
42533                 var indicator = {
42534                     tag: 'i',
42535                     tooltip: 'This field is required'
42536                 };
42537                 
42538                 var label = {
42539                     tag: 'label',
42540                     'for':  id,
42541                     cls: 'control-label',
42542                     cn: []
42543                 };
42544                 
42545                 var label_text = {
42546                     tag: 'span',
42547                     html: this.fieldLabel
42548                 };
42549                 
42550                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42551                 label.cn = [
42552                     indicator,
42553                     label_text
42554                 ];
42555                 
42556                 if(this.indicatorpos == 'right') {
42557                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42558                     label.cn = [
42559                         label_text,
42560                         indicator
42561                     ];
42562                 }
42563                 
42564                 if(align == 'left') {
42565                     container = {
42566                         tag: 'div',
42567                         cn: [
42568                             container
42569                         ]
42570                     };
42571                     
42572                     if(this.labelWidth > 12){
42573                         label.style = "width: " + this.labelWidth + 'px';
42574                     }
42575                     if(this.labelWidth < 13 && this.labelmd == 0){
42576                         this.labelmd = this.labelWidth;
42577                     }
42578                     if(this.labellg > 0){
42579                         label.cls += ' col-lg-' + this.labellg;
42580                         input.cls += ' col-lg-' + (12 - this.labellg);
42581                     }
42582                     if(this.labelmd > 0){
42583                         label.cls += ' col-md-' + this.labelmd;
42584                         container.cls += ' col-md-' + (12 - this.labelmd);
42585                     }
42586                     if(this.labelsm > 0){
42587                         label.cls += ' col-sm-' + this.labelsm;
42588                         container.cls += ' col-sm-' + (12 - this.labelsm);
42589                     }
42590                     if(this.labelxs > 0){
42591                         label.cls += ' col-xs-' + this.labelxs;
42592                         container.cls += ' col-xs-' + (12 - this.labelxs);
42593                     }
42594                 }
42595             }
42596             
42597             cfg.cn = [
42598                 label,
42599                 container
42600             ];
42601             
42602             var settings = this;
42603             
42604             ['xs','sm','md','lg'].map(function(size){
42605                 if (settings[size]) {
42606                     cfg.cls += ' col-' + size + '-' + settings[size];
42607                 }
42608             });
42609             
42610             this.store = new Roo.data.Store({
42611                 proxy : new Roo.data.MemoryProxy({}),
42612                 reader : new Roo.data.JsonReader({
42613                     fields : [
42614                         {
42615                             'name' : 'name',
42616                             'type' : 'string'
42617                         },
42618                         {
42619                             'name' : 'iso2',
42620                             'type' : 'string'
42621                         },
42622                         {
42623                             'name' : 'dialCode',
42624                             'type' : 'string'
42625                         },
42626                         {
42627                             'name' : 'priority',
42628                             'type' : 'string'
42629                         },
42630                         {
42631                             'name' : 'areaCodes',
42632                             'type' : 'string'
42633                         }
42634                     ]
42635                 })
42636             });
42637             
42638             if(!this.preferedCountries) {
42639                 this.preferedCountries = [
42640                     'hk',
42641                     'gb',
42642                     'us'
42643                 ];
42644             }
42645             
42646             var p = this.preferedCountries.reverse();
42647             
42648             if(p) {
42649                 for (var i = 0; i < p.length; i++) {
42650                     for (var j = 0; j < this.allCountries.length; j++) {
42651                         if(this.allCountries[j].iso2 == p[i]) {
42652                             var t = this.allCountries[j];
42653                             this.allCountries.splice(j,1);
42654                             this.allCountries.unshift(t);
42655                         }
42656                     } 
42657                 }
42658             }
42659             
42660             this.store.proxy.data = {
42661                 success: true,
42662                 data: this.allCountries
42663             };
42664             
42665             return cfg;
42666         },
42667         
42668         initEvents : function()
42669         {
42670             this.createList();
42671             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42672             
42673             this.indicator = this.indicatorEl();
42674             this.flag = this.flagEl();
42675             this.dialCodeHolder = this.dialCodeHolderEl();
42676             
42677             this.trigger = this.el.select('div.flag-box',true).first();
42678             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42679             
42680             var _this = this;
42681             
42682             (function(){
42683                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42684                 _this.list.setWidth(lw);
42685             }).defer(100);
42686             
42687             this.list.on('mouseover', this.onViewOver, this);
42688             this.list.on('mousemove', this.onViewMove, this);
42689             this.inputEl().on("keyup", this.onKeyUp, this);
42690             this.inputEl().on("keypress", this.onKeyPress, this);
42691             
42692             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42693
42694             this.view = new Roo.View(this.list, this.tpl, {
42695                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42696             });
42697             
42698             this.view.on('click', this.onViewClick, this);
42699             this.setValue(this.defaultDialCode);
42700         },
42701         
42702         onTriggerClick : function(e)
42703         {
42704             Roo.log('trigger click');
42705             if(this.disabled){
42706                 return;
42707             }
42708             
42709             if(this.isExpanded()){
42710                 this.collapse();
42711                 this.hasFocus = false;
42712             }else {
42713                 this.store.load({});
42714                 this.hasFocus = true;
42715                 this.expand();
42716             }
42717         },
42718         
42719         isExpanded : function()
42720         {
42721             return this.list.isVisible();
42722         },
42723         
42724         collapse : function()
42725         {
42726             if(!this.isExpanded()){
42727                 return;
42728             }
42729             this.list.hide();
42730             Roo.get(document).un('mousedown', this.collapseIf, this);
42731             Roo.get(document).un('mousewheel', this.collapseIf, this);
42732             this.fireEvent('collapse', this);
42733             this.validate();
42734         },
42735         
42736         expand : function()
42737         {
42738             Roo.log('expand');
42739
42740             if(this.isExpanded() || !this.hasFocus){
42741                 return;
42742             }
42743             
42744             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42745             this.list.setWidth(lw);
42746             
42747             this.list.show();
42748             this.restrictHeight();
42749             
42750             Roo.get(document).on('mousedown', this.collapseIf, this);
42751             Roo.get(document).on('mousewheel', this.collapseIf, this);
42752             
42753             this.fireEvent('expand', this);
42754         },
42755         
42756         restrictHeight : function()
42757         {
42758             this.list.alignTo(this.inputEl(), this.listAlign);
42759             this.list.alignTo(this.inputEl(), this.listAlign);
42760         },
42761         
42762         onViewOver : function(e, t)
42763         {
42764             if(this.inKeyMode){
42765                 return;
42766             }
42767             var item = this.view.findItemFromChild(t);
42768             
42769             if(item){
42770                 var index = this.view.indexOf(item);
42771                 this.select(index, false);
42772             }
42773         },
42774
42775         // private
42776         onViewClick : function(view, doFocus, el, e)
42777         {
42778             var index = this.view.getSelectedIndexes()[0];
42779             
42780             var r = this.store.getAt(index);
42781             
42782             if(r){
42783                 this.onSelect(r, index);
42784             }
42785             if(doFocus !== false && !this.blockFocus){
42786                 this.inputEl().focus();
42787             }
42788         },
42789         
42790         onViewMove : function(e, t)
42791         {
42792             this.inKeyMode = false;
42793         },
42794         
42795         select : function(index, scrollIntoView)
42796         {
42797             this.selectedIndex = index;
42798             this.view.select(index);
42799             if(scrollIntoView !== false){
42800                 var el = this.view.getNode(index);
42801                 if(el){
42802                     this.list.scrollChildIntoView(el, false);
42803                 }
42804             }
42805         },
42806         
42807         createList : function()
42808         {
42809             this.list = Roo.get(document.body).createChild({
42810                 tag: 'ul',
42811                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42812                 style: 'display:none'
42813             });
42814             
42815             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42816         },
42817         
42818         collapseIf : function(e)
42819         {
42820             var in_combo  = e.within(this.el);
42821             var in_list =  e.within(this.list);
42822             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42823             
42824             if (in_combo || in_list || is_list) {
42825                 return;
42826             }
42827             this.collapse();
42828         },
42829         
42830         onSelect : function(record, index)
42831         {
42832             if(this.fireEvent('beforeselect', this, record, index) !== false){
42833                 
42834                 this.setFlagClass(record.data.iso2);
42835                 this.setDialCode(record.data.dialCode);
42836                 this.hasFocus = false;
42837                 this.collapse();
42838                 this.fireEvent('select', this, record, index);
42839             }
42840         },
42841         
42842         flagEl : function()
42843         {
42844             var flag = this.el.select('div.flag',true).first();
42845             if(!flag){
42846                 return false;
42847             }
42848             return flag;
42849         },
42850         
42851         dialCodeHolderEl : function()
42852         {
42853             var d = this.el.select('input.dial-code-holder',true).first();
42854             if(!d){
42855                 return false;
42856             }
42857             return d;
42858         },
42859         
42860         setDialCode : function(v)
42861         {
42862             this.dialCodeHolder.dom.value = '+'+v;
42863         },
42864         
42865         setFlagClass : function(n)
42866         {
42867             this.flag.dom.className = 'flag '+n;
42868         },
42869         
42870         getValue : function()
42871         {
42872             var v = this.inputEl().getValue();
42873             if(this.dialCodeHolder) {
42874                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42875             }
42876             return v;
42877         },
42878         
42879         setValue : function(v)
42880         {
42881             var d = this.getDialCode(v);
42882             
42883             //invalid dial code
42884             if(v.length == 0 || !d || d.length == 0) {
42885                 if(this.rendered){
42886                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42887                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42888                 }
42889                 return;
42890             }
42891             
42892             //valid dial code
42893             this.setFlagClass(this.dialCodeMapping[d].iso2);
42894             this.setDialCode(d);
42895             this.inputEl().dom.value = v.replace('+'+d,'');
42896             this.hiddenEl().dom.value = this.getValue();
42897             
42898             this.validate();
42899         },
42900         
42901         getDialCode : function(v)
42902         {
42903             v = v ||  '';
42904             
42905             if (v.length == 0) {
42906                 return this.dialCodeHolder.dom.value;
42907             }
42908             
42909             var dialCode = "";
42910             if (v.charAt(0) != "+") {
42911                 return false;
42912             }
42913             var numericChars = "";
42914             for (var i = 1; i < v.length; i++) {
42915               var c = v.charAt(i);
42916               if (!isNaN(c)) {
42917                 numericChars += c;
42918                 if (this.dialCodeMapping[numericChars]) {
42919                   dialCode = v.substr(1, i);
42920                 }
42921                 if (numericChars.length == 4) {
42922                   break;
42923                 }
42924               }
42925             }
42926             return dialCode;
42927         },
42928         
42929         reset : function()
42930         {
42931             this.setValue(this.defaultDialCode);
42932             this.validate();
42933         },
42934         
42935         hiddenEl : function()
42936         {
42937             return this.el.select('input.hidden-tel-input',true).first();
42938         },
42939         
42940         // after setting val
42941         onKeyUp : function(e){
42942             this.setValue(this.getValue());
42943         },
42944         
42945         onKeyPress : function(e){
42946             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42947                 e.stopEvent();
42948             }
42949         }
42950         
42951 });
42952 /**
42953  * @class Roo.bootstrap.MoneyField
42954  * @extends Roo.bootstrap.ComboBox
42955  * Bootstrap MoneyField class
42956  * 
42957  * @constructor
42958  * Create a new MoneyField.
42959  * @param {Object} config Configuration options
42960  */
42961
42962 Roo.bootstrap.MoneyField = function(config) {
42963     
42964     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42965     
42966 };
42967
42968 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42969     
42970     /**
42971      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42972      */
42973     allowDecimals : true,
42974     /**
42975      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42976      */
42977     decimalSeparator : ".",
42978     /**
42979      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42980      */
42981     decimalPrecision : 0,
42982     /**
42983      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42984      */
42985     allowNegative : true,
42986     /**
42987      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42988      */
42989     allowZero: true,
42990     /**
42991      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42992      */
42993     minValue : Number.NEGATIVE_INFINITY,
42994     /**
42995      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42996      */
42997     maxValue : Number.MAX_VALUE,
42998     /**
42999      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43000      */
43001     minText : "The minimum value for this field is {0}",
43002     /**
43003      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43004      */
43005     maxText : "The maximum value for this field is {0}",
43006     /**
43007      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43008      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43009      */
43010     nanText : "{0} is not a valid number",
43011     /**
43012      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43013      */
43014     castInt : true,
43015     /**
43016      * @cfg {String} defaults currency of the MoneyField
43017      * value should be in lkey
43018      */
43019     defaultCurrency : false,
43020     /**
43021      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43022      */
43023     thousandsDelimiter : false,
43024     /**
43025      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43026      */
43027     max_length: false,
43028     
43029     inputlg : 9,
43030     inputmd : 9,
43031     inputsm : 9,
43032     inputxs : 6,
43033     
43034     store : false,
43035     
43036     getAutoCreate : function()
43037     {
43038         var align = this.labelAlign || this.parentLabelAlign();
43039         
43040         var id = Roo.id();
43041
43042         var cfg = {
43043             cls: 'form-group',
43044             cn: []
43045         };
43046
43047         var input =  {
43048             tag: 'input',
43049             id : id,
43050             cls : 'form-control roo-money-amount-input',
43051             autocomplete: 'new-password'
43052         };
43053         
43054         var hiddenInput = {
43055             tag: 'input',
43056             type: 'hidden',
43057             id: Roo.id(),
43058             cls: 'hidden-number-input'
43059         };
43060         
43061         if(this.max_length) {
43062             input.maxlength = this.max_length; 
43063         }
43064         
43065         if (this.name) {
43066             hiddenInput.name = this.name;
43067         }
43068
43069         if (this.disabled) {
43070             input.disabled = true;
43071         }
43072
43073         var clg = 12 - this.inputlg;
43074         var cmd = 12 - this.inputmd;
43075         var csm = 12 - this.inputsm;
43076         var cxs = 12 - this.inputxs;
43077         
43078         var container = {
43079             tag : 'div',
43080             cls : 'row roo-money-field',
43081             cn : [
43082                 {
43083                     tag : 'div',
43084                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43085                     cn : [
43086                         {
43087                             tag : 'div',
43088                             cls: 'roo-select2-container input-group',
43089                             cn: [
43090                                 {
43091                                     tag : 'input',
43092                                     cls : 'form-control roo-money-currency-input',
43093                                     autocomplete: 'new-password',
43094                                     readOnly : 1,
43095                                     name : this.currencyName
43096                                 },
43097                                 {
43098                                     tag :'span',
43099                                     cls : 'input-group-addon',
43100                                     cn : [
43101                                         {
43102                                             tag: 'span',
43103                                             cls: 'caret'
43104                                         }
43105                                     ]
43106                                 }
43107                             ]
43108                         }
43109                     ]
43110                 },
43111                 {
43112                     tag : 'div',
43113                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43114                     cn : [
43115                         {
43116                             tag: 'div',
43117                             cls: this.hasFeedback ? 'has-feedback' : '',
43118                             cn: [
43119                                 input
43120                             ]
43121                         }
43122                     ]
43123                 }
43124             ]
43125             
43126         };
43127         
43128         if (this.fieldLabel.length) {
43129             var indicator = {
43130                 tag: 'i',
43131                 tooltip: 'This field is required'
43132             };
43133
43134             var label = {
43135                 tag: 'label',
43136                 'for':  id,
43137                 cls: 'control-label',
43138                 cn: []
43139             };
43140
43141             var label_text = {
43142                 tag: 'span',
43143                 html: this.fieldLabel
43144             };
43145
43146             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43147             label.cn = [
43148                 indicator,
43149                 label_text
43150             ];
43151
43152             if(this.indicatorpos == 'right') {
43153                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43154                 label.cn = [
43155                     label_text,
43156                     indicator
43157                 ];
43158             }
43159
43160             if(align == 'left') {
43161                 container = {
43162                     tag: 'div',
43163                     cn: [
43164                         container
43165                     ]
43166                 };
43167
43168                 if(this.labelWidth > 12){
43169                     label.style = "width: " + this.labelWidth + 'px';
43170                 }
43171                 if(this.labelWidth < 13 && this.labelmd == 0){
43172                     this.labelmd = this.labelWidth;
43173                 }
43174                 if(this.labellg > 0){
43175                     label.cls += ' col-lg-' + this.labellg;
43176                     input.cls += ' col-lg-' + (12 - this.labellg);
43177                 }
43178                 if(this.labelmd > 0){
43179                     label.cls += ' col-md-' + this.labelmd;
43180                     container.cls += ' col-md-' + (12 - this.labelmd);
43181                 }
43182                 if(this.labelsm > 0){
43183                     label.cls += ' col-sm-' + this.labelsm;
43184                     container.cls += ' col-sm-' + (12 - this.labelsm);
43185                 }
43186                 if(this.labelxs > 0){
43187                     label.cls += ' col-xs-' + this.labelxs;
43188                     container.cls += ' col-xs-' + (12 - this.labelxs);
43189                 }
43190             }
43191         }
43192
43193         cfg.cn = [
43194             label,
43195             container,
43196             hiddenInput
43197         ];
43198         
43199         var settings = this;
43200
43201         ['xs','sm','md','lg'].map(function(size){
43202             if (settings[size]) {
43203                 cfg.cls += ' col-' + size + '-' + settings[size];
43204             }
43205         });
43206         
43207         return cfg;
43208     },
43209     
43210     initEvents : function()
43211     {
43212         this.indicator = this.indicatorEl();
43213         
43214         this.initCurrencyEvent();
43215         
43216         this.initNumberEvent();
43217     },
43218     
43219     initCurrencyEvent : function()
43220     {
43221         if (!this.store) {
43222             throw "can not find store for combo";
43223         }
43224         
43225         this.store = Roo.factory(this.store, Roo.data);
43226         this.store.parent = this;
43227         
43228         this.createList();
43229         
43230         this.triggerEl = this.el.select('.input-group-addon', true).first();
43231         
43232         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43233         
43234         var _this = this;
43235         
43236         (function(){
43237             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43238             _this.list.setWidth(lw);
43239         }).defer(100);
43240         
43241         this.list.on('mouseover', this.onViewOver, this);
43242         this.list.on('mousemove', this.onViewMove, this);
43243         this.list.on('scroll', this.onViewScroll, this);
43244         
43245         if(!this.tpl){
43246             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43247         }
43248         
43249         this.view = new Roo.View(this.list, this.tpl, {
43250             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43251         });
43252         
43253         this.view.on('click', this.onViewClick, this);
43254         
43255         this.store.on('beforeload', this.onBeforeLoad, this);
43256         this.store.on('load', this.onLoad, this);
43257         this.store.on('loadexception', this.onLoadException, this);
43258         
43259         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43260             "up" : function(e){
43261                 this.inKeyMode = true;
43262                 this.selectPrev();
43263             },
43264
43265             "down" : function(e){
43266                 if(!this.isExpanded()){
43267                     this.onTriggerClick();
43268                 }else{
43269                     this.inKeyMode = true;
43270                     this.selectNext();
43271                 }
43272             },
43273
43274             "enter" : function(e){
43275                 this.collapse();
43276                 
43277                 if(this.fireEvent("specialkey", this, e)){
43278                     this.onViewClick(false);
43279                 }
43280                 
43281                 return true;
43282             },
43283
43284             "esc" : function(e){
43285                 this.collapse();
43286             },
43287
43288             "tab" : function(e){
43289                 this.collapse();
43290                 
43291                 if(this.fireEvent("specialkey", this, e)){
43292                     this.onViewClick(false);
43293                 }
43294                 
43295                 return true;
43296             },
43297
43298             scope : this,
43299
43300             doRelay : function(foo, bar, hname){
43301                 if(hname == 'down' || this.scope.isExpanded()){
43302                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43303                 }
43304                 return true;
43305             },
43306
43307             forceKeyDown: true
43308         });
43309         
43310         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43311         
43312     },
43313     
43314     initNumberEvent : function(e)
43315     {
43316         this.inputEl().on("keydown" , this.fireKey,  this);
43317         this.inputEl().on("focus", this.onFocus,  this);
43318         this.inputEl().on("blur", this.onBlur,  this);
43319         
43320         this.inputEl().relayEvent('keyup', this);
43321         
43322         if(this.indicator){
43323             this.indicator.addClass('invisible');
43324         }
43325  
43326         this.originalValue = this.getValue();
43327         
43328         if(this.validationEvent == 'keyup'){
43329             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43330             this.inputEl().on('keyup', this.filterValidation, this);
43331         }
43332         else if(this.validationEvent !== false){
43333             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43334         }
43335         
43336         if(this.selectOnFocus){
43337             this.on("focus", this.preFocus, this);
43338             
43339         }
43340         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43341             this.inputEl().on("keypress", this.filterKeys, this);
43342         } else {
43343             this.inputEl().relayEvent('keypress', this);
43344         }
43345         
43346         var allowed = "0123456789";
43347         
43348         if(this.allowDecimals){
43349             allowed += this.decimalSeparator;
43350         }
43351         
43352         if(this.allowNegative){
43353             allowed += "-";
43354         }
43355         
43356         if(this.thousandsDelimiter) {
43357             allowed += ",";
43358         }
43359         
43360         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43361         
43362         var keyPress = function(e){
43363             
43364             var k = e.getKey();
43365             
43366             var c = e.getCharCode();
43367             
43368             if(
43369                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43370                     allowed.indexOf(String.fromCharCode(c)) === -1
43371             ){
43372                 e.stopEvent();
43373                 return;
43374             }
43375             
43376             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43377                 return;
43378             }
43379             
43380             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43381                 e.stopEvent();
43382             }
43383         };
43384         
43385         this.inputEl().on("keypress", keyPress, this);
43386         
43387     },
43388     
43389     onTriggerClick : function(e)
43390     {   
43391         if(this.disabled){
43392             return;
43393         }
43394         
43395         this.page = 0;
43396         this.loadNext = false;
43397         
43398         if(this.isExpanded()){
43399             this.collapse();
43400             return;
43401         }
43402         
43403         this.hasFocus = true;
43404         
43405         if(this.triggerAction == 'all') {
43406             this.doQuery(this.allQuery, true);
43407             return;
43408         }
43409         
43410         this.doQuery(this.getRawValue());
43411     },
43412     
43413     getCurrency : function()
43414     {   
43415         var v = this.currencyEl().getValue();
43416         
43417         return v;
43418     },
43419     
43420     restrictHeight : function()
43421     {
43422         this.list.alignTo(this.currencyEl(), this.listAlign);
43423         this.list.alignTo(this.currencyEl(), this.listAlign);
43424     },
43425     
43426     onViewClick : function(view, doFocus, el, e)
43427     {
43428         var index = this.view.getSelectedIndexes()[0];
43429         
43430         var r = this.store.getAt(index);
43431         
43432         if(r){
43433             this.onSelect(r, index);
43434         }
43435     },
43436     
43437     onSelect : function(record, index){
43438         
43439         if(this.fireEvent('beforeselect', this, record, index) !== false){
43440         
43441             this.setFromCurrencyData(index > -1 ? record.data : false);
43442             
43443             this.collapse();
43444             
43445             this.fireEvent('select', this, record, index);
43446         }
43447     },
43448     
43449     setFromCurrencyData : function(o)
43450     {
43451         var currency = '';
43452         
43453         this.lastCurrency = o;
43454         
43455         if (this.currencyField) {
43456             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43457         } else {
43458             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43459         }
43460         
43461         this.lastSelectionText = currency;
43462         
43463         //setting default currency
43464         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43465             this.setCurrency(this.defaultCurrency);
43466             return;
43467         }
43468         
43469         this.setCurrency(currency);
43470     },
43471     
43472     setFromData : function(o)
43473     {
43474         var c = {};
43475         
43476         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43477         
43478         this.setFromCurrencyData(c);
43479         
43480         var value = '';
43481         
43482         if (this.name) {
43483             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43484         } else {
43485             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43486         }
43487         
43488         this.setValue(value);
43489         
43490     },
43491     
43492     setCurrency : function(v)
43493     {   
43494         this.currencyValue = v;
43495         
43496         if(this.rendered){
43497             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43498             this.validate();
43499         }
43500     },
43501     
43502     setValue : function(v)
43503     {
43504         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43505         
43506         this.value = v;
43507         
43508         if(this.rendered){
43509             
43510             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43511             
43512             this.inputEl().dom.value = (v == '') ? '' :
43513                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43514             
43515             if(!this.allowZero && v === '0') {
43516                 this.hiddenEl().dom.value = '';
43517                 this.inputEl().dom.value = '';
43518             }
43519             
43520             this.validate();
43521         }
43522     },
43523     
43524     getRawValue : function()
43525     {
43526         var v = this.inputEl().getValue();
43527         
43528         return v;
43529     },
43530     
43531     getValue : function()
43532     {
43533         return this.fixPrecision(this.parseValue(this.getRawValue()));
43534     },
43535     
43536     parseValue : function(value)
43537     {
43538         if(this.thousandsDelimiter) {
43539             value += "";
43540             r = new RegExp(",", "g");
43541             value = value.replace(r, "");
43542         }
43543         
43544         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43545         return isNaN(value) ? '' : value;
43546         
43547     },
43548     
43549     fixPrecision : function(value)
43550     {
43551         if(this.thousandsDelimiter) {
43552             value += "";
43553             r = new RegExp(",", "g");
43554             value = value.replace(r, "");
43555         }
43556         
43557         var nan = isNaN(value);
43558         
43559         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43560             return nan ? '' : value;
43561         }
43562         return parseFloat(value).toFixed(this.decimalPrecision);
43563     },
43564     
43565     decimalPrecisionFcn : function(v)
43566     {
43567         return Math.floor(v);
43568     },
43569     
43570     validateValue : function(value)
43571     {
43572         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43573             return false;
43574         }
43575         
43576         var num = this.parseValue(value);
43577         
43578         if(isNaN(num)){
43579             this.markInvalid(String.format(this.nanText, value));
43580             return false;
43581         }
43582         
43583         if(num < this.minValue){
43584             this.markInvalid(String.format(this.minText, this.minValue));
43585             return false;
43586         }
43587         
43588         if(num > this.maxValue){
43589             this.markInvalid(String.format(this.maxText, this.maxValue));
43590             return false;
43591         }
43592         
43593         return true;
43594     },
43595     
43596     validate : function()
43597     {
43598         if(this.disabled || this.allowBlank){
43599             this.markValid();
43600             return true;
43601         }
43602         
43603         var currency = this.getCurrency();
43604         
43605         if(this.validateValue(this.getRawValue()) && currency.length){
43606             this.markValid();
43607             return true;
43608         }
43609         
43610         this.markInvalid();
43611         return false;
43612     },
43613     
43614     getName: function()
43615     {
43616         return this.name;
43617     },
43618     
43619     beforeBlur : function()
43620     {
43621         if(!this.castInt){
43622             return;
43623         }
43624         
43625         var v = this.parseValue(this.getRawValue());
43626         
43627         if(v || v == 0){
43628             this.setValue(v);
43629         }
43630     },
43631     
43632     onBlur : function()
43633     {
43634         this.beforeBlur();
43635         
43636         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43637             //this.el.removeClass(this.focusClass);
43638         }
43639         
43640         this.hasFocus = false;
43641         
43642         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43643             this.validate();
43644         }
43645         
43646         var v = this.getValue();
43647         
43648         if(String(v) !== String(this.startValue)){
43649             this.fireEvent('change', this, v, this.startValue);
43650         }
43651         
43652         this.fireEvent("blur", this);
43653     },
43654     
43655     inputEl : function()
43656     {
43657         return this.el.select('.roo-money-amount-input', true).first();
43658     },
43659     
43660     currencyEl : function()
43661     {
43662         return this.el.select('.roo-money-currency-input', true).first();
43663     },
43664     
43665     hiddenEl : function()
43666     {
43667         return this.el.select('input.hidden-number-input',true).first();
43668     }
43669     
43670 });/**
43671  * @class Roo.bootstrap.BezierSignature
43672  * @extends Roo.bootstrap.Component
43673  * Bootstrap BezierSignature class
43674  * This script refer to:
43675  *    Title: Signature Pad
43676  *    Author: szimek
43677  *    Availability: https://github.com/szimek/signature_pad
43678  *
43679  * @constructor
43680  * Create a new BezierSignature
43681  * @param {Object} config The config object
43682  */
43683
43684 Roo.bootstrap.BezierSignature = function(config){
43685     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43686     this.addEvents({
43687         "resize" : true
43688     });
43689 };
43690
43691 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43692 {
43693      
43694     curve_data: [],
43695     
43696     is_empty: true,
43697     
43698     mouse_btn_down: true,
43699     
43700     /**
43701      * @cfg {int} canvas height
43702      */
43703     canvas_height: '200px',
43704     
43705     /**
43706      * @cfg {float|function} Radius of a single dot.
43707      */ 
43708     dot_size: false,
43709     
43710     /**
43711      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43712      */
43713     min_width: 0.5,
43714     
43715     /**
43716      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43717      */
43718     max_width: 2.5,
43719     
43720     /**
43721      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43722      */
43723     throttle: 16,
43724     
43725     /**
43726      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43727      */
43728     min_distance: 5,
43729     
43730     /**
43731      * @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.
43732      */
43733     bg_color: 'rgba(0, 0, 0, 0)',
43734     
43735     /**
43736      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43737      */
43738     dot_color: 'black',
43739     
43740     /**
43741      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43742      */ 
43743     velocity_filter_weight: 0.7,
43744     
43745     /**
43746      * @cfg {function} Callback when stroke begin. 
43747      */
43748     onBegin: false,
43749     
43750     /**
43751      * @cfg {function} Callback when stroke end.
43752      */
43753     onEnd: false,
43754     
43755     getAutoCreate : function()
43756     {
43757         var cls = 'roo-signature column';
43758         
43759         if(this.cls){
43760             cls += ' ' + this.cls;
43761         }
43762         
43763         var col_sizes = [
43764             'lg',
43765             'md',
43766             'sm',
43767             'xs'
43768         ];
43769         
43770         for(var i = 0; i < col_sizes.length; i++) {
43771             if(this[col_sizes[i]]) {
43772                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43773             }
43774         }
43775         
43776         var cfg = {
43777             tag: 'div',
43778             cls: cls,
43779             cn: [
43780                 {
43781                     tag: 'div',
43782                     cls: 'roo-signature-body',
43783                     cn: [
43784                         {
43785                             tag: 'canvas',
43786                             cls: 'roo-signature-body-canvas',
43787                             height: this.canvas_height,
43788                             width: this.canvas_width
43789                         }
43790                     ]
43791                 },
43792                 {
43793                     tag: 'input',
43794                     type: 'file',
43795                     style: 'display: none'
43796                 }
43797             ]
43798         };
43799         
43800         return cfg;
43801     },
43802     
43803     initEvents: function() 
43804     {
43805         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43806         
43807         var canvas = this.canvasEl();
43808         
43809         // mouse && touch event swapping...
43810         canvas.dom.style.touchAction = 'none';
43811         canvas.dom.style.msTouchAction = 'none';
43812         
43813         this.mouse_btn_down = false;
43814         canvas.on('mousedown', this._handleMouseDown, this);
43815         canvas.on('mousemove', this._handleMouseMove, this);
43816         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43817         
43818         if (window.PointerEvent) {
43819             canvas.on('pointerdown', this._handleMouseDown, this);
43820             canvas.on('pointermove', this._handleMouseMove, this);
43821             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43822         }
43823         
43824         if ('ontouchstart' in window) {
43825             canvas.on('touchstart', this._handleTouchStart, this);
43826             canvas.on('touchmove', this._handleTouchMove, this);
43827             canvas.on('touchend', this._handleTouchEnd, this);
43828         }
43829         
43830         Roo.EventManager.onWindowResize(this.resize, this, true);
43831         
43832         // file input event
43833         this.fileEl().on('change', this.uploadImage, this);
43834         
43835         this.clear();
43836         
43837         this.resize();
43838     },
43839     
43840     resize: function(){
43841         
43842         var canvas = this.canvasEl().dom;
43843         var ctx = this.canvasElCtx();
43844         var img_data = false;
43845         
43846         if(canvas.width > 0) {
43847             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43848         }
43849         // setting canvas width will clean img data
43850         canvas.width = 0;
43851         
43852         var style = window.getComputedStyle ? 
43853             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43854             
43855         var padding_left = parseInt(style.paddingLeft) || 0;
43856         var padding_right = parseInt(style.paddingRight) || 0;
43857         
43858         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43859         
43860         if(img_data) {
43861             ctx.putImageData(img_data, 0, 0);
43862         }
43863     },
43864     
43865     _handleMouseDown: function(e)
43866     {
43867         if (e.browserEvent.which === 1) {
43868             this.mouse_btn_down = true;
43869             this.strokeBegin(e);
43870         }
43871     },
43872     
43873     _handleMouseMove: function (e)
43874     {
43875         if (this.mouse_btn_down) {
43876             this.strokeMoveUpdate(e);
43877         }
43878     },
43879     
43880     _handleMouseUp: function (e)
43881     {
43882         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43883             this.mouse_btn_down = false;
43884             this.strokeEnd(e);
43885         }
43886     },
43887     
43888     _handleTouchStart: function (e) {
43889         
43890         e.preventDefault();
43891         if (e.browserEvent.targetTouches.length === 1) {
43892             // var touch = e.browserEvent.changedTouches[0];
43893             // this.strokeBegin(touch);
43894             
43895              this.strokeBegin(e); // assume e catching the correct xy...
43896         }
43897     },
43898     
43899     _handleTouchMove: function (e) {
43900         e.preventDefault();
43901         // var touch = event.targetTouches[0];
43902         // _this._strokeMoveUpdate(touch);
43903         this.strokeMoveUpdate(e);
43904     },
43905     
43906     _handleTouchEnd: function (e) {
43907         var wasCanvasTouched = e.target === this.canvasEl().dom;
43908         if (wasCanvasTouched) {
43909             e.preventDefault();
43910             // var touch = event.changedTouches[0];
43911             // _this._strokeEnd(touch);
43912             this.strokeEnd(e);
43913         }
43914     },
43915     
43916     reset: function () {
43917         this._lastPoints = [];
43918         this._lastVelocity = 0;
43919         this._lastWidth = (this.min_width + this.max_width) / 2;
43920         this.canvasElCtx().fillStyle = this.dot_color;
43921     },
43922     
43923     strokeMoveUpdate: function(e)
43924     {
43925         this.strokeUpdate(e);
43926         
43927         if (this.throttle) {
43928             this.throttleStroke(this.strokeUpdate, this.throttle);
43929         }
43930         else {
43931             this.strokeUpdate(e);
43932         }
43933     },
43934     
43935     strokeBegin: function(e)
43936     {
43937         var newPointGroup = {
43938             color: this.dot_color,
43939             points: []
43940         };
43941         
43942         if (typeof this.onBegin === 'function') {
43943             this.onBegin(e);
43944         }
43945         
43946         this.curve_data.push(newPointGroup);
43947         this.reset();
43948         this.strokeUpdate(e);
43949     },
43950     
43951     strokeUpdate: function(e)
43952     {
43953         var rect = this.canvasEl().dom.getBoundingClientRect();
43954         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43955         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43956         var lastPoints = lastPointGroup.points;
43957         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43958         var isLastPointTooClose = lastPoint
43959             ? point.distanceTo(lastPoint) <= this.min_distance
43960             : false;
43961         var color = lastPointGroup.color;
43962         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43963             var curve = this.addPoint(point);
43964             if (!lastPoint) {
43965                 this.drawDot({color: color, point: point});
43966             }
43967             else if (curve) {
43968                 this.drawCurve({color: color, curve: curve});
43969             }
43970             lastPoints.push({
43971                 time: point.time,
43972                 x: point.x,
43973                 y: point.y
43974             });
43975         }
43976     },
43977     
43978     strokeEnd: function(e)
43979     {
43980         this.strokeUpdate(e);
43981         if (typeof this.onEnd === 'function') {
43982             this.onEnd(e);
43983         }
43984     },
43985     
43986     addPoint:  function (point) {
43987         var _lastPoints = this._lastPoints;
43988         _lastPoints.push(point);
43989         if (_lastPoints.length > 2) {
43990             if (_lastPoints.length === 3) {
43991                 _lastPoints.unshift(_lastPoints[0]);
43992             }
43993             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43994             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43995             _lastPoints.shift();
43996             return curve;
43997         }
43998         return null;
43999     },
44000     
44001     calculateCurveWidths: function (startPoint, endPoint) {
44002         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44003             (1 - this.velocity_filter_weight) * this._lastVelocity;
44004
44005         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44006         var widths = {
44007             end: newWidth,
44008             start: this._lastWidth
44009         };
44010         
44011         this._lastVelocity = velocity;
44012         this._lastWidth = newWidth;
44013         return widths;
44014     },
44015     
44016     drawDot: function (_a) {
44017         var color = _a.color, point = _a.point;
44018         var ctx = this.canvasElCtx();
44019         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44020         ctx.beginPath();
44021         this.drawCurveSegment(point.x, point.y, width);
44022         ctx.closePath();
44023         ctx.fillStyle = color;
44024         ctx.fill();
44025     },
44026     
44027     drawCurve: function (_a) {
44028         var color = _a.color, curve = _a.curve;
44029         var ctx = this.canvasElCtx();
44030         var widthDelta = curve.endWidth - curve.startWidth;
44031         var drawSteps = Math.floor(curve.length()) * 2;
44032         ctx.beginPath();
44033         ctx.fillStyle = color;
44034         for (var i = 0; i < drawSteps; i += 1) {
44035         var t = i / drawSteps;
44036         var tt = t * t;
44037         var ttt = tt * t;
44038         var u = 1 - t;
44039         var uu = u * u;
44040         var uuu = uu * u;
44041         var x = uuu * curve.startPoint.x;
44042         x += 3 * uu * t * curve.control1.x;
44043         x += 3 * u * tt * curve.control2.x;
44044         x += ttt * curve.endPoint.x;
44045         var y = uuu * curve.startPoint.y;
44046         y += 3 * uu * t * curve.control1.y;
44047         y += 3 * u * tt * curve.control2.y;
44048         y += ttt * curve.endPoint.y;
44049         var width = curve.startWidth + ttt * widthDelta;
44050         this.drawCurveSegment(x, y, width);
44051         }
44052         ctx.closePath();
44053         ctx.fill();
44054     },
44055     
44056     drawCurveSegment: function (x, y, width) {
44057         var ctx = this.canvasElCtx();
44058         ctx.moveTo(x, y);
44059         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44060         this.is_empty = false;
44061     },
44062     
44063     clear: function()
44064     {
44065         var ctx = this.canvasElCtx();
44066         var canvas = this.canvasEl().dom;
44067         ctx.fillStyle = this.bg_color;
44068         ctx.clearRect(0, 0, canvas.width, canvas.height);
44069         ctx.fillRect(0, 0, canvas.width, canvas.height);
44070         this.curve_data = [];
44071         this.reset();
44072         this.is_empty = true;
44073     },
44074     
44075     fileEl: function()
44076     {
44077         return  this.el.select('input',true).first();
44078     },
44079     
44080     canvasEl: function()
44081     {
44082         return this.el.select('canvas',true).first();
44083     },
44084     
44085     canvasElCtx: function()
44086     {
44087         return this.el.select('canvas',true).first().dom.getContext('2d');
44088     },
44089     
44090     getImage: function(type)
44091     {
44092         if(this.is_empty) {
44093             return false;
44094         }
44095         
44096         // encryption ?
44097         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44098     },
44099     
44100     drawFromImage: function(img_src)
44101     {
44102         var img = new Image();
44103         
44104         img.onload = function(){
44105             this.canvasElCtx().drawImage(img, 0, 0);
44106         }.bind(this);
44107         
44108         img.src = img_src;
44109         
44110         this.is_empty = false;
44111     },
44112     
44113     selectImage: function()
44114     {
44115         this.fileEl().dom.click();
44116     },
44117     
44118     uploadImage: function(e)
44119     {
44120         var reader = new FileReader();
44121         
44122         reader.onload = function(e){
44123             var img = new Image();
44124             img.onload = function(){
44125                 this.reset();
44126                 this.canvasElCtx().drawImage(img, 0, 0);
44127             }.bind(this);
44128             img.src = e.target.result;
44129         }.bind(this);
44130         
44131         reader.readAsDataURL(e.target.files[0]);
44132     },
44133     
44134     // Bezier Point Constructor
44135     Point: (function () {
44136         function Point(x, y, time) {
44137             this.x = x;
44138             this.y = y;
44139             this.time = time || Date.now();
44140         }
44141         Point.prototype.distanceTo = function (start) {
44142             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44143         };
44144         Point.prototype.equals = function (other) {
44145             return this.x === other.x && this.y === other.y && this.time === other.time;
44146         };
44147         Point.prototype.velocityFrom = function (start) {
44148             return this.time !== start.time
44149             ? this.distanceTo(start) / (this.time - start.time)
44150             : 0;
44151         };
44152         return Point;
44153     }()),
44154     
44155     
44156     // Bezier Constructor
44157     Bezier: (function () {
44158         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44159             this.startPoint = startPoint;
44160             this.control2 = control2;
44161             this.control1 = control1;
44162             this.endPoint = endPoint;
44163             this.startWidth = startWidth;
44164             this.endWidth = endWidth;
44165         }
44166         Bezier.fromPoints = function (points, widths, scope) {
44167             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44168             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44169             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44170         };
44171         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44172             var dx1 = s1.x - s2.x;
44173             var dy1 = s1.y - s2.y;
44174             var dx2 = s2.x - s3.x;
44175             var dy2 = s2.y - s3.y;
44176             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44177             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44178             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44179             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44180             var dxm = m1.x - m2.x;
44181             var dym = m1.y - m2.y;
44182             var k = l2 / (l1 + l2);
44183             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44184             var tx = s2.x - cm.x;
44185             var ty = s2.y - cm.y;
44186             return {
44187                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44188                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44189             };
44190         };
44191         Bezier.prototype.length = function () {
44192             var steps = 10;
44193             var length = 0;
44194             var px;
44195             var py;
44196             for (var i = 0; i <= steps; i += 1) {
44197                 var t = i / steps;
44198                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44199                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44200                 if (i > 0) {
44201                     var xdiff = cx - px;
44202                     var ydiff = cy - py;
44203                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44204                 }
44205                 px = cx;
44206                 py = cy;
44207             }
44208             return length;
44209         };
44210         Bezier.prototype.point = function (t, start, c1, c2, end) {
44211             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44212             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44213             + (3.0 * c2 * (1.0 - t) * t * t)
44214             + (end * t * t * t);
44215         };
44216         return Bezier;
44217     }()),
44218     
44219     throttleStroke: function(fn, wait) {
44220       if (wait === void 0) { wait = 250; }
44221       var previous = 0;
44222       var timeout = null;
44223       var result;
44224       var storedContext;
44225       var storedArgs;
44226       var later = function () {
44227           previous = Date.now();
44228           timeout = null;
44229           result = fn.apply(storedContext, storedArgs);
44230           if (!timeout) {
44231               storedContext = null;
44232               storedArgs = [];
44233           }
44234       };
44235       return function wrapper() {
44236           var args = [];
44237           for (var _i = 0; _i < arguments.length; _i++) {
44238               args[_i] = arguments[_i];
44239           }
44240           var now = Date.now();
44241           var remaining = wait - (now - previous);
44242           storedContext = this;
44243           storedArgs = args;
44244           if (remaining <= 0 || remaining > wait) {
44245               if (timeout) {
44246                   clearTimeout(timeout);
44247                   timeout = null;
44248               }
44249               previous = now;
44250               result = fn.apply(storedContext, storedArgs);
44251               if (!timeout) {
44252                   storedContext = null;
44253                   storedArgs = [];
44254               }
44255           }
44256           else if (!timeout) {
44257               timeout = window.setTimeout(later, remaining);
44258           }
44259           return result;
44260       };
44261   }
44262   
44263 });
44264
44265  
44266
44267