roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3951  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3952  * @cfg {String} size (sm|lg|xl) default empty
3953  * @cfg {Number} max_width set the max width of modal
3954  * @cfg {Boolean} editableTitle can the title be edited
3955
3956  *
3957  *
3958  * @constructor
3959  * Create a new Modal Dialog
3960  * @param {Object} config The config object
3961  */
3962
3963 Roo.bootstrap.Modal = function(config){
3964     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3965     this.addEvents({
3966         // raw events
3967         /**
3968          * @event btnclick
3969          * The raw btnclick event for the button
3970          * @param {Roo.EventObject} e
3971          */
3972         "btnclick" : true,
3973         /**
3974          * @event resize
3975          * Fire when dialog resize
3976          * @param {Roo.bootstrap.Modal} this
3977          * @param {Roo.EventObject} e
3978          */
3979         "resize" : true,
3980         /**
3981          * @event titlechanged
3982          * Fire when the editable title has been changed
3983          * @param {Roo.bootstrap.Modal} this
3984          * @param {Roo.EventObject} value
3985          */
3986         "titlechanged" : true 
3987         
3988     });
3989     this.buttons = this.buttons || [];
3990
3991     if (this.tmpl) {
3992         this.tmpl = Roo.factory(this.tmpl);
3993     }
3994
3995 };
3996
3997 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3998
3999     title : 'test dialog',
4000
4001     buttons : false,
4002
4003     // set on load...
4004
4005     html: false,
4006
4007     tmp: false,
4008
4009     specificTitle: false,
4010
4011     buttonPosition: 'right',
4012
4013     allow_close : true,
4014
4015     animate : true,
4016
4017     fitwindow: false,
4018     
4019      // private
4020     dialogEl: false,
4021     bodyEl:  false,
4022     footerEl:  false,
4023     titleEl:  false,
4024     closeEl:  false,
4025
4026     size: '',
4027     
4028     max_width: 0,
4029     
4030     max_height: 0,
4031     
4032     fit_content: false,
4033     editableTitle  : false,
4034
4035     onRender : function(ct, position)
4036     {
4037         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4038
4039         if(!this.el){
4040             var cfg = Roo.apply({},  this.getAutoCreate());
4041             cfg.id = Roo.id();
4042             //if(!cfg.name){
4043             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4044             //}
4045             //if (!cfg.name.length) {
4046             //    delete cfg.name;
4047            // }
4048             if (this.cls) {
4049                 cfg.cls += ' ' + this.cls;
4050             }
4051             if (this.style) {
4052                 cfg.style = this.style;
4053             }
4054             this.el = Roo.get(document.body).createChild(cfg, position);
4055         }
4056         //var type = this.el.dom.type;
4057
4058
4059         if(this.tabIndex !== undefined){
4060             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4061         }
4062
4063         this.dialogEl = this.el.select('.modal-dialog',true).first();
4064         this.bodyEl = this.el.select('.modal-body',true).first();
4065         this.closeEl = this.el.select('.modal-header .close', true).first();
4066         this.headerEl = this.el.select('.modal-header',true).first();
4067         this.titleEl = this.el.select('.modal-title',true).first();
4068         this.footerEl = this.el.select('.modal-footer',true).first();
4069
4070         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4071         
4072         //this.el.addClass("x-dlg-modal");
4073
4074         if (this.buttons.length) {
4075             Roo.each(this.buttons, function(bb) {
4076                 var b = Roo.apply({}, bb);
4077                 b.xns = b.xns || Roo.bootstrap;
4078                 b.xtype = b.xtype || 'Button';
4079                 if (typeof(b.listeners) == 'undefined') {
4080                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4081                 }
4082
4083                 var btn = Roo.factory(b);
4084
4085                 btn.render(this.getButtonContainer());
4086
4087             },this);
4088         }
4089         // render the children.
4090         var nitems = [];
4091
4092         if(typeof(this.items) != 'undefined'){
4093             var items = this.items;
4094             delete this.items;
4095
4096             for(var i =0;i < items.length;i++) {
4097                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4098             }
4099         }
4100
4101         this.items = nitems;
4102
4103         // where are these used - they used to be body/close/footer
4104
4105
4106         this.initEvents();
4107         //this.el.addClass([this.fieldClass, this.cls]);
4108
4109     },
4110
4111     getAutoCreate : function()
4112     {
4113         // we will default to modal-body-overflow - might need to remove or make optional later.
4114         var bdy = {
4115                 cls : 'modal-body ' + (this.fitwindow ? 'overflow-auto' : ''), 
4116                 html : this.html || ''
4117         };
4118
4119         var title = {
4120             tag: 'h4',
4121             cls : 'modal-title',
4122             html : this.title
4123         };
4124
4125         if(this.specificTitle){ // WTF is this?
4126             title = this.title;
4127         }
4128
4129         var header = [];
4130         if (this.allow_close && Roo.bootstrap.version == 3) {
4131             header.push({
4132                 tag: 'button',
4133                 cls : 'close',
4134                 html : '&times'
4135             });
4136         }
4137
4138         header.push(title);
4139
4140         if (this.editableTitle) {
4141             header.push({
4142                 cls: 'form-control roo-editable-title d-none',
4143                 tag: 'input',
4144                 type: 'text'
4145             });
4146         }
4147         
4148         if (this.allow_close && Roo.bootstrap.version == 4) {
4149             header.push({
4150                 tag: 'button',
4151                 cls : 'close',
4152                 html : '&times'
4153             });
4154         }
4155         
4156         var size = '';
4157
4158         if(this.size.length){
4159             size = 'modal-' + this.size;
4160         }
4161         
4162         var footer = Roo.bootstrap.version == 3 ?
4163             {
4164                 cls : 'modal-footer',
4165                 cn : [
4166                     {
4167                         tag: 'div',
4168                         cls: 'btn-' + this.buttonPosition
4169                     }
4170                 ]
4171
4172             } :
4173             {  // BS4 uses mr-auto on left buttons....
4174                 cls : 'modal-footer'
4175             };
4176
4177             
4178
4179         
4180         
4181         var modal = {
4182             cls: "modal",
4183              cn : [
4184                 {
4185                     cls: "modal-dialog " + size,
4186                     cn : [
4187                         {
4188                             cls : "modal-content",
4189                             cn : [
4190                                 {
4191                                     cls : 'modal-header',
4192                                     cn : header
4193                                 },
4194                                 bdy,
4195                                 footer
4196                             ]
4197
4198                         }
4199                     ]
4200
4201                 }
4202             ]
4203         };
4204
4205         if(this.animate){
4206             modal.cls += ' fade';
4207         }
4208
4209         return modal;
4210
4211     },
4212     getChildContainer : function() {
4213
4214          return this.bodyEl;
4215
4216     },
4217     getButtonContainer : function() {
4218         
4219          return Roo.bootstrap.version == 4 ?
4220             this.el.select('.modal-footer',true).first()
4221             : this.el.select('.modal-footer div',true).first();
4222
4223     },
4224     initEvents : function()
4225     {
4226         if (this.allow_close) {
4227             this.closeEl.on('click', this.hide, this);
4228         }
4229         Roo.EventManager.onWindowResize(this.resize, this, true);
4230         if (this.editableTitle) {
4231             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4232             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4233             this.headerEditEl.on('keyup', function(e) {
4234                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4235                         this.toggleHeaderInput(false)
4236                     }
4237                 }, this);
4238             this.headerEditEl.on('blur', function(e) {
4239                 this.toggleHeaderInput(false)
4240             },this);
4241         }
4242
4243     },
4244   
4245
4246     resize : function()
4247     {
4248         this.maskEl.setSize(
4249             Roo.lib.Dom.getViewWidth(true),
4250             Roo.lib.Dom.getViewHeight(true)
4251         );
4252         
4253         if (this.fitwindow) {
4254             
4255            
4256             this.setSize(
4257                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4258                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4259             );
4260             return;
4261         }
4262         
4263         if(this.max_width !== 0) {
4264             
4265             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4266             
4267             if(this.height) {
4268                 this.setSize(w, this.height);
4269                 return;
4270             }
4271             
4272             if(this.max_height) {
4273                 this.setSize(w,Math.min(
4274                     this.max_height,
4275                     Roo.lib.Dom.getViewportHeight(true) - 60
4276                 ));
4277                 
4278                 return;
4279             }
4280             
4281             if(!this.fit_content) {
4282                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4283                 return;
4284             }
4285             
4286             this.setSize(w, Math.min(
4287                 60 +
4288                 this.headerEl.getHeight() + 
4289                 this.footerEl.getHeight() + 
4290                 this.getChildHeight(this.bodyEl.dom.childNodes),
4291                 Roo.lib.Dom.getViewportHeight(true) - 60)
4292             );
4293         }
4294         
4295     },
4296
4297     setSize : function(w,h)
4298     {
4299         if (!w && !h) {
4300             return;
4301         }
4302         
4303         this.resizeTo(w,h);
4304     },
4305
4306     show : function() {
4307
4308         if (!this.rendered) {
4309             this.render();
4310         }
4311         this.toggleHeaderInput(false);
4312         //this.el.setStyle('display', 'block');
4313         this.el.removeClass('hideing');
4314         this.el.dom.style.display='block';
4315         
4316         Roo.get(document.body).addClass('modal-open');
4317  
4318         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4319             
4320             (function(){
4321                 this.el.addClass('show');
4322                 this.el.addClass('in');
4323             }).defer(50, this);
4324         }else{
4325             this.el.addClass('show');
4326             this.el.addClass('in');
4327         }
4328
4329         // not sure how we can show data in here..
4330         //if (this.tmpl) {
4331         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4332         //}
4333
4334         Roo.get(document.body).addClass("x-body-masked");
4335         
4336         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4337         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4338         this.maskEl.dom.style.display = 'block';
4339         this.maskEl.addClass('show');
4340         
4341         
4342         this.resize();
4343         
4344         this.fireEvent('show', this);
4345
4346         // set zindex here - otherwise it appears to be ignored...
4347         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4348
4349         (function () {
4350             this.items.forEach( function(e) {
4351                 e.layout ? e.layout() : false;
4352
4353             });
4354         }).defer(100,this);
4355
4356     },
4357     hide : function()
4358     {
4359         if(this.fireEvent("beforehide", this) !== false){
4360             
4361             this.maskEl.removeClass('show');
4362             
4363             this.maskEl.dom.style.display = '';
4364             Roo.get(document.body).removeClass("x-body-masked");
4365             this.el.removeClass('in');
4366             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4367
4368             if(this.animate){ // why
4369                 this.el.addClass('hideing');
4370                 this.el.removeClass('show');
4371                 (function(){
4372                     if (!this.el.hasClass('hideing')) {
4373                         return; // it's been shown again...
4374                     }
4375                     
4376                     this.el.dom.style.display='';
4377
4378                     Roo.get(document.body).removeClass('modal-open');
4379                     this.el.removeClass('hideing');
4380                 }).defer(150,this);
4381                 
4382             }else{
4383                 this.el.removeClass('show');
4384                 this.el.dom.style.display='';
4385                 Roo.get(document.body).removeClass('modal-open');
4386
4387             }
4388             this.fireEvent('hide', this);
4389         }
4390     },
4391     isVisible : function()
4392     {
4393         
4394         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4395         
4396     },
4397
4398     addButton : function(str, cb)
4399     {
4400
4401
4402         var b = Roo.apply({}, { html : str } );
4403         b.xns = b.xns || Roo.bootstrap;
4404         b.xtype = b.xtype || 'Button';
4405         if (typeof(b.listeners) == 'undefined') {
4406             b.listeners = { click : cb.createDelegate(this)  };
4407         }
4408
4409         var btn = Roo.factory(b);
4410
4411         btn.render(this.getButtonContainer());
4412
4413         return btn;
4414
4415     },
4416
4417     setDefaultButton : function(btn)
4418     {
4419         //this.el.select('.modal-footer').()
4420     },
4421
4422     resizeTo: function(w,h)
4423     {
4424         this.dialogEl.setWidth(w);
4425         
4426         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4427
4428         this.bodyEl.setHeight(h - diff);
4429         
4430         this.fireEvent('resize', this);
4431     },
4432     
4433     setContentSize  : function(w, h)
4434     {
4435
4436     },
4437     onButtonClick: function(btn,e)
4438     {
4439         //Roo.log([a,b,c]);
4440         this.fireEvent('btnclick', btn.name, e);
4441     },
4442      /**
4443      * Set the title of the Dialog
4444      * @param {String} str new Title
4445      */
4446     setTitle: function(str) {
4447         this.titleEl.dom.innerHTML = str;
4448         this.title = str;
4449     },
4450     /**
4451      * Set the body of the Dialog
4452      * @param {String} str new Title
4453      */
4454     setBody: function(str) {
4455         this.bodyEl.dom.innerHTML = str;
4456     },
4457     /**
4458      * Set the body of the Dialog using the template
4459      * @param {Obj} data - apply this data to the template and replace the body contents.
4460      */
4461     applyBody: function(obj)
4462     {
4463         if (!this.tmpl) {
4464             Roo.log("Error - using apply Body without a template");
4465             //code
4466         }
4467         this.tmpl.overwrite(this.bodyEl, obj);
4468     },
4469     
4470     getChildHeight : function(child_nodes)
4471     {
4472         if(
4473             !child_nodes ||
4474             child_nodes.length == 0
4475         ) {
4476             return 0;
4477         }
4478         
4479         var child_height = 0;
4480         
4481         for(var i = 0; i < child_nodes.length; i++) {
4482             
4483             /*
4484             * for modal with tabs...
4485             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4486                 
4487                 var layout_childs = child_nodes[i].childNodes;
4488                 
4489                 for(var j = 0; j < layout_childs.length; j++) {
4490                     
4491                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4492                         
4493                         var layout_body_childs = layout_childs[j].childNodes;
4494                         
4495                         for(var k = 0; k < layout_body_childs.length; k++) {
4496                             
4497                             if(layout_body_childs[k].classList.contains('navbar')) {
4498                                 child_height += layout_body_childs[k].offsetHeight;
4499                                 continue;
4500                             }
4501                             
4502                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4503                                 
4504                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4505                                 
4506                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4507                                     
4508                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4509                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4510                                         continue;
4511                                     }
4512                                     
4513                                 }
4514                                 
4515                             }
4516                             
4517                         }
4518                     }
4519                 }
4520                 continue;
4521             }
4522             */
4523             
4524             child_height += child_nodes[i].offsetHeight;
4525             // Roo.log(child_nodes[i].offsetHeight);
4526         }
4527         
4528         return child_height;
4529     },
4530     toggleHeaderInput : function(is_edit)
4531     {
4532         if (!this.editableTitle) {
4533             return; // not editable.
4534         }
4535         if (is_edit && this.is_header_editing) {
4536             return; // already editing..
4537         }
4538         if (is_edit) {
4539     
4540             this.headerEditEl.dom.value = this.title;
4541             this.headerEditEl.removeClass('d-none');
4542             this.headerEditEl.dom.focus();
4543             this.titleEl.addClass('d-none');
4544             
4545             this.is_header_editing = true;
4546             return
4547         }
4548         // flip back to not editing.
4549         this.title = this.headerEditEl.dom.value;
4550         this.headerEditEl.addClass('d-none');
4551         this.titleEl.removeClass('d-none');
4552         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4553         this.is_header_editing = false;
4554         this.fireEvent('titlechanged', this, this.title);
4555     
4556             
4557         
4558     }
4559
4560 });
4561
4562
4563 Roo.apply(Roo.bootstrap.Modal,  {
4564     /**
4565          * Button config that displays a single OK button
4566          * @type Object
4567          */
4568         OK :  [{
4569             name : 'ok',
4570             weight : 'primary',
4571             html : 'OK'
4572         }],
4573         /**
4574          * Button config that displays Yes and No buttons
4575          * @type Object
4576          */
4577         YESNO : [
4578             {
4579                 name  : 'no',
4580                 html : 'No'
4581             },
4582             {
4583                 name  :'yes',
4584                 weight : 'primary',
4585                 html : 'Yes'
4586             }
4587         ],
4588
4589         /**
4590          * Button config that displays OK and Cancel buttons
4591          * @type Object
4592          */
4593         OKCANCEL : [
4594             {
4595                name : 'cancel',
4596                 html : 'Cancel'
4597             },
4598             {
4599                 name : 'ok',
4600                 weight : 'primary',
4601                 html : 'OK'
4602             }
4603         ],
4604         /**
4605          * Button config that displays Yes, No and Cancel buttons
4606          * @type Object
4607          */
4608         YESNOCANCEL : [
4609             {
4610                 name : 'yes',
4611                 weight : 'primary',
4612                 html : 'Yes'
4613             },
4614             {
4615                 name : 'no',
4616                 html : 'No'
4617             },
4618             {
4619                 name : 'cancel',
4620                 html : 'Cancel'
4621             }
4622         ],
4623         
4624         zIndex : 10001
4625 });
4626
4627 /*
4628  * - LGPL
4629  *
4630  * messagebox - can be used as a replace
4631  * 
4632  */
4633 /**
4634  * @class Roo.MessageBox
4635  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4636  * Example usage:
4637  *<pre><code>
4638 // Basic alert:
4639 Roo.Msg.alert('Status', 'Changes saved successfully.');
4640
4641 // Prompt for user data:
4642 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4643     if (btn == 'ok'){
4644         // process text value...
4645     }
4646 });
4647
4648 // Show a dialog using config options:
4649 Roo.Msg.show({
4650    title:'Save Changes?',
4651    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4652    buttons: Roo.Msg.YESNOCANCEL,
4653    fn: processResult,
4654    animEl: 'elId'
4655 });
4656 </code></pre>
4657  * @singleton
4658  */
4659 Roo.bootstrap.MessageBox = function(){
4660     var dlg, opt, mask, waitTimer;
4661     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4662     var buttons, activeTextEl, bwidth;
4663
4664     
4665     // private
4666     var handleButton = function(button){
4667         dlg.hide();
4668         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4669     };
4670
4671     // private
4672     var handleHide = function(){
4673         if(opt && opt.cls){
4674             dlg.el.removeClass(opt.cls);
4675         }
4676         //if(waitTimer){
4677         //    Roo.TaskMgr.stop(waitTimer);
4678         //    waitTimer = null;
4679         //}
4680     };
4681
4682     // private
4683     var updateButtons = function(b){
4684         var width = 0;
4685         if(!b){
4686             buttons["ok"].hide();
4687             buttons["cancel"].hide();
4688             buttons["yes"].hide();
4689             buttons["no"].hide();
4690             dlg.footerEl.hide();
4691             
4692             return width;
4693         }
4694         dlg.footerEl.show();
4695         for(var k in buttons){
4696             if(typeof buttons[k] != "function"){
4697                 if(b[k]){
4698                     buttons[k].show();
4699                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4700                     width += buttons[k].el.getWidth()+15;
4701                 }else{
4702                     buttons[k].hide();
4703                 }
4704             }
4705         }
4706         return width;
4707     };
4708
4709     // private
4710     var handleEsc = function(d, k, e){
4711         if(opt && opt.closable !== false){
4712             dlg.hide();
4713         }
4714         if(e){
4715             e.stopEvent();
4716         }
4717     };
4718
4719     return {
4720         /**
4721          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4722          * @return {Roo.BasicDialog} The BasicDialog element
4723          */
4724         getDialog : function(){
4725            if(!dlg){
4726                 dlg = new Roo.bootstrap.Modal( {
4727                     //draggable: true,
4728                     //resizable:false,
4729                     //constraintoviewport:false,
4730                     //fixedcenter:true,
4731                     //collapsible : false,
4732                     //shim:true,
4733                     //modal: true,
4734                 //    width: 'auto',
4735                   //  height:100,
4736                     //buttonAlign:"center",
4737                     closeClick : function(){
4738                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4739                             handleButton("no");
4740                         }else{
4741                             handleButton("cancel");
4742                         }
4743                     }
4744                 });
4745                 dlg.render();
4746                 dlg.on("hide", handleHide);
4747                 mask = dlg.mask;
4748                 //dlg.addKeyListener(27, handleEsc);
4749                 buttons = {};
4750                 this.buttons = buttons;
4751                 var bt = this.buttonText;
4752                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4753                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4754                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4755                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4756                 //Roo.log(buttons);
4757                 bodyEl = dlg.bodyEl.createChild({
4758
4759                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4760                         '<textarea class="roo-mb-textarea"></textarea>' +
4761                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4762                 });
4763                 msgEl = bodyEl.dom.firstChild;
4764                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4765                 textboxEl.enableDisplayMode();
4766                 textboxEl.addKeyListener([10,13], function(){
4767                     if(dlg.isVisible() && opt && opt.buttons){
4768                         if(opt.buttons.ok){
4769                             handleButton("ok");
4770                         }else if(opt.buttons.yes){
4771                             handleButton("yes");
4772                         }
4773                     }
4774                 });
4775                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4776                 textareaEl.enableDisplayMode();
4777                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4778                 progressEl.enableDisplayMode();
4779                 
4780                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4781                 var pf = progressEl.dom.firstChild;
4782                 if (pf) {
4783                     pp = Roo.get(pf.firstChild);
4784                     pp.setHeight(pf.offsetHeight);
4785                 }
4786                 
4787             }
4788             return dlg;
4789         },
4790
4791         /**
4792          * Updates the message box body text
4793          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4794          * the XHTML-compliant non-breaking space character '&amp;#160;')
4795          * @return {Roo.MessageBox} This message box
4796          */
4797         updateText : function(text)
4798         {
4799             if(!dlg.isVisible() && !opt.width){
4800                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4801                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4802             }
4803             msgEl.innerHTML = text || '&#160;';
4804       
4805             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4806             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4807             var w = Math.max(
4808                     Math.min(opt.width || cw , this.maxWidth), 
4809                     Math.max(opt.minWidth || this.minWidth, bwidth)
4810             );
4811             if(opt.prompt){
4812                 activeTextEl.setWidth(w);
4813             }
4814             if(dlg.isVisible()){
4815                 dlg.fixedcenter = false;
4816             }
4817             // to big, make it scroll. = But as usual stupid IE does not support
4818             // !important..
4819             
4820             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4821                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4822                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4823             } else {
4824                 bodyEl.dom.style.height = '';
4825                 bodyEl.dom.style.overflowY = '';
4826             }
4827             if (cw > w) {
4828                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4829             } else {
4830                 bodyEl.dom.style.overflowX = '';
4831             }
4832             
4833             dlg.setContentSize(w, bodyEl.getHeight());
4834             if(dlg.isVisible()){
4835                 dlg.fixedcenter = true;
4836             }
4837             return this;
4838         },
4839
4840         /**
4841          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4842          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4843          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4844          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4845          * @return {Roo.MessageBox} This message box
4846          */
4847         updateProgress : function(value, text){
4848             if(text){
4849                 this.updateText(text);
4850             }
4851             
4852             if (pp) { // weird bug on my firefox - for some reason this is not defined
4853                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4854                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4855             }
4856             return this;
4857         },        
4858
4859         /**
4860          * Returns true if the message box is currently displayed
4861          * @return {Boolean} True if the message box is visible, else false
4862          */
4863         isVisible : function(){
4864             return dlg && dlg.isVisible();  
4865         },
4866
4867         /**
4868          * Hides the message box if it is displayed
4869          */
4870         hide : function(){
4871             if(this.isVisible()){
4872                 dlg.hide();
4873             }  
4874         },
4875
4876         /**
4877          * Displays a new message box, or reinitializes an existing message box, based on the config options
4878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4879          * The following config object properties are supported:
4880          * <pre>
4881 Property    Type             Description
4882 ----------  ---------------  ------------------------------------------------------------------------------------
4883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4884                                    closes (defaults to undefined)
4885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4888                                    progress and wait dialogs will ignore this property and always hide the
4889                                    close button as they can only be closed programmatically.
4890 cls               String           A custom CSS class to apply to the message box element
4891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4892                                    displayed (defaults to 75)
4893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4894                                    function will be btn (the name of the button that was clicked, if applicable,
4895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4896                                    Progress and wait dialogs will ignore this option since they do not respond to
4897                                    user actions and can only be closed programmatically, so any required function
4898                                    should be called by the same code after it closes the dialog.
4899 icon              String           A CSS class that provides a background image to be used as an icon for
4900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4903 modal             Boolean          False to allow user interaction with the page while the message box is
4904                                    displayed (defaults to true)
4905 msg               String           A string that will replace the existing message box body text (defaults
4906                                    to the XHTML-compliant non-breaking space character '&#160;')
4907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4908 progress          Boolean          True to display a progress bar (defaults to false)
4909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4912 title             String           The title text
4913 value             String           The string value to set into the active textbox element if displayed
4914 wait              Boolean          True to display a progress bar (defaults to false)
4915 width             Number           The width of the dialog in pixels
4916 </pre>
4917          *
4918          * Example usage:
4919          * <pre><code>
4920 Roo.Msg.show({
4921    title: 'Address',
4922    msg: 'Please enter your address:',
4923    width: 300,
4924    buttons: Roo.MessageBox.OKCANCEL,
4925    multiline: true,
4926    fn: saveAddress,
4927    animEl: 'addAddressBtn'
4928 });
4929 </code></pre>
4930          * @param {Object} config Configuration options
4931          * @return {Roo.MessageBox} This message box
4932          */
4933         show : function(options)
4934         {
4935             
4936             // this causes nightmares if you show one dialog after another
4937             // especially on callbacks..
4938              
4939             if(this.isVisible()){
4940                 
4941                 this.hide();
4942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4944                 Roo.log("New Dialog Message:" +  options.msg )
4945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4947                 
4948             }
4949             var d = this.getDialog();
4950             opt = options;
4951             d.setTitle(opt.title || "&#160;");
4952             d.closeEl.setDisplayed(opt.closable !== false);
4953             activeTextEl = textboxEl;
4954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4955             if(opt.prompt){
4956                 if(opt.multiline){
4957                     textboxEl.hide();
4958                     textareaEl.show();
4959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4960                         opt.multiline : this.defaultTextHeight);
4961                     activeTextEl = textareaEl;
4962                 }else{
4963                     textboxEl.show();
4964                     textareaEl.hide();
4965                 }
4966             }else{
4967                 textboxEl.hide();
4968                 textareaEl.hide();
4969             }
4970             progressEl.setDisplayed(opt.progress === true);
4971             if (opt.progress) {
4972                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4973             }
4974             this.updateProgress(0);
4975             activeTextEl.dom.value = opt.value || "";
4976             if(opt.prompt){
4977                 dlg.setDefaultButton(activeTextEl);
4978             }else{
4979                 var bs = opt.buttons;
4980                 var db = null;
4981                 if(bs && bs.ok){
4982                     db = buttons["ok"];
4983                 }else if(bs && bs.yes){
4984                     db = buttons["yes"];
4985                 }
4986                 dlg.setDefaultButton(db);
4987             }
4988             bwidth = updateButtons(opt.buttons);
4989             this.updateText(opt.msg);
4990             if(opt.cls){
4991                 d.el.addClass(opt.cls);
4992             }
4993             d.proxyDrag = opt.proxyDrag === true;
4994             d.modal = opt.modal !== false;
4995             d.mask = opt.modal !== false ? mask : false;
4996             if(!d.isVisible()){
4997                 // force it to the end of the z-index stack so it gets a cursor in FF
4998                 document.body.appendChild(dlg.el.dom);
4999                 d.animateTarget = null;
5000                 d.show(options.animEl);
5001             }
5002             return this;
5003         },
5004
5005         /**
5006          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5007          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5008          * and closing the message box when the process is complete.
5009          * @param {String} title The title bar text
5010          * @param {String} msg The message box body text
5011          * @return {Roo.MessageBox} This message box
5012          */
5013         progress : function(title, msg){
5014             this.show({
5015                 title : title,
5016                 msg : msg,
5017                 buttons: false,
5018                 progress:true,
5019                 closable:false,
5020                 minWidth: this.minProgressWidth,
5021                 modal : true
5022             });
5023             return this;
5024         },
5025
5026         /**
5027          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5028          * If a callback function is passed it will be called after the user clicks the button, and the
5029          * id of the button that was clicked will be passed as the only parameter to the callback
5030          * (could also be the top-right close button).
5031          * @param {String} title The title bar text
5032          * @param {String} msg The message box body text
5033          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5034          * @param {Object} scope (optional) The scope of the callback function
5035          * @return {Roo.MessageBox} This message box
5036          */
5037         alert : function(title, msg, fn, scope)
5038         {
5039             this.show({
5040                 title : title,
5041                 msg : msg,
5042                 buttons: this.OK,
5043                 fn: fn,
5044                 closable : false,
5045                 scope : scope,
5046                 modal : true
5047             });
5048             return this;
5049         },
5050
5051         /**
5052          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5053          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5054          * You are responsible for closing the message box when the process is complete.
5055          * @param {String} msg The message box body text
5056          * @param {String} title (optional) The title bar text
5057          * @return {Roo.MessageBox} This message box
5058          */
5059         wait : function(msg, title){
5060             this.show({
5061                 title : title,
5062                 msg : msg,
5063                 buttons: false,
5064                 closable:false,
5065                 progress:true,
5066                 modal:true,
5067                 width:300,
5068                 wait:true
5069             });
5070             waitTimer = Roo.TaskMgr.start({
5071                 run: function(i){
5072                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5073                 },
5074                 interval: 1000
5075             });
5076             return this;
5077         },
5078
5079         /**
5080          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5081          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5082          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5083          * @param {String} title The title bar text
5084          * @param {String} msg The message box body text
5085          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5086          * @param {Object} scope (optional) The scope of the callback function
5087          * @return {Roo.MessageBox} This message box
5088          */
5089         confirm : function(title, msg, fn, scope){
5090             this.show({
5091                 title : title,
5092                 msg : msg,
5093                 buttons: this.YESNO,
5094                 fn: fn,
5095                 scope : scope,
5096                 modal : true
5097             });
5098             return this;
5099         },
5100
5101         /**
5102          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5103          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5104          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5105          * (could also be the top-right close button) and the text that was entered will be passed as the two
5106          * parameters to the callback.
5107          * @param {String} title The title bar text
5108          * @param {String} msg The message box body text
5109          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110          * @param {Object} scope (optional) The scope of the callback function
5111          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5112          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5113          * @return {Roo.MessageBox} This message box
5114          */
5115         prompt : function(title, msg, fn, scope, multiline){
5116             this.show({
5117                 title : title,
5118                 msg : msg,
5119                 buttons: this.OKCANCEL,
5120                 fn: fn,
5121                 minWidth:250,
5122                 scope : scope,
5123                 prompt:true,
5124                 multiline: multiline,
5125                 modal : true
5126             });
5127             return this;
5128         },
5129
5130         /**
5131          * Button config that displays a single OK button
5132          * @type Object
5133          */
5134         OK : {ok:true},
5135         /**
5136          * Button config that displays Yes and No buttons
5137          * @type Object
5138          */
5139         YESNO : {yes:true, no:true},
5140         /**
5141          * Button config that displays OK and Cancel buttons
5142          * @type Object
5143          */
5144         OKCANCEL : {ok:true, cancel:true},
5145         /**
5146          * Button config that displays Yes, No and Cancel buttons
5147          * @type Object
5148          */
5149         YESNOCANCEL : {yes:true, no:true, cancel:true},
5150
5151         /**
5152          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5153          * @type Number
5154          */
5155         defaultTextHeight : 75,
5156         /**
5157          * The maximum width in pixels of the message box (defaults to 600)
5158          * @type Number
5159          */
5160         maxWidth : 600,
5161         /**
5162          * The minimum width in pixels of the message box (defaults to 100)
5163          * @type Number
5164          */
5165         minWidth : 100,
5166         /**
5167          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5168          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5169          * @type Number
5170          */
5171         minProgressWidth : 250,
5172         /**
5173          * An object containing the default button text strings that can be overriden for localized language support.
5174          * Supported properties are: ok, cancel, yes and no.
5175          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5176          * @type Object
5177          */
5178         buttonText : {
5179             ok : "OK",
5180             cancel : "Cancel",
5181             yes : "Yes",
5182             no : "No"
5183         }
5184     };
5185 }();
5186
5187 /**
5188  * Shorthand for {@link Roo.MessageBox}
5189  */
5190 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5191 Roo.Msg = Roo.Msg || Roo.MessageBox;
5192 /*
5193  * - LGPL
5194  *
5195  * navbar
5196  * 
5197  */
5198
5199 /**
5200  * @class Roo.bootstrap.Navbar
5201  * @extends Roo.bootstrap.Component
5202  * Bootstrap Navbar class
5203
5204  * @constructor
5205  * Create a new Navbar
5206  * @param {Object} config The config object
5207  */
5208
5209
5210 Roo.bootstrap.Navbar = function(config){
5211     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5212     this.addEvents({
5213         // raw events
5214         /**
5215          * @event beforetoggle
5216          * Fire before toggle the menu
5217          * @param {Roo.EventObject} e
5218          */
5219         "beforetoggle" : true
5220     });
5221 };
5222
5223 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5224     
5225     
5226    
5227     // private
5228     navItems : false,
5229     loadMask : false,
5230     
5231     
5232     getAutoCreate : function(){
5233         
5234         
5235         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5236         
5237     },
5238     
5239     initEvents :function ()
5240     {
5241         //Roo.log(this.el.select('.navbar-toggle',true));
5242         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5243         
5244         var mark = {
5245             tag: "div",
5246             cls:"x-dlg-mask"
5247         };
5248         
5249         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5250         
5251         var size = this.el.getSize();
5252         this.maskEl.setSize(size.width, size.height);
5253         this.maskEl.enableDisplayMode("block");
5254         this.maskEl.hide();
5255         
5256         if(this.loadMask){
5257             this.maskEl.show();
5258         }
5259     },
5260     
5261     
5262     getChildContainer : function()
5263     {
5264         if (this.el && this.el.select('.collapse').getCount()) {
5265             return this.el.select('.collapse',true).first();
5266         }
5267         
5268         return this.el;
5269     },
5270     
5271     mask : function()
5272     {
5273         this.maskEl.show();
5274     },
5275     
5276     unmask : function()
5277     {
5278         this.maskEl.hide();
5279     },
5280     onToggle : function()
5281     {
5282         
5283         if(this.fireEvent('beforetoggle', this) === false){
5284             return;
5285         }
5286         var ce = this.el.select('.navbar-collapse',true).first();
5287       
5288         if (!ce.hasClass('show')) {
5289            this.expand();
5290         } else {
5291             this.collapse();
5292         }
5293         
5294         
5295     
5296     },
5297     /**
5298      * Expand the navbar pulldown 
5299      */
5300     expand : function ()
5301     {
5302        
5303         var ce = this.el.select('.navbar-collapse',true).first();
5304         if (ce.hasClass('collapsing')) {
5305             return;
5306         }
5307         ce.dom.style.height = '';
5308                // show it...
5309         ce.addClass('in'); // old...
5310         ce.removeClass('collapse');
5311         ce.addClass('show');
5312         var h = ce.getHeight();
5313         Roo.log(h);
5314         ce.removeClass('show');
5315         // at this point we should be able to see it..
5316         ce.addClass('collapsing');
5317         
5318         ce.setHeight(0); // resize it ...
5319         ce.on('transitionend', function() {
5320             //Roo.log('done transition');
5321             ce.removeClass('collapsing');
5322             ce.addClass('show');
5323             ce.removeClass('collapse');
5324
5325             ce.dom.style.height = '';
5326         }, this, { single: true} );
5327         ce.setHeight(h);
5328         ce.dom.scrollTop = 0;
5329     },
5330     /**
5331      * Collapse the navbar pulldown 
5332      */
5333     collapse : function()
5334     {
5335          var ce = this.el.select('.navbar-collapse',true).first();
5336        
5337         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5338             // it's collapsed or collapsing..
5339             return;
5340         }
5341         ce.removeClass('in'); // old...
5342         ce.setHeight(ce.getHeight());
5343         ce.removeClass('show');
5344         ce.addClass('collapsing');
5345         
5346         ce.on('transitionend', function() {
5347             ce.dom.style.height = '';
5348             ce.removeClass('collapsing');
5349             ce.addClass('collapse');
5350         }, this, { single: true} );
5351         ce.setHeight(0);
5352     }
5353     
5354     
5355     
5356 });
5357
5358
5359
5360  
5361
5362  /*
5363  * - LGPL
5364  *
5365  * navbar
5366  * 
5367  */
5368
5369 /**
5370  * @class Roo.bootstrap.NavSimplebar
5371  * @extends Roo.bootstrap.Navbar
5372  * Bootstrap Sidebar class
5373  *
5374  * @cfg {Boolean} inverse is inverted color
5375  * 
5376  * @cfg {String} type (nav | pills | tabs)
5377  * @cfg {Boolean} arrangement stacked | justified
5378  * @cfg {String} align (left | right) alignment
5379  * 
5380  * @cfg {Boolean} main (true|false) main nav bar? default false
5381  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5382  * 
5383  * @cfg {String} tag (header|footer|nav|div) default is nav 
5384
5385  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5386  * 
5387  * 
5388  * @constructor
5389  * Create a new Sidebar
5390  * @param {Object} config The config object
5391  */
5392
5393
5394 Roo.bootstrap.NavSimplebar = function(config){
5395     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5396 };
5397
5398 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5399     
5400     inverse: false,
5401     
5402     type: false,
5403     arrangement: '',
5404     align : false,
5405     
5406     weight : 'light',
5407     
5408     main : false,
5409     
5410     
5411     tag : false,
5412     
5413     
5414     getAutoCreate : function(){
5415         
5416         
5417         var cfg = {
5418             tag : this.tag || 'div',
5419             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5420         };
5421         if (['light','white'].indexOf(this.weight) > -1) {
5422             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5423         }
5424         cfg.cls += ' bg-' + this.weight;
5425         
5426         if (this.inverse) {
5427             cfg.cls += ' navbar-inverse';
5428             
5429         }
5430         
5431         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5432         
5433         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5434             return cfg;
5435         }
5436         
5437         
5438     
5439         
5440         cfg.cn = [
5441             {
5442                 cls: 'nav nav-' + this.xtype,
5443                 tag : 'ul'
5444             }
5445         ];
5446         
5447          
5448         this.type = this.type || 'nav';
5449         if (['tabs','pills'].indexOf(this.type) != -1) {
5450             cfg.cn[0].cls += ' nav-' + this.type
5451         
5452         
5453         } else {
5454             if (this.type!=='nav') {
5455                 Roo.log('nav type must be nav/tabs/pills')
5456             }
5457             cfg.cn[0].cls += ' navbar-nav'
5458         }
5459         
5460         
5461         
5462         
5463         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5464             cfg.cn[0].cls += ' nav-' + this.arrangement;
5465         }
5466         
5467         
5468         if (this.align === 'right') {
5469             cfg.cn[0].cls += ' navbar-right';
5470         }
5471         
5472         
5473         
5474         
5475         return cfg;
5476     
5477         
5478     }
5479     
5480     
5481     
5482 });
5483
5484
5485
5486  
5487
5488  
5489        /*
5490  * - LGPL
5491  *
5492  * navbar
5493  * navbar-fixed-top
5494  * navbar-expand-md  fixed-top 
5495  */
5496
5497 /**
5498  * @class Roo.bootstrap.NavHeaderbar
5499  * @extends Roo.bootstrap.NavSimplebar
5500  * Bootstrap Sidebar class
5501  *
5502  * @cfg {String} brand what is brand
5503  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5504  * @cfg {String} brand_href href of the brand
5505  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5506  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5507  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5508  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5509  * 
5510  * @constructor
5511  * Create a new Sidebar
5512  * @param {Object} config The config object
5513  */
5514
5515
5516 Roo.bootstrap.NavHeaderbar = function(config){
5517     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5518       
5519 };
5520
5521 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5522     
5523     position: '',
5524     brand: '',
5525     brand_href: false,
5526     srButton : true,
5527     autohide : false,
5528     desktopCenter : false,
5529    
5530     
5531     getAutoCreate : function(){
5532         
5533         var   cfg = {
5534             tag: this.nav || 'nav',
5535             cls: 'navbar navbar-expand-md',
5536             role: 'navigation',
5537             cn: []
5538         };
5539         
5540         var cn = cfg.cn;
5541         if (this.desktopCenter) {
5542             cn.push({cls : 'container', cn : []});
5543             cn = cn[0].cn;
5544         }
5545         
5546         if(this.srButton){
5547             var btn = {
5548                 tag: 'button',
5549                 type: 'button',
5550                 cls: 'navbar-toggle navbar-toggler',
5551                 'data-toggle': 'collapse',
5552                 cn: [
5553                     {
5554                         tag: 'span',
5555                         cls: 'sr-only',
5556                         html: 'Toggle navigation'
5557                     },
5558                     {
5559                         tag: 'span',
5560                         cls: 'icon-bar navbar-toggler-icon'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     }
5570                 ]
5571             };
5572             
5573             cn.push( Roo.bootstrap.version == 4 ? btn : {
5574                 tag: 'div',
5575                 cls: 'navbar-header',
5576                 cn: [
5577                     btn
5578                 ]
5579             });
5580         }
5581         
5582         cn.push({
5583             tag: 'div',
5584             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5585             cn : []
5586         });
5587         
5588         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5589         
5590         if (['light','white'].indexOf(this.weight) > -1) {
5591             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5592         }
5593         cfg.cls += ' bg-' + this.weight;
5594         
5595         
5596         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5597             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5598             
5599             // tag can override this..
5600             
5601             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5602         }
5603         
5604         if (this.brand !== '') {
5605             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5606             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5607                 tag: 'a',
5608                 href: this.brand_href ? this.brand_href : '#',
5609                 cls: 'navbar-brand',
5610                 cn: [
5611                 this.brand
5612                 ]
5613             });
5614         }
5615         
5616         if(this.main){
5617             cfg.cls += ' main-nav';
5618         }
5619         
5620         
5621         return cfg;
5622
5623         
5624     },
5625     getHeaderChildContainer : function()
5626     {
5627         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5628             return this.el.select('.navbar-header',true).first();
5629         }
5630         
5631         return this.getChildContainer();
5632     },
5633     
5634     getChildContainer : function()
5635     {
5636          
5637         return this.el.select('.roo-navbar-collapse',true).first();
5638          
5639         
5640     },
5641     
5642     initEvents : function()
5643     {
5644         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5645         
5646         if (this.autohide) {
5647             
5648             var prevScroll = 0;
5649             var ft = this.el;
5650             
5651             Roo.get(document).on('scroll',function(e) {
5652                 var ns = Roo.get(document).getScroll().top;
5653                 var os = prevScroll;
5654                 prevScroll = ns;
5655                 
5656                 if(ns > os){
5657                     ft.removeClass('slideDown');
5658                     ft.addClass('slideUp');
5659                     return;
5660                 }
5661                 ft.removeClass('slideUp');
5662                 ft.addClass('slideDown');
5663                  
5664               
5665           },this);
5666         }
5667     }    
5668     
5669 });
5670
5671
5672
5673  
5674
5675  /*
5676  * - LGPL
5677  *
5678  * navbar
5679  * 
5680  */
5681
5682 /**
5683  * @class Roo.bootstrap.NavSidebar
5684  * @extends Roo.bootstrap.Navbar
5685  * Bootstrap Sidebar class
5686  * 
5687  * @constructor
5688  * Create a new Sidebar
5689  * @param {Object} config The config object
5690  */
5691
5692
5693 Roo.bootstrap.NavSidebar = function(config){
5694     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5695 };
5696
5697 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5698     
5699     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5700     
5701     getAutoCreate : function(){
5702         
5703         
5704         return  {
5705             tag: 'div',
5706             cls: 'sidebar sidebar-nav'
5707         };
5708     
5709         
5710     }
5711     
5712     
5713     
5714 });
5715
5716
5717
5718  
5719
5720  /*
5721  * - LGPL
5722  *
5723  * nav group
5724  * 
5725  */
5726
5727 /**
5728  * @class Roo.bootstrap.NavGroup
5729  * @extends Roo.bootstrap.Component
5730  * Bootstrap NavGroup class
5731  * @cfg {String} align (left|right)
5732  * @cfg {Boolean} inverse
5733  * @cfg {String} type (nav|pills|tab) default nav
5734  * @cfg {String} navId - reference Id for navbar.
5735
5736  * 
5737  * @constructor
5738  * Create a new nav group
5739  * @param {Object} config The config object
5740  */
5741
5742 Roo.bootstrap.NavGroup = function(config){
5743     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5744     this.navItems = [];
5745    
5746     Roo.bootstrap.NavGroup.register(this);
5747      this.addEvents({
5748         /**
5749              * @event changed
5750              * Fires when the active item changes
5751              * @param {Roo.bootstrap.NavGroup} this
5752              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5753              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5754          */
5755         'changed': true
5756      });
5757     
5758 };
5759
5760 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5761     
5762     align: '',
5763     inverse: false,
5764     form: false,
5765     type: 'nav',
5766     navId : '',
5767     // private
5768     
5769     navItems : false, 
5770     
5771     getAutoCreate : function()
5772     {
5773         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5774         
5775         cfg = {
5776             tag : 'ul',
5777             cls: 'nav' 
5778         };
5779         if (Roo.bootstrap.version == 4) {
5780             if (['tabs','pills'].indexOf(this.type) != -1) {
5781                 cfg.cls += ' nav-' + this.type; 
5782             } else {
5783                 // trying to remove so header bar can right align top?
5784                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5785                     // do not use on header bar... 
5786                     cfg.cls += ' navbar-nav';
5787                 }
5788             }
5789             
5790         } else {
5791             if (['tabs','pills'].indexOf(this.type) != -1) {
5792                 cfg.cls += ' nav-' + this.type
5793             } else {
5794                 if (this.type !== 'nav') {
5795                     Roo.log('nav type must be nav/tabs/pills')
5796                 }
5797                 cfg.cls += ' navbar-nav'
5798             }
5799         }
5800         
5801         if (this.parent() && this.parent().sidebar) {
5802             cfg = {
5803                 tag: 'ul',
5804                 cls: 'dashboard-menu sidebar-menu'
5805             };
5806             
5807             return cfg;
5808         }
5809         
5810         if (this.form === true) {
5811             cfg = {
5812                 tag: 'form',
5813                 cls: 'navbar-form form-inline'
5814             };
5815             //nav navbar-right ml-md-auto
5816             if (this.align === 'right') {
5817                 cfg.cls += ' navbar-right ml-md-auto';
5818             } else {
5819                 cfg.cls += ' navbar-left';
5820             }
5821         }
5822         
5823         if (this.align === 'right') {
5824             cfg.cls += ' navbar-right ml-md-auto';
5825         } else {
5826             cfg.cls += ' mr-auto';
5827         }
5828         
5829         if (this.inverse) {
5830             cfg.cls += ' navbar-inverse';
5831             
5832         }
5833         
5834         
5835         return cfg;
5836     },
5837     /**
5838     * sets the active Navigation item
5839     * @param {Roo.bootstrap.NavItem} the new current navitem
5840     */
5841     setActiveItem : function(item)
5842     {
5843         var prev = false;
5844         Roo.each(this.navItems, function(v){
5845             if (v == item) {
5846                 return ;
5847             }
5848             if (v.isActive()) {
5849                 v.setActive(false, true);
5850                 prev = v;
5851                 
5852             }
5853             
5854         });
5855
5856         item.setActive(true, true);
5857         this.fireEvent('changed', this, item, prev);
5858         
5859         
5860     },
5861     /**
5862     * gets the active Navigation item
5863     * @return {Roo.bootstrap.NavItem} the current navitem
5864     */
5865     getActive : function()
5866     {
5867         
5868         var prev = false;
5869         Roo.each(this.navItems, function(v){
5870             
5871             if (v.isActive()) {
5872                 prev = v;
5873                 
5874             }
5875             
5876         });
5877         return prev;
5878     },
5879     
5880     indexOfNav : function()
5881     {
5882         
5883         var prev = false;
5884         Roo.each(this.navItems, function(v,i){
5885             
5886             if (v.isActive()) {
5887                 prev = i;
5888                 
5889             }
5890             
5891         });
5892         return prev;
5893     },
5894     /**
5895     * adds a Navigation item
5896     * @param {Roo.bootstrap.NavItem} the navitem to add
5897     */
5898     addItem : function(cfg)
5899     {
5900         if (this.form && Roo.bootstrap.version == 4) {
5901             cfg.tag = 'div';
5902         }
5903         var cn = new Roo.bootstrap.NavItem(cfg);
5904         this.register(cn);
5905         cn.parentId = this.id;
5906         cn.onRender(this.el, null);
5907         return cn;
5908     },
5909     /**
5910     * register a Navigation item
5911     * @param {Roo.bootstrap.NavItem} the navitem to add
5912     */
5913     register : function(item)
5914     {
5915         this.navItems.push( item);
5916         item.navId = this.navId;
5917     
5918     },
5919     
5920     /**
5921     * clear all the Navigation item
5922     */
5923    
5924     clearAll : function()
5925     {
5926         this.navItems = [];
5927         this.el.dom.innerHTML = '';
5928     },
5929     
5930     getNavItem: function(tabId)
5931     {
5932         var ret = false;
5933         Roo.each(this.navItems, function(e) {
5934             if (e.tabId == tabId) {
5935                ret =  e;
5936                return false;
5937             }
5938             return true;
5939             
5940         });
5941         return ret;
5942     },
5943     
5944     setActiveNext : function()
5945     {
5946         var i = this.indexOfNav(this.getActive());
5947         if (i > this.navItems.length) {
5948             return;
5949         }
5950         this.setActiveItem(this.navItems[i+1]);
5951     },
5952     setActivePrev : function()
5953     {
5954         var i = this.indexOfNav(this.getActive());
5955         if (i  < 1) {
5956             return;
5957         }
5958         this.setActiveItem(this.navItems[i-1]);
5959     },
5960     clearWasActive : function(except) {
5961         Roo.each(this.navItems, function(e) {
5962             if (e.tabId != except.tabId && e.was_active) {
5963                e.was_active = false;
5964                return false;
5965             }
5966             return true;
5967             
5968         });
5969     },
5970     getWasActive : function ()
5971     {
5972         var r = false;
5973         Roo.each(this.navItems, function(e) {
5974             if (e.was_active) {
5975                r = e;
5976                return false;
5977             }
5978             return true;
5979             
5980         });
5981         return r;
5982     }
5983     
5984     
5985 });
5986
5987  
5988 Roo.apply(Roo.bootstrap.NavGroup, {
5989     
5990     groups: {},
5991      /**
5992     * register a Navigation Group
5993     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5994     */
5995     register : function(navgrp)
5996     {
5997         this.groups[navgrp.navId] = navgrp;
5998         
5999     },
6000     /**
6001     * fetch a Navigation Group based on the navigation ID
6002     * @param {string} the navgroup to add
6003     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6004     */
6005     get: function(navId) {
6006         if (typeof(this.groups[navId]) == 'undefined') {
6007             return false;
6008             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6009         }
6010         return this.groups[navId] ;
6011     }
6012     
6013     
6014     
6015 });
6016
6017  /*
6018  * - LGPL
6019  *
6020  * row
6021  * 
6022  */
6023
6024 /**
6025  * @class Roo.bootstrap.NavItem
6026  * @extends Roo.bootstrap.Component
6027  * Bootstrap Navbar.NavItem class
6028  * @cfg {String} href  link to
6029  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) 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 {String} over what (parent or false to trigger manually.)
19608  * @cfg {Number} delay - delay before showing
19609  
19610  * @constructor
19611  * Create a new Popover
19612  * @param {Object} config The config object
19613  */
19614
19615 Roo.bootstrap.Popover = function(config){
19616     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19617     
19618     this.addEvents({
19619         // raw events
19620          /**
19621          * @event show
19622          * After the popover show
19623          * 
19624          * @param {Roo.bootstrap.Popover} this
19625          */
19626         "show" : true,
19627         /**
19628          * @event hide
19629          * After the popover hide
19630          * 
19631          * @param {Roo.bootstrap.Popover} this
19632          */
19633         "hide" : true
19634     });
19635 };
19636
19637 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19638     
19639     title: 'Fill in a title',
19640     html: false,
19641     
19642     placement : 'right',
19643     trigger : 'hover', // hover
19644     
19645     delay : 0,
19646     
19647     over: false,
19648     
19649     can_build_overlaid : false,
19650     
19651     getChildContainer : function()
19652     {
19653         return this.el.select('.popover-content',true).first();
19654     },
19655     
19656     getAutoCreate : function(){
19657          
19658         var cfg = {
19659            cls : 'popover roo-dynamic',
19660            style: 'display:block',
19661            cn : [
19662                 {
19663                     cls : 'arrow'
19664                 },
19665                 {
19666                     cls : 'popover-inner',
19667                     cn : [
19668                         {
19669                             tag: 'h3',
19670                             cls: 'popover-title popover-header',
19671                             html : this.title
19672                         },
19673                         {
19674                             cls : 'popover-content popover-body',
19675                             html : this.html
19676                         }
19677                     ]
19678                     
19679                 }
19680            ]
19681         };
19682         
19683         return cfg;
19684     },
19685     setTitle: function(str)
19686     {
19687         this.title = str;
19688         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19689     },
19690     setContent: function(str)
19691     {
19692         this.html = str;
19693         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19694     },
19695     // as it get's added to the bottom of the page.
19696     onRender : function(ct, position)
19697     {
19698         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19699         if(!this.el){
19700             var cfg = Roo.apply({},  this.getAutoCreate());
19701             cfg.id = Roo.id();
19702             
19703             if (this.cls) {
19704                 cfg.cls += ' ' + this.cls;
19705             }
19706             if (this.style) {
19707                 cfg.style = this.style;
19708             }
19709             //Roo.log("adding to ");
19710             this.el = Roo.get(document.body).createChild(cfg, position);
19711 //            Roo.log(this.el);
19712         }
19713         
19714         var nitems = [];
19715         if(typeof(this.items) != 'undefined'){
19716             var items = this.items;
19717             delete this.items;
19718
19719             for(var i =0;i < items.length;i++) {
19720                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19721             }
19722         }
19723
19724         this.items = nitems;
19725         
19726         
19727         this.initEvents();
19728     },
19729     
19730     initEvents : function()
19731     {
19732         
19733         Roo.bootstrap.Popover.register(this);
19734         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19735         this.el.enableDisplayMode('block');
19736         this.el.hide();
19737         if (this.over === false) {
19738             return; 
19739         }
19740         if (this.triggers === false) {
19741             return;
19742         }
19743          
19744         
19745         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19746         var triggers = this.trigger ? this.trigger.split(' ') : [];
19747         Roo.each(triggers, function(trigger) {
19748         
19749             if (trigger == 'click') {
19750                 on_el.on('click', this.toggle, this);
19751             } else if (trigger != 'manual') {
19752                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19753                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19754       
19755                 on_el.on(eventIn  ,this.enter, this);
19756                 on_el.on(eventOut, this.leave, this);
19757             }
19758         }, this);
19759         
19760     },
19761     
19762     
19763     // private
19764     timeout : null,
19765     hoverState : null,
19766     
19767     toggle : function () {
19768         this.hoverState == 'in' ? this.leave() : this.enter();
19769     },
19770     
19771     enter : function () {
19772         
19773         clearTimeout(this.timeout);
19774     
19775         this.hoverState = 'in';
19776     
19777         if (!this.delay || !this.delay.show) {
19778             this.show();
19779             return;
19780         }
19781         var _t = this;
19782         this.timeout = setTimeout(function () {
19783             if (_t.hoverState == 'in') {
19784                 _t.show();
19785             }
19786         }, this.delay.show)
19787     },
19788     
19789     leave : function() {
19790         clearTimeout(this.timeout);
19791     
19792         this.hoverState = 'out';
19793     
19794         if (!this.delay || !this.delay.hide) {
19795             this.hide();
19796             return;
19797         }
19798         var _t = this;
19799         this.timeout = setTimeout(function () {
19800             if (_t.hoverState == 'out') {
19801                 _t.hide();
19802             }
19803         }, this.delay.hide)
19804     },
19805     
19806     show : function (on_el)
19807     {
19808         if (!on_el) {
19809             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19810         }
19811         
19812         if (!this.el) {
19813             this.render(document.body);
19814         }
19815         
19816         // set content.
19817         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19818         if (this.html !== false) {
19819             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19820         }
19821         this.el.removeClass([
19822             'fade','top','bottom', 'left', 'right','in',
19823             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19824         ]);
19825         if (!this.title.length) {
19826             this.el.select('.popover-title',true).hide();
19827         }
19828         
19829         var placement = typeof this.placement == 'function' ?
19830             this.placement.call(this, this.el, on_el) :
19831             this.placement;
19832             
19833         var autoToken = /\s?auto?\s?/i;
19834         var autoPlace = autoToken.test(placement);
19835         if (autoPlace) {
19836             placement = placement.replace(autoToken, '') || 'top';
19837         }
19838         
19839         //this.el.detach()
19840         //this.el.setXY([0,0]);
19841         this.el.show();
19842         this.el.dom.style.display='block';
19843         
19844         //this.el.appendTo(on_el);
19845         
19846         var p = this.getPosition();
19847         var box = this.el.getBox();
19848         
19849         if (autoPlace) {
19850             // fixme..
19851         }
19852         var align = Roo.bootstrap.Popover.alignment[placement];
19853         this.el.addClass(align[2]);
19854
19855 //        Roo.log(align);
19856         this.el.alignTo(on_el, align[0],align[1]);
19857         //var arrow = this.el.select('.arrow',true).first();
19858         //arrow.set(align[2], 
19859         
19860         this.el.addClass('in');
19861         
19862         
19863         if (this.el.hasClass('fade')) {
19864             // fade it?
19865         }
19866         
19867         this.hoverState = 'in';
19868         
19869         this.fireEvent('show', this);
19870         
19871     },
19872     hide : function()
19873     {
19874         this.el.setXY([0,0]);
19875         this.el.removeClass('in');
19876         this.el.hide();
19877         this.hoverState = null;
19878         
19879         this.fireEvent('hide', this);
19880     }
19881     
19882 });
19883
19884
19885 Roo.apply(Roo.bootstrap.Popover, {
19886
19887     alignment : {
19888         'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19889         'right' : ['l-r', [10,0], 'left bs-popover-left'],
19890         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19891         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19892     },
19893
19894     clickHander : false,
19895     
19896
19897     onMouseDown : function(e)
19898     {
19899         if (!e.getTarget(".roo-popup")) {
19900             this.hideAll();
19901         }
19902          
19903     },
19904     
19905     popups : [],
19906     
19907     register : function(popup)
19908     {
19909         if (this.clickHandler === false) {
19910             this.clickHandler = Roo.get(document).un("mousedown", this.onMouseDown, this);
19911         }
19912         // hide other popups.
19913         this.hideAll();
19914         this.popups.push(popup);
19915     },
19916     hideAll : function()
19917     {
19918         this.popups.each(function(p) {
19919             p.hide();
19920         });
19921     }
19922
19923 });/*
19924  * - LGPL
19925  *
19926  * Progress
19927  * 
19928  */
19929
19930 /**
19931  * @class Roo.bootstrap.Progress
19932  * @extends Roo.bootstrap.Component
19933  * Bootstrap Progress class
19934  * @cfg {Boolean} striped striped of the progress bar
19935  * @cfg {Boolean} active animated of the progress bar
19936  * 
19937  * 
19938  * @constructor
19939  * Create a new Progress
19940  * @param {Object} config The config object
19941  */
19942
19943 Roo.bootstrap.Progress = function(config){
19944     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19945 };
19946
19947 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19948     
19949     striped : false,
19950     active: false,
19951     
19952     getAutoCreate : function(){
19953         var cfg = {
19954             tag: 'div',
19955             cls: 'progress'
19956         };
19957         
19958         
19959         if(this.striped){
19960             cfg.cls += ' progress-striped';
19961         }
19962       
19963         if(this.active){
19964             cfg.cls += ' active';
19965         }
19966         
19967         
19968         return cfg;
19969     }
19970    
19971 });
19972
19973  
19974
19975  /*
19976  * - LGPL
19977  *
19978  * ProgressBar
19979  * 
19980  */
19981
19982 /**
19983  * @class Roo.bootstrap.ProgressBar
19984  * @extends Roo.bootstrap.Component
19985  * Bootstrap ProgressBar class
19986  * @cfg {Number} aria_valuenow aria-value now
19987  * @cfg {Number} aria_valuemin aria-value min
19988  * @cfg {Number} aria_valuemax aria-value max
19989  * @cfg {String} label label for the progress bar
19990  * @cfg {String} panel (success | info | warning | danger )
19991  * @cfg {String} role role of the progress bar
19992  * @cfg {String} sr_only text
19993  * 
19994  * 
19995  * @constructor
19996  * Create a new ProgressBar
19997  * @param {Object} config The config object
19998  */
19999
20000 Roo.bootstrap.ProgressBar = function(config){
20001     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20002 };
20003
20004 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20005     
20006     aria_valuenow : 0,
20007     aria_valuemin : 0,
20008     aria_valuemax : 100,
20009     label : false,
20010     panel : false,
20011     role : false,
20012     sr_only: false,
20013     
20014     getAutoCreate : function()
20015     {
20016         
20017         var cfg = {
20018             tag: 'div',
20019             cls: 'progress-bar',
20020             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20021         };
20022         
20023         if(this.sr_only){
20024             cfg.cn = {
20025                 tag: 'span',
20026                 cls: 'sr-only',
20027                 html: this.sr_only
20028             }
20029         }
20030         
20031         if(this.role){
20032             cfg.role = this.role;
20033         }
20034         
20035         if(this.aria_valuenow){
20036             cfg['aria-valuenow'] = this.aria_valuenow;
20037         }
20038         
20039         if(this.aria_valuemin){
20040             cfg['aria-valuemin'] = this.aria_valuemin;
20041         }
20042         
20043         if(this.aria_valuemax){
20044             cfg['aria-valuemax'] = this.aria_valuemax;
20045         }
20046         
20047         if(this.label && !this.sr_only){
20048             cfg.html = this.label;
20049         }
20050         
20051         if(this.panel){
20052             cfg.cls += ' progress-bar-' + this.panel;
20053         }
20054         
20055         return cfg;
20056     },
20057     
20058     update : function(aria_valuenow)
20059     {
20060         this.aria_valuenow = aria_valuenow;
20061         
20062         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20063     }
20064    
20065 });
20066
20067  
20068
20069  /*
20070  * - LGPL
20071  *
20072  * column
20073  * 
20074  */
20075
20076 /**
20077  * @class Roo.bootstrap.TabGroup
20078  * @extends Roo.bootstrap.Column
20079  * Bootstrap Column class
20080  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20081  * @cfg {Boolean} carousel true to make the group behave like a carousel
20082  * @cfg {Boolean} bullets show bullets for the panels
20083  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20084  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20085  * @cfg {Boolean} showarrow (true|false) show arrow default true
20086  * 
20087  * @constructor
20088  * Create a new TabGroup
20089  * @param {Object} config The config object
20090  */
20091
20092 Roo.bootstrap.TabGroup = function(config){
20093     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20094     if (!this.navId) {
20095         this.navId = Roo.id();
20096     }
20097     this.tabs = [];
20098     Roo.bootstrap.TabGroup.register(this);
20099     
20100 };
20101
20102 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20103     
20104     carousel : false,
20105     transition : false,
20106     bullets : 0,
20107     timer : 0,
20108     autoslide : false,
20109     slideFn : false,
20110     slideOnTouch : false,
20111     showarrow : true,
20112     
20113     getAutoCreate : function()
20114     {
20115         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20116         
20117         cfg.cls += ' tab-content';
20118         
20119         if (this.carousel) {
20120             cfg.cls += ' carousel slide';
20121             
20122             cfg.cn = [{
20123                cls : 'carousel-inner',
20124                cn : []
20125             }];
20126         
20127             if(this.bullets  && !Roo.isTouch){
20128                 
20129                 var bullets = {
20130                     cls : 'carousel-bullets',
20131                     cn : []
20132                 };
20133                
20134                 if(this.bullets_cls){
20135                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20136                 }
20137                 
20138                 bullets.cn.push({
20139                     cls : 'clear'
20140                 });
20141                 
20142                 cfg.cn[0].cn.push(bullets);
20143             }
20144             
20145             if(this.showarrow){
20146                 cfg.cn[0].cn.push({
20147                     tag : 'div',
20148                     class : 'carousel-arrow',
20149                     cn : [
20150                         {
20151                             tag : 'div',
20152                             class : 'carousel-prev',
20153                             cn : [
20154                                 {
20155                                     tag : 'i',
20156                                     class : 'fa fa-chevron-left'
20157                                 }
20158                             ]
20159                         },
20160                         {
20161                             tag : 'div',
20162                             class : 'carousel-next',
20163                             cn : [
20164                                 {
20165                                     tag : 'i',
20166                                     class : 'fa fa-chevron-right'
20167                                 }
20168                             ]
20169                         }
20170                     ]
20171                 });
20172             }
20173             
20174         }
20175         
20176         return cfg;
20177     },
20178     
20179     initEvents:  function()
20180     {
20181 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20182 //            this.el.on("touchstart", this.onTouchStart, this);
20183 //        }
20184         
20185         if(this.autoslide){
20186             var _this = this;
20187             
20188             this.slideFn = window.setInterval(function() {
20189                 _this.showPanelNext();
20190             }, this.timer);
20191         }
20192         
20193         if(this.showarrow){
20194             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20195             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20196         }
20197         
20198         
20199     },
20200     
20201 //    onTouchStart : function(e, el, o)
20202 //    {
20203 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20204 //            return;
20205 //        }
20206 //        
20207 //        this.showPanelNext();
20208 //    },
20209     
20210     
20211     getChildContainer : function()
20212     {
20213         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20214     },
20215     
20216     /**
20217     * register a Navigation item
20218     * @param {Roo.bootstrap.NavItem} the navitem to add
20219     */
20220     register : function(item)
20221     {
20222         this.tabs.push( item);
20223         item.navId = this.navId; // not really needed..
20224         this.addBullet();
20225     
20226     },
20227     
20228     getActivePanel : function()
20229     {
20230         var r = false;
20231         Roo.each(this.tabs, function(t) {
20232             if (t.active) {
20233                 r = t;
20234                 return false;
20235             }
20236             return null;
20237         });
20238         return r;
20239         
20240     },
20241     getPanelByName : function(n)
20242     {
20243         var r = false;
20244         Roo.each(this.tabs, function(t) {
20245             if (t.tabId == n) {
20246                 r = t;
20247                 return false;
20248             }
20249             return null;
20250         });
20251         return r;
20252     },
20253     indexOfPanel : function(p)
20254     {
20255         var r = false;
20256         Roo.each(this.tabs, function(t,i) {
20257             if (t.tabId == p.tabId) {
20258                 r = i;
20259                 return false;
20260             }
20261             return null;
20262         });
20263         return r;
20264     },
20265     /**
20266      * show a specific panel
20267      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20268      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20269      */
20270     showPanel : function (pan)
20271     {
20272         if(this.transition || typeof(pan) == 'undefined'){
20273             Roo.log("waiting for the transitionend");
20274             return false;
20275         }
20276         
20277         if (typeof(pan) == 'number') {
20278             pan = this.tabs[pan];
20279         }
20280         
20281         if (typeof(pan) == 'string') {
20282             pan = this.getPanelByName(pan);
20283         }
20284         
20285         var cur = this.getActivePanel();
20286         
20287         if(!pan || !cur){
20288             Roo.log('pan or acitve pan is undefined');
20289             return false;
20290         }
20291         
20292         if (pan.tabId == this.getActivePanel().tabId) {
20293             return true;
20294         }
20295         
20296         if (false === cur.fireEvent('beforedeactivate')) {
20297             return false;
20298         }
20299         
20300         if(this.bullets > 0 && !Roo.isTouch){
20301             this.setActiveBullet(this.indexOfPanel(pan));
20302         }
20303         
20304         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20305             
20306             //class="carousel-item carousel-item-next carousel-item-left"
20307             
20308             this.transition = true;
20309             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20310             var lr = dir == 'next' ? 'left' : 'right';
20311             pan.el.addClass(dir); // or prev
20312             pan.el.addClass('carousel-item-' + dir); // or prev
20313             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20314             cur.el.addClass(lr); // or right
20315             pan.el.addClass(lr);
20316             cur.el.addClass('carousel-item-' +lr); // or right
20317             pan.el.addClass('carousel-item-' +lr);
20318             
20319             
20320             var _this = this;
20321             cur.el.on('transitionend', function() {
20322                 Roo.log("trans end?");
20323                 
20324                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20325                 pan.setActive(true);
20326                 
20327                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20328                 cur.setActive(false);
20329                 
20330                 _this.transition = false;
20331                 
20332             }, this, { single:  true } );
20333             
20334             return true;
20335         }
20336         
20337         cur.setActive(false);
20338         pan.setActive(true);
20339         
20340         return true;
20341         
20342     },
20343     showPanelNext : function()
20344     {
20345         var i = this.indexOfPanel(this.getActivePanel());
20346         
20347         if (i >= this.tabs.length - 1 && !this.autoslide) {
20348             return;
20349         }
20350         
20351         if (i >= this.tabs.length - 1 && this.autoslide) {
20352             i = -1;
20353         }
20354         
20355         this.showPanel(this.tabs[i+1]);
20356     },
20357     
20358     showPanelPrev : function()
20359     {
20360         var i = this.indexOfPanel(this.getActivePanel());
20361         
20362         if (i  < 1 && !this.autoslide) {
20363             return;
20364         }
20365         
20366         if (i < 1 && this.autoslide) {
20367             i = this.tabs.length;
20368         }
20369         
20370         this.showPanel(this.tabs[i-1]);
20371     },
20372     
20373     
20374     addBullet: function()
20375     {
20376         if(!this.bullets || Roo.isTouch){
20377             return;
20378         }
20379         var ctr = this.el.select('.carousel-bullets',true).first();
20380         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20381         var bullet = ctr.createChild({
20382             cls : 'bullet bullet-' + i
20383         },ctr.dom.lastChild);
20384         
20385         
20386         var _this = this;
20387         
20388         bullet.on('click', (function(e, el, o, ii, t){
20389
20390             e.preventDefault();
20391
20392             this.showPanel(ii);
20393
20394             if(this.autoslide && this.slideFn){
20395                 clearInterval(this.slideFn);
20396                 this.slideFn = window.setInterval(function() {
20397                     _this.showPanelNext();
20398                 }, this.timer);
20399             }
20400
20401         }).createDelegate(this, [i, bullet], true));
20402                 
20403         
20404     },
20405      
20406     setActiveBullet : function(i)
20407     {
20408         if(Roo.isTouch){
20409             return;
20410         }
20411         
20412         Roo.each(this.el.select('.bullet', true).elements, function(el){
20413             el.removeClass('selected');
20414         });
20415
20416         var bullet = this.el.select('.bullet-' + i, true).first();
20417         
20418         if(!bullet){
20419             return;
20420         }
20421         
20422         bullet.addClass('selected');
20423     }
20424     
20425     
20426   
20427 });
20428
20429  
20430
20431  
20432  
20433 Roo.apply(Roo.bootstrap.TabGroup, {
20434     
20435     groups: {},
20436      /**
20437     * register a Navigation Group
20438     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20439     */
20440     register : function(navgrp)
20441     {
20442         this.groups[navgrp.navId] = navgrp;
20443         
20444     },
20445     /**
20446     * fetch a Navigation Group based on the navigation ID
20447     * if one does not exist , it will get created.
20448     * @param {string} the navgroup to add
20449     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20450     */
20451     get: function(navId) {
20452         if (typeof(this.groups[navId]) == 'undefined') {
20453             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20454         }
20455         return this.groups[navId] ;
20456     }
20457     
20458     
20459     
20460 });
20461
20462  /*
20463  * - LGPL
20464  *
20465  * TabPanel
20466  * 
20467  */
20468
20469 /**
20470  * @class Roo.bootstrap.TabPanel
20471  * @extends Roo.bootstrap.Component
20472  * Bootstrap TabPanel class
20473  * @cfg {Boolean} active panel active
20474  * @cfg {String} html panel content
20475  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20476  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20477  * @cfg {String} href click to link..
20478  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20479  * 
20480  * 
20481  * @constructor
20482  * Create a new TabPanel
20483  * @param {Object} config The config object
20484  */
20485
20486 Roo.bootstrap.TabPanel = function(config){
20487     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20488     this.addEvents({
20489         /**
20490              * @event changed
20491              * Fires when the active status changes
20492              * @param {Roo.bootstrap.TabPanel} this
20493              * @param {Boolean} state the new state
20494             
20495          */
20496         'changed': true,
20497         /**
20498              * @event beforedeactivate
20499              * Fires before a tab is de-activated - can be used to do validation on a form.
20500              * @param {Roo.bootstrap.TabPanel} this
20501              * @return {Boolean} false if there is an error
20502             
20503          */
20504         'beforedeactivate': true
20505      });
20506     
20507     this.tabId = this.tabId || Roo.id();
20508   
20509 };
20510
20511 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20512     
20513     active: false,
20514     html: false,
20515     tabId: false,
20516     navId : false,
20517     href : '',
20518     touchSlide : false,
20519     getAutoCreate : function(){
20520         
20521         
20522         var cfg = {
20523             tag: 'div',
20524             // item is needed for carousel - not sure if it has any effect otherwise
20525             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20526             html: this.html || ''
20527         };
20528         
20529         if(this.active){
20530             cfg.cls += ' active';
20531         }
20532         
20533         if(this.tabId){
20534             cfg.tabId = this.tabId;
20535         }
20536         
20537         
20538         
20539         return cfg;
20540     },
20541     
20542     initEvents:  function()
20543     {
20544         var p = this.parent();
20545         
20546         this.navId = this.navId || p.navId;
20547         
20548         if (typeof(this.navId) != 'undefined') {
20549             // not really needed.. but just in case.. parent should be a NavGroup.
20550             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20551             
20552             tg.register(this);
20553             
20554             var i = tg.tabs.length - 1;
20555             
20556             if(this.active && tg.bullets > 0 && i < tg.bullets){
20557                 tg.setActiveBullet(i);
20558             }
20559         }
20560         
20561         this.el.on('click', this.onClick, this);
20562         
20563         if(Roo.isTouch && this.touchSlide){
20564             this.el.on("touchstart", this.onTouchStart, this);
20565             this.el.on("touchmove", this.onTouchMove, this);
20566             this.el.on("touchend", this.onTouchEnd, this);
20567         }
20568         
20569     },
20570     
20571     onRender : function(ct, position)
20572     {
20573         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20574     },
20575     
20576     setActive : function(state)
20577     {
20578         Roo.log("panel - set active " + this.tabId + "=" + state);
20579         
20580         this.active = state;
20581         if (!state) {
20582             this.el.removeClass('active');
20583             
20584         } else  if (!this.el.hasClass('active')) {
20585             this.el.addClass('active');
20586         }
20587         
20588         this.fireEvent('changed', this, state);
20589     },
20590     
20591     onClick : function(e)
20592     {
20593         e.preventDefault();
20594         
20595         if(!this.href.length){
20596             return;
20597         }
20598         
20599         window.location.href = this.href;
20600     },
20601     
20602     startX : 0,
20603     startY : 0,
20604     endX : 0,
20605     endY : 0,
20606     swiping : false,
20607     
20608     onTouchStart : function(e)
20609     {
20610         this.swiping = false;
20611         
20612         this.startX = e.browserEvent.touches[0].clientX;
20613         this.startY = e.browserEvent.touches[0].clientY;
20614     },
20615     
20616     onTouchMove : function(e)
20617     {
20618         this.swiping = true;
20619         
20620         this.endX = e.browserEvent.touches[0].clientX;
20621         this.endY = e.browserEvent.touches[0].clientY;
20622     },
20623     
20624     onTouchEnd : function(e)
20625     {
20626         if(!this.swiping){
20627             this.onClick(e);
20628             return;
20629         }
20630         
20631         var tabGroup = this.parent();
20632         
20633         if(this.endX > this.startX){ // swiping right
20634             tabGroup.showPanelPrev();
20635             return;
20636         }
20637         
20638         if(this.startX > this.endX){ // swiping left
20639             tabGroup.showPanelNext();
20640             return;
20641         }
20642     }
20643     
20644     
20645 });
20646  
20647
20648  
20649
20650  /*
20651  * - LGPL
20652  *
20653  * DateField
20654  * 
20655  */
20656
20657 /**
20658  * @class Roo.bootstrap.DateField
20659  * @extends Roo.bootstrap.Input
20660  * Bootstrap DateField class
20661  * @cfg {Number} weekStart default 0
20662  * @cfg {String} viewMode default empty, (months|years)
20663  * @cfg {String} minViewMode default empty, (months|years)
20664  * @cfg {Number} startDate default -Infinity
20665  * @cfg {Number} endDate default Infinity
20666  * @cfg {Boolean} todayHighlight default false
20667  * @cfg {Boolean} todayBtn default false
20668  * @cfg {Boolean} calendarWeeks default false
20669  * @cfg {Object} daysOfWeekDisabled default empty
20670  * @cfg {Boolean} singleMode default false (true | false)
20671  * 
20672  * @cfg {Boolean} keyboardNavigation default true
20673  * @cfg {String} language default en
20674  * 
20675  * @constructor
20676  * Create a new DateField
20677  * @param {Object} config The config object
20678  */
20679
20680 Roo.bootstrap.DateField = function(config){
20681     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20682      this.addEvents({
20683             /**
20684              * @event show
20685              * Fires when this field show.
20686              * @param {Roo.bootstrap.DateField} this
20687              * @param {Mixed} date The date value
20688              */
20689             show : true,
20690             /**
20691              * @event show
20692              * Fires when this field hide.
20693              * @param {Roo.bootstrap.DateField} this
20694              * @param {Mixed} date The date value
20695              */
20696             hide : true,
20697             /**
20698              * @event select
20699              * Fires when select a date.
20700              * @param {Roo.bootstrap.DateField} this
20701              * @param {Mixed} date The date value
20702              */
20703             select : true,
20704             /**
20705              * @event beforeselect
20706              * Fires when before select a date.
20707              * @param {Roo.bootstrap.DateField} this
20708              * @param {Mixed} date The date value
20709              */
20710             beforeselect : true
20711         });
20712 };
20713
20714 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20715     
20716     /**
20717      * @cfg {String} format
20718      * The default date format string which can be overriden for localization support.  The format must be
20719      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20720      */
20721     format : "m/d/y",
20722     /**
20723      * @cfg {String} altFormats
20724      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20725      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20726      */
20727     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20728     
20729     weekStart : 0,
20730     
20731     viewMode : '',
20732     
20733     minViewMode : '',
20734     
20735     todayHighlight : false,
20736     
20737     todayBtn: false,
20738     
20739     language: 'en',
20740     
20741     keyboardNavigation: true,
20742     
20743     calendarWeeks: false,
20744     
20745     startDate: -Infinity,
20746     
20747     endDate: Infinity,
20748     
20749     daysOfWeekDisabled: [],
20750     
20751     _events: [],
20752     
20753     singleMode : false,
20754     
20755     UTCDate: function()
20756     {
20757         return new Date(Date.UTC.apply(Date, arguments));
20758     },
20759     
20760     UTCToday: function()
20761     {
20762         var today = new Date();
20763         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20764     },
20765     
20766     getDate: function() {
20767             var d = this.getUTCDate();
20768             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20769     },
20770     
20771     getUTCDate: function() {
20772             return this.date;
20773     },
20774     
20775     setDate: function(d) {
20776             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20777     },
20778     
20779     setUTCDate: function(d) {
20780             this.date = d;
20781             this.setValue(this.formatDate(this.date));
20782     },
20783         
20784     onRender: function(ct, position)
20785     {
20786         
20787         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20788         
20789         this.language = this.language || 'en';
20790         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20791         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20792         
20793         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20794         this.format = this.format || 'm/d/y';
20795         this.isInline = false;
20796         this.isInput = true;
20797         this.component = this.el.select('.add-on', true).first() || false;
20798         this.component = (this.component && this.component.length === 0) ? false : this.component;
20799         this.hasInput = this.component && this.inputEl().length;
20800         
20801         if (typeof(this.minViewMode === 'string')) {
20802             switch (this.minViewMode) {
20803                 case 'months':
20804                     this.minViewMode = 1;
20805                     break;
20806                 case 'years':
20807                     this.minViewMode = 2;
20808                     break;
20809                 default:
20810                     this.minViewMode = 0;
20811                     break;
20812             }
20813         }
20814         
20815         if (typeof(this.viewMode === 'string')) {
20816             switch (this.viewMode) {
20817                 case 'months':
20818                     this.viewMode = 1;
20819                     break;
20820                 case 'years':
20821                     this.viewMode = 2;
20822                     break;
20823                 default:
20824                     this.viewMode = 0;
20825                     break;
20826             }
20827         }
20828                 
20829         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20830         
20831 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20832         
20833         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20834         
20835         this.picker().on('mousedown', this.onMousedown, this);
20836         this.picker().on('click', this.onClick, this);
20837         
20838         this.picker().addClass('datepicker-dropdown');
20839         
20840         this.startViewMode = this.viewMode;
20841         
20842         if(this.singleMode){
20843             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20844                 v.setVisibilityMode(Roo.Element.DISPLAY);
20845                 v.hide();
20846             });
20847             
20848             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20849                 v.setStyle('width', '189px');
20850             });
20851         }
20852         
20853         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20854             if(!this.calendarWeeks){
20855                 v.remove();
20856                 return;
20857             }
20858             
20859             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20860             v.attr('colspan', function(i, val){
20861                 return parseInt(val) + 1;
20862             });
20863         });
20864                         
20865         
20866         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20867         
20868         this.setStartDate(this.startDate);
20869         this.setEndDate(this.endDate);
20870         
20871         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20872         
20873         this.fillDow();
20874         this.fillMonths();
20875         this.update();
20876         this.showMode();
20877         
20878         if(this.isInline) {
20879             this.showPopup();
20880         }
20881     },
20882     
20883     picker : function()
20884     {
20885         return this.pickerEl;
20886 //        return this.el.select('.datepicker', true).first();
20887     },
20888     
20889     fillDow: function()
20890     {
20891         var dowCnt = this.weekStart;
20892         
20893         var dow = {
20894             tag: 'tr',
20895             cn: [
20896                 
20897             ]
20898         };
20899         
20900         if(this.calendarWeeks){
20901             dow.cn.push({
20902                 tag: 'th',
20903                 cls: 'cw',
20904                 html: '&nbsp;'
20905             })
20906         }
20907         
20908         while (dowCnt < this.weekStart + 7) {
20909             dow.cn.push({
20910                 tag: 'th',
20911                 cls: 'dow',
20912                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20913             });
20914         }
20915         
20916         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20917     },
20918     
20919     fillMonths: function()
20920     {    
20921         var i = 0;
20922         var months = this.picker().select('>.datepicker-months td', true).first();
20923         
20924         months.dom.innerHTML = '';
20925         
20926         while (i < 12) {
20927             var month = {
20928                 tag: 'span',
20929                 cls: 'month',
20930                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20931             };
20932             
20933             months.createChild(month);
20934         }
20935         
20936     },
20937     
20938     update: function()
20939     {
20940         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;
20941         
20942         if (this.date < this.startDate) {
20943             this.viewDate = new Date(this.startDate);
20944         } else if (this.date > this.endDate) {
20945             this.viewDate = new Date(this.endDate);
20946         } else {
20947             this.viewDate = new Date(this.date);
20948         }
20949         
20950         this.fill();
20951     },
20952     
20953     fill: function() 
20954     {
20955         var d = new Date(this.viewDate),
20956                 year = d.getUTCFullYear(),
20957                 month = d.getUTCMonth(),
20958                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20959                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20960                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20961                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20962                 currentDate = this.date && this.date.valueOf(),
20963                 today = this.UTCToday();
20964         
20965         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20966         
20967 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20968         
20969 //        this.picker.select('>tfoot th.today').
20970 //                                              .text(dates[this.language].today)
20971 //                                              .toggle(this.todayBtn !== false);
20972     
20973         this.updateNavArrows();
20974         this.fillMonths();
20975                                                 
20976         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20977         
20978         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20979          
20980         prevMonth.setUTCDate(day);
20981         
20982         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20983         
20984         var nextMonth = new Date(prevMonth);
20985         
20986         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20987         
20988         nextMonth = nextMonth.valueOf();
20989         
20990         var fillMonths = false;
20991         
20992         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20993         
20994         while(prevMonth.valueOf() <= nextMonth) {
20995             var clsName = '';
20996             
20997             if (prevMonth.getUTCDay() === this.weekStart) {
20998                 if(fillMonths){
20999                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21000                 }
21001                     
21002                 fillMonths = {
21003                     tag: 'tr',
21004                     cn: []
21005                 };
21006                 
21007                 if(this.calendarWeeks){
21008                     // ISO 8601: First week contains first thursday.
21009                     // ISO also states week starts on Monday, but we can be more abstract here.
21010                     var
21011                     // Start of current week: based on weekstart/current date
21012                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21013                     // Thursday of this week
21014                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21015                     // First Thursday of year, year from thursday
21016                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21017                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21018                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21019                     
21020                     fillMonths.cn.push({
21021                         tag: 'td',
21022                         cls: 'cw',
21023                         html: calWeek
21024                     });
21025                 }
21026             }
21027             
21028             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21029                 clsName += ' old';
21030             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21031                 clsName += ' new';
21032             }
21033             if (this.todayHighlight &&
21034                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21035                 prevMonth.getUTCMonth() == today.getMonth() &&
21036                 prevMonth.getUTCDate() == today.getDate()) {
21037                 clsName += ' today';
21038             }
21039             
21040             if (currentDate && prevMonth.valueOf() === currentDate) {
21041                 clsName += ' active';
21042             }
21043             
21044             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21045                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21046                     clsName += ' disabled';
21047             }
21048             
21049             fillMonths.cn.push({
21050                 tag: 'td',
21051                 cls: 'day ' + clsName,
21052                 html: prevMonth.getDate()
21053             });
21054             
21055             prevMonth.setDate(prevMonth.getDate()+1);
21056         }
21057           
21058         var currentYear = this.date && this.date.getUTCFullYear();
21059         var currentMonth = this.date && this.date.getUTCMonth();
21060         
21061         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21062         
21063         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21064             v.removeClass('active');
21065             
21066             if(currentYear === year && k === currentMonth){
21067                 v.addClass('active');
21068             }
21069             
21070             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21071                 v.addClass('disabled');
21072             }
21073             
21074         });
21075         
21076         
21077         year = parseInt(year/10, 10) * 10;
21078         
21079         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21080         
21081         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21082         
21083         year -= 1;
21084         for (var i = -1; i < 11; i++) {
21085             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21086                 tag: 'span',
21087                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21088                 html: year
21089             });
21090             
21091             year += 1;
21092         }
21093     },
21094     
21095     showMode: function(dir) 
21096     {
21097         if (dir) {
21098             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21099         }
21100         
21101         Roo.each(this.picker().select('>div',true).elements, function(v){
21102             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21103             v.hide();
21104         });
21105         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21106     },
21107     
21108     place: function()
21109     {
21110         if(this.isInline) {
21111             return;
21112         }
21113         
21114         this.picker().removeClass(['bottom', 'top']);
21115         
21116         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21117             /*
21118              * place to the top of element!
21119              *
21120              */
21121             
21122             this.picker().addClass('top');
21123             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21124             
21125             return;
21126         }
21127         
21128         this.picker().addClass('bottom');
21129         
21130         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21131     },
21132     
21133     parseDate : function(value)
21134     {
21135         if(!value || value instanceof Date){
21136             return value;
21137         }
21138         var v = Date.parseDate(value, this.format);
21139         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21140             v = Date.parseDate(value, 'Y-m-d');
21141         }
21142         if(!v && this.altFormats){
21143             if(!this.altFormatsArray){
21144                 this.altFormatsArray = this.altFormats.split("|");
21145             }
21146             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21147                 v = Date.parseDate(value, this.altFormatsArray[i]);
21148             }
21149         }
21150         return v;
21151     },
21152     
21153     formatDate : function(date, fmt)
21154     {   
21155         return (!date || !(date instanceof Date)) ?
21156         date : date.dateFormat(fmt || this.format);
21157     },
21158     
21159     onFocus : function()
21160     {
21161         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21162         this.showPopup();
21163     },
21164     
21165     onBlur : function()
21166     {
21167         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21168         
21169         var d = this.inputEl().getValue();
21170         
21171         this.setValue(d);
21172                 
21173         this.hidePopup();
21174     },
21175     
21176     showPopup : function()
21177     {
21178         this.picker().show();
21179         this.update();
21180         this.place();
21181         
21182         this.fireEvent('showpopup', this, this.date);
21183     },
21184     
21185     hidePopup : function()
21186     {
21187         if(this.isInline) {
21188             return;
21189         }
21190         this.picker().hide();
21191         this.viewMode = this.startViewMode;
21192         this.showMode();
21193         
21194         this.fireEvent('hidepopup', this, this.date);
21195         
21196     },
21197     
21198     onMousedown: function(e)
21199     {
21200         e.stopPropagation();
21201         e.preventDefault();
21202     },
21203     
21204     keyup: function(e)
21205     {
21206         Roo.bootstrap.DateField.superclass.keyup.call(this);
21207         this.update();
21208     },
21209
21210     setValue: function(v)
21211     {
21212         if(this.fireEvent('beforeselect', this, v) !== false){
21213             var d = new Date(this.parseDate(v) ).clearTime();
21214         
21215             if(isNaN(d.getTime())){
21216                 this.date = this.viewDate = '';
21217                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21218                 return;
21219             }
21220
21221             v = this.formatDate(d);
21222
21223             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21224
21225             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21226
21227             this.update();
21228
21229             this.fireEvent('select', this, this.date);
21230         }
21231     },
21232     
21233     getValue: function()
21234     {
21235         return this.formatDate(this.date);
21236     },
21237     
21238     fireKey: function(e)
21239     {
21240         if (!this.picker().isVisible()){
21241             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21242                 this.showPopup();
21243             }
21244             return;
21245         }
21246         
21247         var dateChanged = false,
21248         dir, day, month,
21249         newDate, newViewDate;
21250         
21251         switch(e.keyCode){
21252             case 27: // escape
21253                 this.hidePopup();
21254                 e.preventDefault();
21255                 break;
21256             case 37: // left
21257             case 39: // right
21258                 if (!this.keyboardNavigation) {
21259                     break;
21260                 }
21261                 dir = e.keyCode == 37 ? -1 : 1;
21262                 
21263                 if (e.ctrlKey){
21264                     newDate = this.moveYear(this.date, dir);
21265                     newViewDate = this.moveYear(this.viewDate, dir);
21266                 } else if (e.shiftKey){
21267                     newDate = this.moveMonth(this.date, dir);
21268                     newViewDate = this.moveMonth(this.viewDate, dir);
21269                 } else {
21270                     newDate = new Date(this.date);
21271                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21272                     newViewDate = new Date(this.viewDate);
21273                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21274                 }
21275                 if (this.dateWithinRange(newDate)){
21276                     this.date = newDate;
21277                     this.viewDate = newViewDate;
21278                     this.setValue(this.formatDate(this.date));
21279 //                    this.update();
21280                     e.preventDefault();
21281                     dateChanged = true;
21282                 }
21283                 break;
21284             case 38: // up
21285             case 40: // down
21286                 if (!this.keyboardNavigation) {
21287                     break;
21288                 }
21289                 dir = e.keyCode == 38 ? -1 : 1;
21290                 if (e.ctrlKey){
21291                     newDate = this.moveYear(this.date, dir);
21292                     newViewDate = this.moveYear(this.viewDate, dir);
21293                 } else if (e.shiftKey){
21294                     newDate = this.moveMonth(this.date, dir);
21295                     newViewDate = this.moveMonth(this.viewDate, dir);
21296                 } else {
21297                     newDate = new Date(this.date);
21298                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21299                     newViewDate = new Date(this.viewDate);
21300                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21301                 }
21302                 if (this.dateWithinRange(newDate)){
21303                     this.date = newDate;
21304                     this.viewDate = newViewDate;
21305                     this.setValue(this.formatDate(this.date));
21306 //                    this.update();
21307                     e.preventDefault();
21308                     dateChanged = true;
21309                 }
21310                 break;
21311             case 13: // enter
21312                 this.setValue(this.formatDate(this.date));
21313                 this.hidePopup();
21314                 e.preventDefault();
21315                 break;
21316             case 9: // tab
21317                 this.setValue(this.formatDate(this.date));
21318                 this.hidePopup();
21319                 break;
21320             case 16: // shift
21321             case 17: // ctrl
21322             case 18: // alt
21323                 break;
21324             default :
21325                 this.hidePopup();
21326                 
21327         }
21328     },
21329     
21330     
21331     onClick: function(e) 
21332     {
21333         e.stopPropagation();
21334         e.preventDefault();
21335         
21336         var target = e.getTarget();
21337         
21338         if(target.nodeName.toLowerCase() === 'i'){
21339             target = Roo.get(target).dom.parentNode;
21340         }
21341         
21342         var nodeName = target.nodeName;
21343         var className = target.className;
21344         var html = target.innerHTML;
21345         //Roo.log(nodeName);
21346         
21347         switch(nodeName.toLowerCase()) {
21348             case 'th':
21349                 switch(className) {
21350                     case 'switch':
21351                         this.showMode(1);
21352                         break;
21353                     case 'prev':
21354                     case 'next':
21355                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21356                         switch(this.viewMode){
21357                                 case 0:
21358                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21359                                         break;
21360                                 case 1:
21361                                 case 2:
21362                                         this.viewDate = this.moveYear(this.viewDate, dir);
21363                                         break;
21364                         }
21365                         this.fill();
21366                         break;
21367                     case 'today':
21368                         var date = new Date();
21369                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21370 //                        this.fill()
21371                         this.setValue(this.formatDate(this.date));
21372                         
21373                         this.hidePopup();
21374                         break;
21375                 }
21376                 break;
21377             case 'span':
21378                 if (className.indexOf('disabled') < 0) {
21379                     this.viewDate.setUTCDate(1);
21380                     if (className.indexOf('month') > -1) {
21381                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21382                     } else {
21383                         var year = parseInt(html, 10) || 0;
21384                         this.viewDate.setUTCFullYear(year);
21385                         
21386                     }
21387                     
21388                     if(this.singleMode){
21389                         this.setValue(this.formatDate(this.viewDate));
21390                         this.hidePopup();
21391                         return;
21392                     }
21393                     
21394                     this.showMode(-1);
21395                     this.fill();
21396                 }
21397                 break;
21398                 
21399             case 'td':
21400                 //Roo.log(className);
21401                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21402                     var day = parseInt(html, 10) || 1;
21403                     var year = this.viewDate.getUTCFullYear(),
21404                         month = this.viewDate.getUTCMonth();
21405
21406                     if (className.indexOf('old') > -1) {
21407                         if(month === 0 ){
21408                             month = 11;
21409                             year -= 1;
21410                         }else{
21411                             month -= 1;
21412                         }
21413                     } else if (className.indexOf('new') > -1) {
21414                         if (month == 11) {
21415                             month = 0;
21416                             year += 1;
21417                         } else {
21418                             month += 1;
21419                         }
21420                     }
21421                     //Roo.log([year,month,day]);
21422                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21423                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21424 //                    this.fill();
21425                     //Roo.log(this.formatDate(this.date));
21426                     this.setValue(this.formatDate(this.date));
21427                     this.hidePopup();
21428                 }
21429                 break;
21430         }
21431     },
21432     
21433     setStartDate: function(startDate)
21434     {
21435         this.startDate = startDate || -Infinity;
21436         if (this.startDate !== -Infinity) {
21437             this.startDate = this.parseDate(this.startDate);
21438         }
21439         this.update();
21440         this.updateNavArrows();
21441     },
21442
21443     setEndDate: function(endDate)
21444     {
21445         this.endDate = endDate || Infinity;
21446         if (this.endDate !== Infinity) {
21447             this.endDate = this.parseDate(this.endDate);
21448         }
21449         this.update();
21450         this.updateNavArrows();
21451     },
21452     
21453     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21454     {
21455         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21456         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21457             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21458         }
21459         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21460             return parseInt(d, 10);
21461         });
21462         this.update();
21463         this.updateNavArrows();
21464     },
21465     
21466     updateNavArrows: function() 
21467     {
21468         if(this.singleMode){
21469             return;
21470         }
21471         
21472         var d = new Date(this.viewDate),
21473         year = d.getUTCFullYear(),
21474         month = d.getUTCMonth();
21475         
21476         Roo.each(this.picker().select('.prev', true).elements, function(v){
21477             v.show();
21478             switch (this.viewMode) {
21479                 case 0:
21480
21481                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21482                         v.hide();
21483                     }
21484                     break;
21485                 case 1:
21486                 case 2:
21487                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21488                         v.hide();
21489                     }
21490                     break;
21491             }
21492         });
21493         
21494         Roo.each(this.picker().select('.next', true).elements, function(v){
21495             v.show();
21496             switch (this.viewMode) {
21497                 case 0:
21498
21499                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21500                         v.hide();
21501                     }
21502                     break;
21503                 case 1:
21504                 case 2:
21505                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21506                         v.hide();
21507                     }
21508                     break;
21509             }
21510         })
21511     },
21512     
21513     moveMonth: function(date, dir)
21514     {
21515         if (!dir) {
21516             return date;
21517         }
21518         var new_date = new Date(date.valueOf()),
21519         day = new_date.getUTCDate(),
21520         month = new_date.getUTCMonth(),
21521         mag = Math.abs(dir),
21522         new_month, test;
21523         dir = dir > 0 ? 1 : -1;
21524         if (mag == 1){
21525             test = dir == -1
21526             // If going back one month, make sure month is not current month
21527             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21528             ? function(){
21529                 return new_date.getUTCMonth() == month;
21530             }
21531             // If going forward one month, make sure month is as expected
21532             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21533             : function(){
21534                 return new_date.getUTCMonth() != new_month;
21535             };
21536             new_month = month + dir;
21537             new_date.setUTCMonth(new_month);
21538             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21539             if (new_month < 0 || new_month > 11) {
21540                 new_month = (new_month + 12) % 12;
21541             }
21542         } else {
21543             // For magnitudes >1, move one month at a time...
21544             for (var i=0; i<mag; i++) {
21545                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21546                 new_date = this.moveMonth(new_date, dir);
21547             }
21548             // ...then reset the day, keeping it in the new month
21549             new_month = new_date.getUTCMonth();
21550             new_date.setUTCDate(day);
21551             test = function(){
21552                 return new_month != new_date.getUTCMonth();
21553             };
21554         }
21555         // Common date-resetting loop -- if date is beyond end of month, make it
21556         // end of month
21557         while (test()){
21558             new_date.setUTCDate(--day);
21559             new_date.setUTCMonth(new_month);
21560         }
21561         return new_date;
21562     },
21563
21564     moveYear: function(date, dir)
21565     {
21566         return this.moveMonth(date, dir*12);
21567     },
21568
21569     dateWithinRange: function(date)
21570     {
21571         return date >= this.startDate && date <= this.endDate;
21572     },
21573
21574     
21575     remove: function() 
21576     {
21577         this.picker().remove();
21578     },
21579     
21580     validateValue : function(value)
21581     {
21582         if(this.getVisibilityEl().hasClass('hidden')){
21583             return true;
21584         }
21585         
21586         if(value.length < 1)  {
21587             if(this.allowBlank){
21588                 return true;
21589             }
21590             return false;
21591         }
21592         
21593         if(value.length < this.minLength){
21594             return false;
21595         }
21596         if(value.length > this.maxLength){
21597             return false;
21598         }
21599         if(this.vtype){
21600             var vt = Roo.form.VTypes;
21601             if(!vt[this.vtype](value, this)){
21602                 return false;
21603             }
21604         }
21605         if(typeof this.validator == "function"){
21606             var msg = this.validator(value);
21607             if(msg !== true){
21608                 return false;
21609             }
21610         }
21611         
21612         if(this.regex && !this.regex.test(value)){
21613             return false;
21614         }
21615         
21616         if(typeof(this.parseDate(value)) == 'undefined'){
21617             return false;
21618         }
21619         
21620         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21621             return false;
21622         }      
21623         
21624         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21625             return false;
21626         } 
21627         
21628         
21629         return true;
21630     },
21631     
21632     reset : function()
21633     {
21634         this.date = this.viewDate = '';
21635         
21636         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21637     }
21638    
21639 });
21640
21641 Roo.apply(Roo.bootstrap.DateField,  {
21642     
21643     head : {
21644         tag: 'thead',
21645         cn: [
21646         {
21647             tag: 'tr',
21648             cn: [
21649             {
21650                 tag: 'th',
21651                 cls: 'prev',
21652                 html: '<i class="fa fa-arrow-left"/>'
21653             },
21654             {
21655                 tag: 'th',
21656                 cls: 'switch',
21657                 colspan: '5'
21658             },
21659             {
21660                 tag: 'th',
21661                 cls: 'next',
21662                 html: '<i class="fa fa-arrow-right"/>'
21663             }
21664
21665             ]
21666         }
21667         ]
21668     },
21669     
21670     content : {
21671         tag: 'tbody',
21672         cn: [
21673         {
21674             tag: 'tr',
21675             cn: [
21676             {
21677                 tag: 'td',
21678                 colspan: '7'
21679             }
21680             ]
21681         }
21682         ]
21683     },
21684     
21685     footer : {
21686         tag: 'tfoot',
21687         cn: [
21688         {
21689             tag: 'tr',
21690             cn: [
21691             {
21692                 tag: 'th',
21693                 colspan: '7',
21694                 cls: 'today'
21695             }
21696                     
21697             ]
21698         }
21699         ]
21700     },
21701     
21702     dates:{
21703         en: {
21704             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21705             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21706             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21707             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21708             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21709             today: "Today"
21710         }
21711     },
21712     
21713     modes: [
21714     {
21715         clsName: 'days',
21716         navFnc: 'Month',
21717         navStep: 1
21718     },
21719     {
21720         clsName: 'months',
21721         navFnc: 'FullYear',
21722         navStep: 1
21723     },
21724     {
21725         clsName: 'years',
21726         navFnc: 'FullYear',
21727         navStep: 10
21728     }]
21729 });
21730
21731 Roo.apply(Roo.bootstrap.DateField,  {
21732   
21733     template : {
21734         tag: 'div',
21735         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21736         cn: [
21737         {
21738             tag: 'div',
21739             cls: 'datepicker-days',
21740             cn: [
21741             {
21742                 tag: 'table',
21743                 cls: 'table-condensed',
21744                 cn:[
21745                 Roo.bootstrap.DateField.head,
21746                 {
21747                     tag: 'tbody'
21748                 },
21749                 Roo.bootstrap.DateField.footer
21750                 ]
21751             }
21752             ]
21753         },
21754         {
21755             tag: 'div',
21756             cls: 'datepicker-months',
21757             cn: [
21758             {
21759                 tag: 'table',
21760                 cls: 'table-condensed',
21761                 cn:[
21762                 Roo.bootstrap.DateField.head,
21763                 Roo.bootstrap.DateField.content,
21764                 Roo.bootstrap.DateField.footer
21765                 ]
21766             }
21767             ]
21768         },
21769         {
21770             tag: 'div',
21771             cls: 'datepicker-years',
21772             cn: [
21773             {
21774                 tag: 'table',
21775                 cls: 'table-condensed',
21776                 cn:[
21777                 Roo.bootstrap.DateField.head,
21778                 Roo.bootstrap.DateField.content,
21779                 Roo.bootstrap.DateField.footer
21780                 ]
21781             }
21782             ]
21783         }
21784         ]
21785     }
21786 });
21787
21788  
21789
21790  /*
21791  * - LGPL
21792  *
21793  * TimeField
21794  * 
21795  */
21796
21797 /**
21798  * @class Roo.bootstrap.TimeField
21799  * @extends Roo.bootstrap.Input
21800  * Bootstrap DateField class
21801  * 
21802  * 
21803  * @constructor
21804  * Create a new TimeField
21805  * @param {Object} config The config object
21806  */
21807
21808 Roo.bootstrap.TimeField = function(config){
21809     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21810     this.addEvents({
21811             /**
21812              * @event show
21813              * Fires when this field show.
21814              * @param {Roo.bootstrap.DateField} thisthis
21815              * @param {Mixed} date The date value
21816              */
21817             show : true,
21818             /**
21819              * @event show
21820              * Fires when this field hide.
21821              * @param {Roo.bootstrap.DateField} this
21822              * @param {Mixed} date The date value
21823              */
21824             hide : true,
21825             /**
21826              * @event select
21827              * Fires when select a date.
21828              * @param {Roo.bootstrap.DateField} this
21829              * @param {Mixed} date The date value
21830              */
21831             select : true
21832         });
21833 };
21834
21835 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21836     
21837     /**
21838      * @cfg {String} format
21839      * The default time format string which can be overriden for localization support.  The format must be
21840      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21841      */
21842     format : "H:i",
21843        
21844     onRender: function(ct, position)
21845     {
21846         
21847         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21848                 
21849         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21850         
21851         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21852         
21853         this.pop = this.picker().select('>.datepicker-time',true).first();
21854         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21855         
21856         this.picker().on('mousedown', this.onMousedown, this);
21857         this.picker().on('click', this.onClick, this);
21858         
21859         this.picker().addClass('datepicker-dropdown');
21860     
21861         this.fillTime();
21862         this.update();
21863             
21864         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21865         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21866         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21867         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21868         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21869         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21870
21871     },
21872     
21873     fireKey: function(e){
21874         if (!this.picker().isVisible()){
21875             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21876                 this.show();
21877             }
21878             return;
21879         }
21880
21881         e.preventDefault();
21882         
21883         switch(e.keyCode){
21884             case 27: // escape
21885                 this.hide();
21886                 break;
21887             case 37: // left
21888             case 39: // right
21889                 this.onTogglePeriod();
21890                 break;
21891             case 38: // up
21892                 this.onIncrementMinutes();
21893                 break;
21894             case 40: // down
21895                 this.onDecrementMinutes();
21896                 break;
21897             case 13: // enter
21898             case 9: // tab
21899                 this.setTime();
21900                 break;
21901         }
21902     },
21903     
21904     onClick: function(e) {
21905         e.stopPropagation();
21906         e.preventDefault();
21907     },
21908     
21909     picker : function()
21910     {
21911         return this.el.select('.datepicker', true).first();
21912     },
21913     
21914     fillTime: function()
21915     {    
21916         var time = this.pop.select('tbody', true).first();
21917         
21918         time.dom.innerHTML = '';
21919         
21920         time.createChild({
21921             tag: 'tr',
21922             cn: [
21923                 {
21924                     tag: 'td',
21925                     cn: [
21926                         {
21927                             tag: 'a',
21928                             href: '#',
21929                             cls: 'btn',
21930                             cn: [
21931                                 {
21932                                     tag: 'span',
21933                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21934                                 }
21935                             ]
21936                         } 
21937                     ]
21938                 },
21939                 {
21940                     tag: 'td',
21941                     cls: 'separator'
21942                 },
21943                 {
21944                     tag: 'td',
21945                     cn: [
21946                         {
21947                             tag: 'a',
21948                             href: '#',
21949                             cls: 'btn',
21950                             cn: [
21951                                 {
21952                                     tag: 'span',
21953                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21954                                 }
21955                             ]
21956                         }
21957                     ]
21958                 },
21959                 {
21960                     tag: 'td',
21961                     cls: 'separator'
21962                 }
21963             ]
21964         });
21965         
21966         time.createChild({
21967             tag: 'tr',
21968             cn: [
21969                 {
21970                     tag: 'td',
21971                     cn: [
21972                         {
21973                             tag: 'span',
21974                             cls: 'timepicker-hour',
21975                             html: '00'
21976                         }  
21977                     ]
21978                 },
21979                 {
21980                     tag: 'td',
21981                     cls: 'separator',
21982                     html: ':'
21983                 },
21984                 {
21985                     tag: 'td',
21986                     cn: [
21987                         {
21988                             tag: 'span',
21989                             cls: 'timepicker-minute',
21990                             html: '00'
21991                         }  
21992                     ]
21993                 },
21994                 {
21995                     tag: 'td',
21996                     cls: 'separator'
21997                 },
21998                 {
21999                     tag: 'td',
22000                     cn: [
22001                         {
22002                             tag: 'button',
22003                             type: 'button',
22004                             cls: 'btn btn-primary period',
22005                             html: 'AM'
22006                             
22007                         }
22008                     ]
22009                 }
22010             ]
22011         });
22012         
22013         time.createChild({
22014             tag: 'tr',
22015             cn: [
22016                 {
22017                     tag: 'td',
22018                     cn: [
22019                         {
22020                             tag: 'a',
22021                             href: '#',
22022                             cls: 'btn',
22023                             cn: [
22024                                 {
22025                                     tag: 'span',
22026                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22027                                 }
22028                             ]
22029                         }
22030                     ]
22031                 },
22032                 {
22033                     tag: 'td',
22034                     cls: 'separator'
22035                 },
22036                 {
22037                     tag: 'td',
22038                     cn: [
22039                         {
22040                             tag: 'a',
22041                             href: '#',
22042                             cls: 'btn',
22043                             cn: [
22044                                 {
22045                                     tag: 'span',
22046                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22047                                 }
22048                             ]
22049                         }
22050                     ]
22051                 },
22052                 {
22053                     tag: 'td',
22054                     cls: 'separator'
22055                 }
22056             ]
22057         });
22058         
22059     },
22060     
22061     update: function()
22062     {
22063         
22064         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22065         
22066         this.fill();
22067     },
22068     
22069     fill: function() 
22070     {
22071         var hours = this.time.getHours();
22072         var minutes = this.time.getMinutes();
22073         var period = 'AM';
22074         
22075         if(hours > 11){
22076             period = 'PM';
22077         }
22078         
22079         if(hours == 0){
22080             hours = 12;
22081         }
22082         
22083         
22084         if(hours > 12){
22085             hours = hours - 12;
22086         }
22087         
22088         if(hours < 10){
22089             hours = '0' + hours;
22090         }
22091         
22092         if(minutes < 10){
22093             minutes = '0' + minutes;
22094         }
22095         
22096         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22097         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22098         this.pop.select('button', true).first().dom.innerHTML = period;
22099         
22100     },
22101     
22102     place: function()
22103     {   
22104         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22105         
22106         var cls = ['bottom'];
22107         
22108         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22109             cls.pop();
22110             cls.push('top');
22111         }
22112         
22113         cls.push('right');
22114         
22115         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22116             cls.pop();
22117             cls.push('left');
22118         }
22119         
22120         this.picker().addClass(cls.join('-'));
22121         
22122         var _this = this;
22123         
22124         Roo.each(cls, function(c){
22125             if(c == 'bottom'){
22126                 _this.picker().setTop(_this.inputEl().getHeight());
22127                 return;
22128             }
22129             if(c == 'top'){
22130                 _this.picker().setTop(0 - _this.picker().getHeight());
22131                 return;
22132             }
22133             
22134             if(c == 'left'){
22135                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22136                 return;
22137             }
22138             if(c == 'right'){
22139                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22140                 return;
22141             }
22142         });
22143         
22144     },
22145   
22146     onFocus : function()
22147     {
22148         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22149         this.show();
22150     },
22151     
22152     onBlur : function()
22153     {
22154         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22155         this.hide();
22156     },
22157     
22158     show : function()
22159     {
22160         this.picker().show();
22161         this.pop.show();
22162         this.update();
22163         this.place();
22164         
22165         this.fireEvent('show', this, this.date);
22166     },
22167     
22168     hide : function()
22169     {
22170         this.picker().hide();
22171         this.pop.hide();
22172         
22173         this.fireEvent('hide', this, this.date);
22174     },
22175     
22176     setTime : function()
22177     {
22178         this.hide();
22179         this.setValue(this.time.format(this.format));
22180         
22181         this.fireEvent('select', this, this.date);
22182         
22183         
22184     },
22185     
22186     onMousedown: function(e){
22187         e.stopPropagation();
22188         e.preventDefault();
22189     },
22190     
22191     onIncrementHours: function()
22192     {
22193         Roo.log('onIncrementHours');
22194         this.time = this.time.add(Date.HOUR, 1);
22195         this.update();
22196         
22197     },
22198     
22199     onDecrementHours: function()
22200     {
22201         Roo.log('onDecrementHours');
22202         this.time = this.time.add(Date.HOUR, -1);
22203         this.update();
22204     },
22205     
22206     onIncrementMinutes: function()
22207     {
22208         Roo.log('onIncrementMinutes');
22209         this.time = this.time.add(Date.MINUTE, 1);
22210         this.update();
22211     },
22212     
22213     onDecrementMinutes: function()
22214     {
22215         Roo.log('onDecrementMinutes');
22216         this.time = this.time.add(Date.MINUTE, -1);
22217         this.update();
22218     },
22219     
22220     onTogglePeriod: function()
22221     {
22222         Roo.log('onTogglePeriod');
22223         this.time = this.time.add(Date.HOUR, 12);
22224         this.update();
22225     }
22226     
22227    
22228 });
22229
22230 Roo.apply(Roo.bootstrap.TimeField,  {
22231     
22232     content : {
22233         tag: 'tbody',
22234         cn: [
22235             {
22236                 tag: 'tr',
22237                 cn: [
22238                 {
22239                     tag: 'td',
22240                     colspan: '7'
22241                 }
22242                 ]
22243             }
22244         ]
22245     },
22246     
22247     footer : {
22248         tag: 'tfoot',
22249         cn: [
22250             {
22251                 tag: 'tr',
22252                 cn: [
22253                 {
22254                     tag: 'th',
22255                     colspan: '7',
22256                     cls: '',
22257                     cn: [
22258                         {
22259                             tag: 'button',
22260                             cls: 'btn btn-info ok',
22261                             html: 'OK'
22262                         }
22263                     ]
22264                 }
22265
22266                 ]
22267             }
22268         ]
22269     }
22270 });
22271
22272 Roo.apply(Roo.bootstrap.TimeField,  {
22273   
22274     template : {
22275         tag: 'div',
22276         cls: 'datepicker dropdown-menu',
22277         cn: [
22278             {
22279                 tag: 'div',
22280                 cls: 'datepicker-time',
22281                 cn: [
22282                 {
22283                     tag: 'table',
22284                     cls: 'table-condensed',
22285                     cn:[
22286                     Roo.bootstrap.TimeField.content,
22287                     Roo.bootstrap.TimeField.footer
22288                     ]
22289                 }
22290                 ]
22291             }
22292         ]
22293     }
22294 });
22295
22296  
22297
22298  /*
22299  * - LGPL
22300  *
22301  * MonthField
22302  * 
22303  */
22304
22305 /**
22306  * @class Roo.bootstrap.MonthField
22307  * @extends Roo.bootstrap.Input
22308  * Bootstrap MonthField class
22309  * 
22310  * @cfg {String} language default en
22311  * 
22312  * @constructor
22313  * Create a new MonthField
22314  * @param {Object} config The config object
22315  */
22316
22317 Roo.bootstrap.MonthField = function(config){
22318     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22319     
22320     this.addEvents({
22321         /**
22322          * @event show
22323          * Fires when this field show.
22324          * @param {Roo.bootstrap.MonthField} this
22325          * @param {Mixed} date The date value
22326          */
22327         show : true,
22328         /**
22329          * @event show
22330          * Fires when this field hide.
22331          * @param {Roo.bootstrap.MonthField} this
22332          * @param {Mixed} date The date value
22333          */
22334         hide : true,
22335         /**
22336          * @event select
22337          * Fires when select a date.
22338          * @param {Roo.bootstrap.MonthField} this
22339          * @param {String} oldvalue The old value
22340          * @param {String} newvalue The new value
22341          */
22342         select : true
22343     });
22344 };
22345
22346 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22347     
22348     onRender: function(ct, position)
22349     {
22350         
22351         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22352         
22353         this.language = this.language || 'en';
22354         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22355         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22356         
22357         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22358         this.isInline = false;
22359         this.isInput = true;
22360         this.component = this.el.select('.add-on', true).first() || false;
22361         this.component = (this.component && this.component.length === 0) ? false : this.component;
22362         this.hasInput = this.component && this.inputEL().length;
22363         
22364         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22365         
22366         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22367         
22368         this.picker().on('mousedown', this.onMousedown, this);
22369         this.picker().on('click', this.onClick, this);
22370         
22371         this.picker().addClass('datepicker-dropdown');
22372         
22373         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22374             v.setStyle('width', '189px');
22375         });
22376         
22377         this.fillMonths();
22378         
22379         this.update();
22380         
22381         if(this.isInline) {
22382             this.show();
22383         }
22384         
22385     },
22386     
22387     setValue: function(v, suppressEvent)
22388     {   
22389         var o = this.getValue();
22390         
22391         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22392         
22393         this.update();
22394
22395         if(suppressEvent !== true){
22396             this.fireEvent('select', this, o, v);
22397         }
22398         
22399     },
22400     
22401     getValue: function()
22402     {
22403         return this.value;
22404     },
22405     
22406     onClick: function(e) 
22407     {
22408         e.stopPropagation();
22409         e.preventDefault();
22410         
22411         var target = e.getTarget();
22412         
22413         if(target.nodeName.toLowerCase() === 'i'){
22414             target = Roo.get(target).dom.parentNode;
22415         }
22416         
22417         var nodeName = target.nodeName;
22418         var className = target.className;
22419         var html = target.innerHTML;
22420         
22421         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22422             return;
22423         }
22424         
22425         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22426         
22427         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22428         
22429         this.hide();
22430                         
22431     },
22432     
22433     picker : function()
22434     {
22435         return this.pickerEl;
22436     },
22437     
22438     fillMonths: function()
22439     {    
22440         var i = 0;
22441         var months = this.picker().select('>.datepicker-months td', true).first();
22442         
22443         months.dom.innerHTML = '';
22444         
22445         while (i < 12) {
22446             var month = {
22447                 tag: 'span',
22448                 cls: 'month',
22449                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22450             };
22451             
22452             months.createChild(month);
22453         }
22454         
22455     },
22456     
22457     update: function()
22458     {
22459         var _this = this;
22460         
22461         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22462             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22463         }
22464         
22465         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22466             e.removeClass('active');
22467             
22468             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22469                 e.addClass('active');
22470             }
22471         })
22472     },
22473     
22474     place: function()
22475     {
22476         if(this.isInline) {
22477             return;
22478         }
22479         
22480         this.picker().removeClass(['bottom', 'top']);
22481         
22482         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22483             /*
22484              * place to the top of element!
22485              *
22486              */
22487             
22488             this.picker().addClass('top');
22489             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22490             
22491             return;
22492         }
22493         
22494         this.picker().addClass('bottom');
22495         
22496         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22497     },
22498     
22499     onFocus : function()
22500     {
22501         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22502         this.show();
22503     },
22504     
22505     onBlur : function()
22506     {
22507         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22508         
22509         var d = this.inputEl().getValue();
22510         
22511         this.setValue(d);
22512                 
22513         this.hide();
22514     },
22515     
22516     show : function()
22517     {
22518         this.picker().show();
22519         this.picker().select('>.datepicker-months', true).first().show();
22520         this.update();
22521         this.place();
22522         
22523         this.fireEvent('show', this, this.date);
22524     },
22525     
22526     hide : function()
22527     {
22528         if(this.isInline) {
22529             return;
22530         }
22531         this.picker().hide();
22532         this.fireEvent('hide', this, this.date);
22533         
22534     },
22535     
22536     onMousedown: function(e)
22537     {
22538         e.stopPropagation();
22539         e.preventDefault();
22540     },
22541     
22542     keyup: function(e)
22543     {
22544         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22545         this.update();
22546     },
22547
22548     fireKey: function(e)
22549     {
22550         if (!this.picker().isVisible()){
22551             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22552                 this.show();
22553             }
22554             return;
22555         }
22556         
22557         var dir;
22558         
22559         switch(e.keyCode){
22560             case 27: // escape
22561                 this.hide();
22562                 e.preventDefault();
22563                 break;
22564             case 37: // left
22565             case 39: // right
22566                 dir = e.keyCode == 37 ? -1 : 1;
22567                 
22568                 this.vIndex = this.vIndex + dir;
22569                 
22570                 if(this.vIndex < 0){
22571                     this.vIndex = 0;
22572                 }
22573                 
22574                 if(this.vIndex > 11){
22575                     this.vIndex = 11;
22576                 }
22577                 
22578                 if(isNaN(this.vIndex)){
22579                     this.vIndex = 0;
22580                 }
22581                 
22582                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22583                 
22584                 break;
22585             case 38: // up
22586             case 40: // down
22587                 
22588                 dir = e.keyCode == 38 ? -1 : 1;
22589                 
22590                 this.vIndex = this.vIndex + dir * 4;
22591                 
22592                 if(this.vIndex < 0){
22593                     this.vIndex = 0;
22594                 }
22595                 
22596                 if(this.vIndex > 11){
22597                     this.vIndex = 11;
22598                 }
22599                 
22600                 if(isNaN(this.vIndex)){
22601                     this.vIndex = 0;
22602                 }
22603                 
22604                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22605                 break;
22606                 
22607             case 13: // enter
22608                 
22609                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22610                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22611                 }
22612                 
22613                 this.hide();
22614                 e.preventDefault();
22615                 break;
22616             case 9: // tab
22617                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22618                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22619                 }
22620                 this.hide();
22621                 break;
22622             case 16: // shift
22623             case 17: // ctrl
22624             case 18: // alt
22625                 break;
22626             default :
22627                 this.hide();
22628                 
22629         }
22630     },
22631     
22632     remove: function() 
22633     {
22634         this.picker().remove();
22635     }
22636    
22637 });
22638
22639 Roo.apply(Roo.bootstrap.MonthField,  {
22640     
22641     content : {
22642         tag: 'tbody',
22643         cn: [
22644         {
22645             tag: 'tr',
22646             cn: [
22647             {
22648                 tag: 'td',
22649                 colspan: '7'
22650             }
22651             ]
22652         }
22653         ]
22654     },
22655     
22656     dates:{
22657         en: {
22658             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22659             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22660         }
22661     }
22662 });
22663
22664 Roo.apply(Roo.bootstrap.MonthField,  {
22665   
22666     template : {
22667         tag: 'div',
22668         cls: 'datepicker dropdown-menu roo-dynamic',
22669         cn: [
22670             {
22671                 tag: 'div',
22672                 cls: 'datepicker-months',
22673                 cn: [
22674                 {
22675                     tag: 'table',
22676                     cls: 'table-condensed',
22677                     cn:[
22678                         Roo.bootstrap.DateField.content
22679                     ]
22680                 }
22681                 ]
22682             }
22683         ]
22684     }
22685 });
22686
22687  
22688
22689  
22690  /*
22691  * - LGPL
22692  *
22693  * CheckBox
22694  * 
22695  */
22696
22697 /**
22698  * @class Roo.bootstrap.CheckBox
22699  * @extends Roo.bootstrap.Input
22700  * Bootstrap CheckBox class
22701  * 
22702  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22703  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22704  * @cfg {String} boxLabel The text that appears beside the checkbox
22705  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22706  * @cfg {Boolean} checked initnal the element
22707  * @cfg {Boolean} inline inline the element (default false)
22708  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22709  * @cfg {String} tooltip label tooltip
22710  * 
22711  * @constructor
22712  * Create a new CheckBox
22713  * @param {Object} config The config object
22714  */
22715
22716 Roo.bootstrap.CheckBox = function(config){
22717     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22718    
22719     this.addEvents({
22720         /**
22721         * @event check
22722         * Fires when the element is checked or unchecked.
22723         * @param {Roo.bootstrap.CheckBox} this This input
22724         * @param {Boolean} checked The new checked value
22725         */
22726        check : true,
22727        /**
22728         * @event click
22729         * Fires when the element is click.
22730         * @param {Roo.bootstrap.CheckBox} this This input
22731         */
22732        click : true
22733     });
22734     
22735 };
22736
22737 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22738   
22739     inputType: 'checkbox',
22740     inputValue: 1,
22741     valueOff: 0,
22742     boxLabel: false,
22743     checked: false,
22744     weight : false,
22745     inline: false,
22746     tooltip : '',
22747     
22748     // checkbox success does not make any sense really.. 
22749     invalidClass : "",
22750     validClass : "",
22751     
22752     
22753     getAutoCreate : function()
22754     {
22755         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22756         
22757         var id = Roo.id();
22758         
22759         var cfg = {};
22760         
22761         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22762         
22763         if(this.inline){
22764             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22765         }
22766         
22767         var input =  {
22768             tag: 'input',
22769             id : id,
22770             type : this.inputType,
22771             value : this.inputValue,
22772             cls : 'roo-' + this.inputType, //'form-box',
22773             placeholder : this.placeholder || ''
22774             
22775         };
22776         
22777         if(this.inputType != 'radio'){
22778             var hidden =  {
22779                 tag: 'input',
22780                 type : 'hidden',
22781                 cls : 'roo-hidden-value',
22782                 value : this.checked ? this.inputValue : this.valueOff
22783             };
22784         }
22785         
22786             
22787         if (this.weight) { // Validity check?
22788             cfg.cls += " " + this.inputType + "-" + this.weight;
22789         }
22790         
22791         if (this.disabled) {
22792             input.disabled=true;
22793         }
22794         
22795         if(this.checked){
22796             input.checked = this.checked;
22797         }
22798         
22799         if (this.name) {
22800             
22801             input.name = this.name;
22802             
22803             if(this.inputType != 'radio'){
22804                 hidden.name = this.name;
22805                 input.name = '_hidden_' + this.name;
22806             }
22807         }
22808         
22809         if (this.size) {
22810             input.cls += ' input-' + this.size;
22811         }
22812         
22813         var settings=this;
22814         
22815         ['xs','sm','md','lg'].map(function(size){
22816             if (settings[size]) {
22817                 cfg.cls += ' col-' + size + '-' + settings[size];
22818             }
22819         });
22820         
22821         var inputblock = input;
22822          
22823         if (this.before || this.after) {
22824             
22825             inputblock = {
22826                 cls : 'input-group',
22827                 cn :  [] 
22828             };
22829             
22830             if (this.before) {
22831                 inputblock.cn.push({
22832                     tag :'span',
22833                     cls : 'input-group-addon',
22834                     html : this.before
22835                 });
22836             }
22837             
22838             inputblock.cn.push(input);
22839             
22840             if(this.inputType != 'radio'){
22841                 inputblock.cn.push(hidden);
22842             }
22843             
22844             if (this.after) {
22845                 inputblock.cn.push({
22846                     tag :'span',
22847                     cls : 'input-group-addon',
22848                     html : this.after
22849                 });
22850             }
22851             
22852         }
22853         var boxLabelCfg = false;
22854         
22855         if(this.boxLabel){
22856            
22857             boxLabelCfg = {
22858                 tag: 'label',
22859                 //'for': id, // box label is handled by onclick - so no for...
22860                 cls: 'box-label',
22861                 html: this.boxLabel
22862             };
22863             if(this.tooltip){
22864                 boxLabelCfg.tooltip = this.tooltip;
22865             }
22866              
22867         }
22868         
22869         
22870         if (align ==='left' && this.fieldLabel.length) {
22871 //                Roo.log("left and has label");
22872             cfg.cn = [
22873                 {
22874                     tag: 'label',
22875                     'for' :  id,
22876                     cls : 'control-label',
22877                     html : this.fieldLabel
22878                 },
22879                 {
22880                     cls : "", 
22881                     cn: [
22882                         inputblock
22883                     ]
22884                 }
22885             ];
22886             
22887             if (boxLabelCfg) {
22888                 cfg.cn[1].cn.push(boxLabelCfg);
22889             }
22890             
22891             if(this.labelWidth > 12){
22892                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22893             }
22894             
22895             if(this.labelWidth < 13 && this.labelmd == 0){
22896                 this.labelmd = this.labelWidth;
22897             }
22898             
22899             if(this.labellg > 0){
22900                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22901                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22902             }
22903             
22904             if(this.labelmd > 0){
22905                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22906                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22907             }
22908             
22909             if(this.labelsm > 0){
22910                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22911                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22912             }
22913             
22914             if(this.labelxs > 0){
22915                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22916                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22917             }
22918             
22919         } else if ( this.fieldLabel.length) {
22920 //                Roo.log(" label");
22921                 cfg.cn = [
22922                    
22923                     {
22924                         tag: this.boxLabel ? 'span' : 'label',
22925                         'for': id,
22926                         cls: 'control-label box-input-label',
22927                         //cls : 'input-group-addon',
22928                         html : this.fieldLabel
22929                     },
22930                     
22931                     inputblock
22932                     
22933                 ];
22934                 if (boxLabelCfg) {
22935                     cfg.cn.push(boxLabelCfg);
22936                 }
22937
22938         } else {
22939             
22940 //                Roo.log(" no label && no align");
22941                 cfg.cn = [  inputblock ] ;
22942                 if (boxLabelCfg) {
22943                     cfg.cn.push(boxLabelCfg);
22944                 }
22945
22946                 
22947         }
22948         
22949        
22950         
22951         if(this.inputType != 'radio'){
22952             cfg.cn.push(hidden);
22953         }
22954         
22955         return cfg;
22956         
22957     },
22958     
22959     /**
22960      * return the real input element.
22961      */
22962     inputEl: function ()
22963     {
22964         return this.el.select('input.roo-' + this.inputType,true).first();
22965     },
22966     hiddenEl: function ()
22967     {
22968         return this.el.select('input.roo-hidden-value',true).first();
22969     },
22970     
22971     labelEl: function()
22972     {
22973         return this.el.select('label.control-label',true).first();
22974     },
22975     /* depricated... */
22976     
22977     label: function()
22978     {
22979         return this.labelEl();
22980     },
22981     
22982     boxLabelEl: function()
22983     {
22984         return this.el.select('label.box-label',true).first();
22985     },
22986     
22987     initEvents : function()
22988     {
22989 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22990         
22991         this.inputEl().on('click', this.onClick,  this);
22992         
22993         if (this.boxLabel) { 
22994             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22995         }
22996         
22997         this.startValue = this.getValue();
22998         
22999         if(this.groupId){
23000             Roo.bootstrap.CheckBox.register(this);
23001         }
23002     },
23003     
23004     onClick : function(e)
23005     {   
23006         if(this.fireEvent('click', this, e) !== false){
23007             this.setChecked(!this.checked);
23008         }
23009         
23010     },
23011     
23012     setChecked : function(state,suppressEvent)
23013     {
23014         this.startValue = this.getValue();
23015
23016         if(this.inputType == 'radio'){
23017             
23018             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23019                 e.dom.checked = false;
23020             });
23021             
23022             this.inputEl().dom.checked = true;
23023             
23024             this.inputEl().dom.value = this.inputValue;
23025             
23026             if(suppressEvent !== true){
23027                 this.fireEvent('check', this, true);
23028             }
23029             
23030             this.validate();
23031             
23032             return;
23033         }
23034         
23035         this.checked = state;
23036         
23037         this.inputEl().dom.checked = state;
23038         
23039         
23040         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23041         
23042         if(suppressEvent !== true){
23043             this.fireEvent('check', this, state);
23044         }
23045         
23046         this.validate();
23047     },
23048     
23049     getValue : function()
23050     {
23051         if(this.inputType == 'radio'){
23052             return this.getGroupValue();
23053         }
23054         
23055         return this.hiddenEl().dom.value;
23056         
23057     },
23058     
23059     getGroupValue : function()
23060     {
23061         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23062             return '';
23063         }
23064         
23065         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23066     },
23067     
23068     setValue : function(v,suppressEvent)
23069     {
23070         if(this.inputType == 'radio'){
23071             this.setGroupValue(v, suppressEvent);
23072             return;
23073         }
23074         
23075         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23076         
23077         this.validate();
23078     },
23079     
23080     setGroupValue : function(v, suppressEvent)
23081     {
23082         this.startValue = this.getValue();
23083         
23084         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23085             e.dom.checked = false;
23086             
23087             if(e.dom.value == v){
23088                 e.dom.checked = true;
23089             }
23090         });
23091         
23092         if(suppressEvent !== true){
23093             this.fireEvent('check', this, true);
23094         }
23095
23096         this.validate();
23097         
23098         return;
23099     },
23100     
23101     validate : function()
23102     {
23103         if(this.getVisibilityEl().hasClass('hidden')){
23104             return true;
23105         }
23106         
23107         if(
23108                 this.disabled || 
23109                 (this.inputType == 'radio' && this.validateRadio()) ||
23110                 (this.inputType == 'checkbox' && this.validateCheckbox())
23111         ){
23112             this.markValid();
23113             return true;
23114         }
23115         
23116         this.markInvalid();
23117         return false;
23118     },
23119     
23120     validateRadio : function()
23121     {
23122         if(this.getVisibilityEl().hasClass('hidden')){
23123             return true;
23124         }
23125         
23126         if(this.allowBlank){
23127             return true;
23128         }
23129         
23130         var valid = false;
23131         
23132         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23133             if(!e.dom.checked){
23134                 return;
23135             }
23136             
23137             valid = true;
23138             
23139             return false;
23140         });
23141         
23142         return valid;
23143     },
23144     
23145     validateCheckbox : function()
23146     {
23147         if(!this.groupId){
23148             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23149             //return (this.getValue() == this.inputValue) ? true : false;
23150         }
23151         
23152         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23153         
23154         if(!group){
23155             return false;
23156         }
23157         
23158         var r = false;
23159         
23160         for(var i in group){
23161             if(group[i].el.isVisible(true)){
23162                 r = false;
23163                 break;
23164             }
23165             
23166             r = true;
23167         }
23168         
23169         for(var i in group){
23170             if(r){
23171                 break;
23172             }
23173             
23174             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23175         }
23176         
23177         return r;
23178     },
23179     
23180     /**
23181      * Mark this field as valid
23182      */
23183     markValid : function()
23184     {
23185         var _this = this;
23186         
23187         this.fireEvent('valid', this);
23188         
23189         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23190         
23191         if(this.groupId){
23192             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23193         }
23194         
23195         if(label){
23196             label.markValid();
23197         }
23198
23199         if(this.inputType == 'radio'){
23200             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23201                 var fg = e.findParent('.form-group', false, true);
23202                 if (Roo.bootstrap.version == 3) {
23203                     fg.removeClass([_this.invalidClass, _this.validClass]);
23204                     fg.addClass(_this.validClass);
23205                 } else {
23206                     fg.removeClass(['is-valid', 'is-invalid']);
23207                     fg.addClass('is-valid');
23208                 }
23209             });
23210             
23211             return;
23212         }
23213
23214         if(!this.groupId){
23215             var fg = this.el.findParent('.form-group', false, true);
23216             if (Roo.bootstrap.version == 3) {
23217                 fg.removeClass([this.invalidClass, this.validClass]);
23218                 fg.addClass(this.validClass);
23219             } else {
23220                 fg.removeClass(['is-valid', 'is-invalid']);
23221                 fg.addClass('is-valid');
23222             }
23223             return;
23224         }
23225         
23226         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23227         
23228         if(!group){
23229             return;
23230         }
23231         
23232         for(var i in group){
23233             var fg = group[i].el.findParent('.form-group', false, true);
23234             if (Roo.bootstrap.version == 3) {
23235                 fg.removeClass([this.invalidClass, this.validClass]);
23236                 fg.addClass(this.validClass);
23237             } else {
23238                 fg.removeClass(['is-valid', 'is-invalid']);
23239                 fg.addClass('is-valid');
23240             }
23241         }
23242     },
23243     
23244      /**
23245      * Mark this field as invalid
23246      * @param {String} msg The validation message
23247      */
23248     markInvalid : function(msg)
23249     {
23250         if(this.allowBlank){
23251             return;
23252         }
23253         
23254         var _this = this;
23255         
23256         this.fireEvent('invalid', this, msg);
23257         
23258         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23259         
23260         if(this.groupId){
23261             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23262         }
23263         
23264         if(label){
23265             label.markInvalid();
23266         }
23267             
23268         if(this.inputType == 'radio'){
23269             
23270             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23271                 var fg = e.findParent('.form-group', false, true);
23272                 if (Roo.bootstrap.version == 3) {
23273                     fg.removeClass([_this.invalidClass, _this.validClass]);
23274                     fg.addClass(_this.invalidClass);
23275                 } else {
23276                     fg.removeClass(['is-invalid', 'is-valid']);
23277                     fg.addClass('is-invalid');
23278                 }
23279             });
23280             
23281             return;
23282         }
23283         
23284         if(!this.groupId){
23285             var fg = this.el.findParent('.form-group', false, true);
23286             if (Roo.bootstrap.version == 3) {
23287                 fg.removeClass([_this.invalidClass, _this.validClass]);
23288                 fg.addClass(_this.invalidClass);
23289             } else {
23290                 fg.removeClass(['is-invalid', 'is-valid']);
23291                 fg.addClass('is-invalid');
23292             }
23293             return;
23294         }
23295         
23296         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23297         
23298         if(!group){
23299             return;
23300         }
23301         
23302         for(var i in group){
23303             var fg = group[i].el.findParent('.form-group', false, true);
23304             if (Roo.bootstrap.version == 3) {
23305                 fg.removeClass([_this.invalidClass, _this.validClass]);
23306                 fg.addClass(_this.invalidClass);
23307             } else {
23308                 fg.removeClass(['is-invalid', 'is-valid']);
23309                 fg.addClass('is-invalid');
23310             }
23311         }
23312         
23313     },
23314     
23315     clearInvalid : function()
23316     {
23317         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23318         
23319         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23320         
23321         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23322         
23323         if (label && label.iconEl) {
23324             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23325             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23326         }
23327     },
23328     
23329     disable : function()
23330     {
23331         if(this.inputType != 'radio'){
23332             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23333             return;
23334         }
23335         
23336         var _this = this;
23337         
23338         if(this.rendered){
23339             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23340                 _this.getActionEl().addClass(this.disabledClass);
23341                 e.dom.disabled = true;
23342             });
23343         }
23344         
23345         this.disabled = true;
23346         this.fireEvent("disable", this);
23347         return this;
23348     },
23349
23350     enable : function()
23351     {
23352         if(this.inputType != 'radio'){
23353             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23354             return;
23355         }
23356         
23357         var _this = this;
23358         
23359         if(this.rendered){
23360             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23361                 _this.getActionEl().removeClass(this.disabledClass);
23362                 e.dom.disabled = false;
23363             });
23364         }
23365         
23366         this.disabled = false;
23367         this.fireEvent("enable", this);
23368         return this;
23369     },
23370     
23371     setBoxLabel : function(v)
23372     {
23373         this.boxLabel = v;
23374         
23375         if(this.rendered){
23376             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23377         }
23378     }
23379
23380 });
23381
23382 Roo.apply(Roo.bootstrap.CheckBox, {
23383     
23384     groups: {},
23385     
23386      /**
23387     * register a CheckBox Group
23388     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23389     */
23390     register : function(checkbox)
23391     {
23392         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23393             this.groups[checkbox.groupId] = {};
23394         }
23395         
23396         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23397             return;
23398         }
23399         
23400         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23401         
23402     },
23403     /**
23404     * fetch a CheckBox Group based on the group ID
23405     * @param {string} the group ID
23406     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23407     */
23408     get: function(groupId) {
23409         if (typeof(this.groups[groupId]) == 'undefined') {
23410             return false;
23411         }
23412         
23413         return this.groups[groupId] ;
23414     }
23415     
23416     
23417 });
23418 /*
23419  * - LGPL
23420  *
23421  * RadioItem
23422  * 
23423  */
23424
23425 /**
23426  * @class Roo.bootstrap.Radio
23427  * @extends Roo.bootstrap.Component
23428  * Bootstrap Radio class
23429  * @cfg {String} boxLabel - the label associated
23430  * @cfg {String} value - the value of radio
23431  * 
23432  * @constructor
23433  * Create a new Radio
23434  * @param {Object} config The config object
23435  */
23436 Roo.bootstrap.Radio = function(config){
23437     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23438     
23439 };
23440
23441 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23442     
23443     boxLabel : '',
23444     
23445     value : '',
23446     
23447     getAutoCreate : function()
23448     {
23449         var cfg = {
23450             tag : 'div',
23451             cls : 'form-group radio',
23452             cn : [
23453                 {
23454                     tag : 'label',
23455                     cls : 'box-label',
23456                     html : this.boxLabel
23457                 }
23458             ]
23459         };
23460         
23461         return cfg;
23462     },
23463     
23464     initEvents : function() 
23465     {
23466         this.parent().register(this);
23467         
23468         this.el.on('click', this.onClick, this);
23469         
23470     },
23471     
23472     onClick : function(e)
23473     {
23474         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23475             this.setChecked(true);
23476         }
23477     },
23478     
23479     setChecked : function(state, suppressEvent)
23480     {
23481         this.parent().setValue(this.value, suppressEvent);
23482         
23483     },
23484     
23485     setBoxLabel : function(v)
23486     {
23487         this.boxLabel = v;
23488         
23489         if(this.rendered){
23490             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23491         }
23492     }
23493     
23494 });
23495  
23496
23497  /*
23498  * - LGPL
23499  *
23500  * Input
23501  * 
23502  */
23503
23504 /**
23505  * @class Roo.bootstrap.SecurePass
23506  * @extends Roo.bootstrap.Input
23507  * Bootstrap SecurePass class
23508  *
23509  * 
23510  * @constructor
23511  * Create a new SecurePass
23512  * @param {Object} config The config object
23513  */
23514  
23515 Roo.bootstrap.SecurePass = function (config) {
23516     // these go here, so the translation tool can replace them..
23517     this.errors = {
23518         PwdEmpty: "Please type a password, and then retype it to confirm.",
23519         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23520         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23521         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23522         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23523         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23524         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23525         TooWeak: "Your password is Too Weak."
23526     },
23527     this.meterLabel = "Password strength:";
23528     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23529     this.meterClass = [
23530         "roo-password-meter-tooweak", 
23531         "roo-password-meter-weak", 
23532         "roo-password-meter-medium", 
23533         "roo-password-meter-strong", 
23534         "roo-password-meter-grey"
23535     ];
23536     
23537     this.errors = {};
23538     
23539     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23540 }
23541
23542 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23543     /**
23544      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23545      * {
23546      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23547      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23548      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23549      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23550      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23551      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23552      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23553      * })
23554      */
23555     // private
23556     
23557     meterWidth: 300,
23558     errorMsg :'',    
23559     errors: false,
23560     imageRoot: '/',
23561     /**
23562      * @cfg {String/Object} Label for the strength meter (defaults to
23563      * 'Password strength:')
23564      */
23565     // private
23566     meterLabel: '',
23567     /**
23568      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23569      * ['Weak', 'Medium', 'Strong'])
23570      */
23571     // private    
23572     pwdStrengths: false,    
23573     // private
23574     strength: 0,
23575     // private
23576     _lastPwd: null,
23577     // private
23578     kCapitalLetter: 0,
23579     kSmallLetter: 1,
23580     kDigit: 2,
23581     kPunctuation: 3,
23582     
23583     insecure: false,
23584     // private
23585     initEvents: function ()
23586     {
23587         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23588
23589         if (this.el.is('input[type=password]') && Roo.isSafari) {
23590             this.el.on('keydown', this.SafariOnKeyDown, this);
23591         }
23592
23593         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23594     },
23595     // private
23596     onRender: function (ct, position)
23597     {
23598         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23599         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23600         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23601
23602         this.trigger.createChild({
23603                    cn: [
23604                     {
23605                     //id: 'PwdMeter',
23606                     tag: 'div',
23607                     cls: 'roo-password-meter-grey col-xs-12',
23608                     style: {
23609                         //width: 0,
23610                         //width: this.meterWidth + 'px'                                                
23611                         }
23612                     },
23613                     {                            
23614                          cls: 'roo-password-meter-text'                          
23615                     }
23616                 ]            
23617         });
23618
23619          
23620         if (this.hideTrigger) {
23621             this.trigger.setDisplayed(false);
23622         }
23623         this.setSize(this.width || '', this.height || '');
23624     },
23625     // private
23626     onDestroy: function ()
23627     {
23628         if (this.trigger) {
23629             this.trigger.removeAllListeners();
23630             this.trigger.remove();
23631         }
23632         if (this.wrap) {
23633             this.wrap.remove();
23634         }
23635         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23636     },
23637     // private
23638     checkStrength: function ()
23639     {
23640         var pwd = this.inputEl().getValue();
23641         if (pwd == this._lastPwd) {
23642             return;
23643         }
23644
23645         var strength;
23646         if (this.ClientSideStrongPassword(pwd)) {
23647             strength = 3;
23648         } else if (this.ClientSideMediumPassword(pwd)) {
23649             strength = 2;
23650         } else if (this.ClientSideWeakPassword(pwd)) {
23651             strength = 1;
23652         } else {
23653             strength = 0;
23654         }
23655         
23656         Roo.log('strength1: ' + strength);
23657         
23658         //var pm = this.trigger.child('div/div/div').dom;
23659         var pm = this.trigger.child('div/div');
23660         pm.removeClass(this.meterClass);
23661         pm.addClass(this.meterClass[strength]);
23662                 
23663         
23664         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23665                 
23666         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23667         
23668         this._lastPwd = pwd;
23669     },
23670     reset: function ()
23671     {
23672         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23673         
23674         this._lastPwd = '';
23675         
23676         var pm = this.trigger.child('div/div');
23677         pm.removeClass(this.meterClass);
23678         pm.addClass('roo-password-meter-grey');        
23679         
23680         
23681         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23682         
23683         pt.innerHTML = '';
23684         this.inputEl().dom.type='password';
23685     },
23686     // private
23687     validateValue: function (value)
23688     {
23689         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23690             return false;
23691         }
23692         if (value.length == 0) {
23693             if (this.allowBlank) {
23694                 this.clearInvalid();
23695                 return true;
23696             }
23697
23698             this.markInvalid(this.errors.PwdEmpty);
23699             this.errorMsg = this.errors.PwdEmpty;
23700             return false;
23701         }
23702         
23703         if(this.insecure){
23704             return true;
23705         }
23706         
23707         if (!value.match(/[\x21-\x7e]+/)) {
23708             this.markInvalid(this.errors.PwdBadChar);
23709             this.errorMsg = this.errors.PwdBadChar;
23710             return false;
23711         }
23712         if (value.length < 6) {
23713             this.markInvalid(this.errors.PwdShort);
23714             this.errorMsg = this.errors.PwdShort;
23715             return false;
23716         }
23717         if (value.length > 16) {
23718             this.markInvalid(this.errors.PwdLong);
23719             this.errorMsg = this.errors.PwdLong;
23720             return false;
23721         }
23722         var strength;
23723         if (this.ClientSideStrongPassword(value)) {
23724             strength = 3;
23725         } else if (this.ClientSideMediumPassword(value)) {
23726             strength = 2;
23727         } else if (this.ClientSideWeakPassword(value)) {
23728             strength = 1;
23729         } else {
23730             strength = 0;
23731         }
23732
23733         
23734         if (strength < 2) {
23735             //this.markInvalid(this.errors.TooWeak);
23736             this.errorMsg = this.errors.TooWeak;
23737             //return false;
23738         }
23739         
23740         
23741         console.log('strength2: ' + strength);
23742         
23743         //var pm = this.trigger.child('div/div/div').dom;
23744         
23745         var pm = this.trigger.child('div/div');
23746         pm.removeClass(this.meterClass);
23747         pm.addClass(this.meterClass[strength]);
23748                 
23749         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23750                 
23751         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23752         
23753         this.errorMsg = ''; 
23754         return true;
23755     },
23756     // private
23757     CharacterSetChecks: function (type)
23758     {
23759         this.type = type;
23760         this.fResult = false;
23761     },
23762     // private
23763     isctype: function (character, type)
23764     {
23765         switch (type) {  
23766             case this.kCapitalLetter:
23767                 if (character >= 'A' && character <= 'Z') {
23768                     return true;
23769                 }
23770                 break;
23771             
23772             case this.kSmallLetter:
23773                 if (character >= 'a' && character <= 'z') {
23774                     return true;
23775                 }
23776                 break;
23777             
23778             case this.kDigit:
23779                 if (character >= '0' && character <= '9') {
23780                     return true;
23781                 }
23782                 break;
23783             
23784             case this.kPunctuation:
23785                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23786                     return true;
23787                 }
23788                 break;
23789             
23790             default:
23791                 return false;
23792         }
23793
23794     },
23795     // private
23796     IsLongEnough: function (pwd, size)
23797     {
23798         return !(pwd == null || isNaN(size) || pwd.length < size);
23799     },
23800     // private
23801     SpansEnoughCharacterSets: function (word, nb)
23802     {
23803         if (!this.IsLongEnough(word, nb))
23804         {
23805             return false;
23806         }
23807
23808         var characterSetChecks = new Array(
23809             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23810             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23811         );
23812         
23813         for (var index = 0; index < word.length; ++index) {
23814             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23815                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23816                     characterSetChecks[nCharSet].fResult = true;
23817                     break;
23818                 }
23819             }
23820         }
23821
23822         var nCharSets = 0;
23823         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23824             if (characterSetChecks[nCharSet].fResult) {
23825                 ++nCharSets;
23826             }
23827         }
23828
23829         if (nCharSets < nb) {
23830             return false;
23831         }
23832         return true;
23833     },
23834     // private
23835     ClientSideStrongPassword: function (pwd)
23836     {
23837         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23838     },
23839     // private
23840     ClientSideMediumPassword: function (pwd)
23841     {
23842         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23843     },
23844     // private
23845     ClientSideWeakPassword: function (pwd)
23846     {
23847         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23848     }
23849           
23850 })//<script type="text/javascript">
23851
23852 /*
23853  * Based  Ext JS Library 1.1.1
23854  * Copyright(c) 2006-2007, Ext JS, LLC.
23855  * LGPL
23856  *
23857  */
23858  
23859 /**
23860  * @class Roo.HtmlEditorCore
23861  * @extends Roo.Component
23862  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23863  *
23864  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23865  */
23866
23867 Roo.HtmlEditorCore = function(config){
23868     
23869     
23870     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23871     
23872     
23873     this.addEvents({
23874         /**
23875          * @event initialize
23876          * Fires when the editor is fully initialized (including the iframe)
23877          * @param {Roo.HtmlEditorCore} this
23878          */
23879         initialize: true,
23880         /**
23881          * @event activate
23882          * Fires when the editor is first receives the focus. Any insertion must wait
23883          * until after this event.
23884          * @param {Roo.HtmlEditorCore} this
23885          */
23886         activate: true,
23887          /**
23888          * @event beforesync
23889          * Fires before the textarea is updated with content from the editor iframe. Return false
23890          * to cancel the sync.
23891          * @param {Roo.HtmlEditorCore} this
23892          * @param {String} html
23893          */
23894         beforesync: true,
23895          /**
23896          * @event beforepush
23897          * Fires before the iframe editor is updated with content from the textarea. Return false
23898          * to cancel the push.
23899          * @param {Roo.HtmlEditorCore} this
23900          * @param {String} html
23901          */
23902         beforepush: true,
23903          /**
23904          * @event sync
23905          * Fires when the textarea is updated with content from the editor iframe.
23906          * @param {Roo.HtmlEditorCore} this
23907          * @param {String} html
23908          */
23909         sync: true,
23910          /**
23911          * @event push
23912          * Fires when the iframe editor is updated with content from the textarea.
23913          * @param {Roo.HtmlEditorCore} this
23914          * @param {String} html
23915          */
23916         push: true,
23917         
23918         /**
23919          * @event editorevent
23920          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23921          * @param {Roo.HtmlEditorCore} this
23922          */
23923         editorevent: true
23924         
23925     });
23926     
23927     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23928     
23929     // defaults : white / black...
23930     this.applyBlacklists();
23931     
23932     
23933     
23934 };
23935
23936
23937 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23938
23939
23940      /**
23941      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23942      */
23943     
23944     owner : false,
23945     
23946      /**
23947      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23948      *                        Roo.resizable.
23949      */
23950     resizable : false,
23951      /**
23952      * @cfg {Number} height (in pixels)
23953      */   
23954     height: 300,
23955    /**
23956      * @cfg {Number} width (in pixels)
23957      */   
23958     width: 500,
23959     
23960     /**
23961      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23962      * 
23963      */
23964     stylesheets: false,
23965     
23966     // id of frame..
23967     frameId: false,
23968     
23969     // private properties
23970     validationEvent : false,
23971     deferHeight: true,
23972     initialized : false,
23973     activated : false,
23974     sourceEditMode : false,
23975     onFocus : Roo.emptyFn,
23976     iframePad:3,
23977     hideMode:'offsets',
23978     
23979     clearUp: true,
23980     
23981     // blacklist + whitelisted elements..
23982     black: false,
23983     white: false,
23984      
23985     bodyCls : '',
23986
23987     /**
23988      * Protected method that will not generally be called directly. It
23989      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23990      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23991      */
23992     getDocMarkup : function(){
23993         // body styles..
23994         var st = '';
23995         
23996         // inherit styels from page...?? 
23997         if (this.stylesheets === false) {
23998             
23999             Roo.get(document.head).select('style').each(function(node) {
24000                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24001             });
24002             
24003             Roo.get(document.head).select('link').each(function(node) { 
24004                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24005             });
24006             
24007         } else if (!this.stylesheets.length) {
24008                 // simple..
24009                 st = '<style type="text/css">' +
24010                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24011                    '</style>';
24012         } else {
24013             for (var i in this.stylesheets) { 
24014                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24015             }
24016             
24017         }
24018         
24019         st +=  '<style type="text/css">' +
24020             'IMG { cursor: pointer } ' +
24021         '</style>';
24022
24023         var cls = 'roo-htmleditor-body';
24024         
24025         if(this.bodyCls.length){
24026             cls += ' ' + this.bodyCls;
24027         }
24028         
24029         return '<html><head>' + st  +
24030             //<style type="text/css">' +
24031             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24032             //'</style>' +
24033             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24034     },
24035
24036     // private
24037     onRender : function(ct, position)
24038     {
24039         var _t = this;
24040         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24041         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24042         
24043         
24044         this.el.dom.style.border = '0 none';
24045         this.el.dom.setAttribute('tabIndex', -1);
24046         this.el.addClass('x-hidden hide');
24047         
24048         
24049         
24050         if(Roo.isIE){ // fix IE 1px bogus margin
24051             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24052         }
24053        
24054         
24055         this.frameId = Roo.id();
24056         
24057          
24058         
24059         var iframe = this.owner.wrap.createChild({
24060             tag: 'iframe',
24061             cls: 'form-control', // bootstrap..
24062             id: this.frameId,
24063             name: this.frameId,
24064             frameBorder : 'no',
24065             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24066         }, this.el
24067         );
24068         
24069         
24070         this.iframe = iframe.dom;
24071
24072          this.assignDocWin();
24073         
24074         this.doc.designMode = 'on';
24075        
24076         this.doc.open();
24077         this.doc.write(this.getDocMarkup());
24078         this.doc.close();
24079
24080         
24081         var task = { // must defer to wait for browser to be ready
24082             run : function(){
24083                 //console.log("run task?" + this.doc.readyState);
24084                 this.assignDocWin();
24085                 if(this.doc.body || this.doc.readyState == 'complete'){
24086                     try {
24087                         this.doc.designMode="on";
24088                     } catch (e) {
24089                         return;
24090                     }
24091                     Roo.TaskMgr.stop(task);
24092                     this.initEditor.defer(10, this);
24093                 }
24094             },
24095             interval : 10,
24096             duration: 10000,
24097             scope: this
24098         };
24099         Roo.TaskMgr.start(task);
24100
24101     },
24102
24103     // private
24104     onResize : function(w, h)
24105     {
24106          Roo.log('resize: ' +w + ',' + h );
24107         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24108         if(!this.iframe){
24109             return;
24110         }
24111         if(typeof w == 'number'){
24112             
24113             this.iframe.style.width = w + 'px';
24114         }
24115         if(typeof h == 'number'){
24116             
24117             this.iframe.style.height = h + 'px';
24118             if(this.doc){
24119                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24120             }
24121         }
24122         
24123     },
24124
24125     /**
24126      * Toggles the editor between standard and source edit mode.
24127      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24128      */
24129     toggleSourceEdit : function(sourceEditMode){
24130         
24131         this.sourceEditMode = sourceEditMode === true;
24132         
24133         if(this.sourceEditMode){
24134  
24135             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24136             
24137         }else{
24138             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24139             //this.iframe.className = '';
24140             this.deferFocus();
24141         }
24142         //this.setSize(this.owner.wrap.getSize());
24143         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24144     },
24145
24146     
24147   
24148
24149     /**
24150      * Protected method that will not generally be called directly. If you need/want
24151      * custom HTML cleanup, this is the method you should override.
24152      * @param {String} html The HTML to be cleaned
24153      * return {String} The cleaned HTML
24154      */
24155     cleanHtml : function(html){
24156         html = String(html);
24157         if(html.length > 5){
24158             if(Roo.isSafari){ // strip safari nonsense
24159                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24160             }
24161         }
24162         if(html == '&nbsp;'){
24163             html = '';
24164         }
24165         return html;
24166     },
24167
24168     /**
24169      * HTML Editor -> Textarea
24170      * Protected method that will not generally be called directly. Syncs the contents
24171      * of the editor iframe with the textarea.
24172      */
24173     syncValue : function(){
24174         if(this.initialized){
24175             var bd = (this.doc.body || this.doc.documentElement);
24176             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24177             var html = bd.innerHTML;
24178             if(Roo.isSafari){
24179                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24180                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24181                 if(m && m[1]){
24182                     html = '<div style="'+m[0]+'">' + html + '</div>';
24183                 }
24184             }
24185             html = this.cleanHtml(html);
24186             // fix up the special chars.. normaly like back quotes in word...
24187             // however we do not want to do this with chinese..
24188             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24189                 
24190                 var cc = match.charCodeAt();
24191
24192                 // Get the character value, handling surrogate pairs
24193                 if (match.length == 2) {
24194                     // It's a surrogate pair, calculate the Unicode code point
24195                     var high = match.charCodeAt(0) - 0xD800;
24196                     var low  = match.charCodeAt(1) - 0xDC00;
24197                     cc = (high * 0x400) + low + 0x10000;
24198                 }  else if (
24199                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24200                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24201                     (cc >= 0xf900 && cc < 0xfb00 )
24202                 ) {
24203                         return match;
24204                 }  
24205          
24206                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24207                 return "&#" + cc + ";";
24208                 
24209                 
24210             });
24211             
24212             
24213              
24214             if(this.owner.fireEvent('beforesync', this, html) !== false){
24215                 this.el.dom.value = html;
24216                 this.owner.fireEvent('sync', this, html);
24217             }
24218         }
24219     },
24220
24221     /**
24222      * Protected method that will not generally be called directly. Pushes the value of the textarea
24223      * into the iframe editor.
24224      */
24225     pushValue : function(){
24226         if(this.initialized){
24227             var v = this.el.dom.value.trim();
24228             
24229 //            if(v.length < 1){
24230 //                v = '&#160;';
24231 //            }
24232             
24233             if(this.owner.fireEvent('beforepush', this, v) !== false){
24234                 var d = (this.doc.body || this.doc.documentElement);
24235                 d.innerHTML = v;
24236                 this.cleanUpPaste();
24237                 this.el.dom.value = d.innerHTML;
24238                 this.owner.fireEvent('push', this, v);
24239             }
24240         }
24241     },
24242
24243     // private
24244     deferFocus : function(){
24245         this.focus.defer(10, this);
24246     },
24247
24248     // doc'ed in Field
24249     focus : function(){
24250         if(this.win && !this.sourceEditMode){
24251             this.win.focus();
24252         }else{
24253             this.el.focus();
24254         }
24255     },
24256     
24257     assignDocWin: function()
24258     {
24259         var iframe = this.iframe;
24260         
24261          if(Roo.isIE){
24262             this.doc = iframe.contentWindow.document;
24263             this.win = iframe.contentWindow;
24264         } else {
24265 //            if (!Roo.get(this.frameId)) {
24266 //                return;
24267 //            }
24268 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24269 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24270             
24271             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24272                 return;
24273             }
24274             
24275             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24276             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24277         }
24278     },
24279     
24280     // private
24281     initEditor : function(){
24282         //console.log("INIT EDITOR");
24283         this.assignDocWin();
24284         
24285         
24286         
24287         this.doc.designMode="on";
24288         this.doc.open();
24289         this.doc.write(this.getDocMarkup());
24290         this.doc.close();
24291         
24292         var dbody = (this.doc.body || this.doc.documentElement);
24293         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24294         // this copies styles from the containing element into thsi one..
24295         // not sure why we need all of this..
24296         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24297         
24298         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24299         //ss['background-attachment'] = 'fixed'; // w3c
24300         dbody.bgProperties = 'fixed'; // ie
24301         //Roo.DomHelper.applyStyles(dbody, ss);
24302         Roo.EventManager.on(this.doc, {
24303             //'mousedown': this.onEditorEvent,
24304             'mouseup': this.onEditorEvent,
24305             'dblclick': this.onEditorEvent,
24306             'click': this.onEditorEvent,
24307             'keyup': this.onEditorEvent,
24308             buffer:100,
24309             scope: this
24310         });
24311         if(Roo.isGecko){
24312             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24313         }
24314         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24315             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24316         }
24317         this.initialized = true;
24318
24319         this.owner.fireEvent('initialize', this);
24320         this.pushValue();
24321     },
24322
24323     // private
24324     onDestroy : function(){
24325         
24326         
24327         
24328         if(this.rendered){
24329             
24330             //for (var i =0; i < this.toolbars.length;i++) {
24331             //    // fixme - ask toolbars for heights?
24332             //    this.toolbars[i].onDestroy();
24333            // }
24334             
24335             //this.wrap.dom.innerHTML = '';
24336             //this.wrap.remove();
24337         }
24338     },
24339
24340     // private
24341     onFirstFocus : function(){
24342         
24343         this.assignDocWin();
24344         
24345         
24346         this.activated = true;
24347          
24348     
24349         if(Roo.isGecko){ // prevent silly gecko errors
24350             this.win.focus();
24351             var s = this.win.getSelection();
24352             if(!s.focusNode || s.focusNode.nodeType != 3){
24353                 var r = s.getRangeAt(0);
24354                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24355                 r.collapse(true);
24356                 this.deferFocus();
24357             }
24358             try{
24359                 this.execCmd('useCSS', true);
24360                 this.execCmd('styleWithCSS', false);
24361             }catch(e){}
24362         }
24363         this.owner.fireEvent('activate', this);
24364     },
24365
24366     // private
24367     adjustFont: function(btn){
24368         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24369         //if(Roo.isSafari){ // safari
24370         //    adjust *= 2;
24371        // }
24372         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24373         if(Roo.isSafari){ // safari
24374             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24375             v =  (v < 10) ? 10 : v;
24376             v =  (v > 48) ? 48 : v;
24377             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24378             
24379         }
24380         
24381         
24382         v = Math.max(1, v+adjust);
24383         
24384         this.execCmd('FontSize', v  );
24385     },
24386
24387     onEditorEvent : function(e)
24388     {
24389         this.owner.fireEvent('editorevent', this, e);
24390       //  this.updateToolbar();
24391         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24392     },
24393
24394     insertTag : function(tg)
24395     {
24396         // could be a bit smarter... -> wrap the current selected tRoo..
24397         if (tg.toLowerCase() == 'span' ||
24398             tg.toLowerCase() == 'code' ||
24399             tg.toLowerCase() == 'sup' ||
24400             tg.toLowerCase() == 'sub' 
24401             ) {
24402             
24403             range = this.createRange(this.getSelection());
24404             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24405             wrappingNode.appendChild(range.extractContents());
24406             range.insertNode(wrappingNode);
24407
24408             return;
24409             
24410             
24411             
24412         }
24413         this.execCmd("formatblock",   tg);
24414         
24415     },
24416     
24417     insertText : function(txt)
24418     {
24419         
24420         
24421         var range = this.createRange();
24422         range.deleteContents();
24423                //alert(Sender.getAttribute('label'));
24424                
24425         range.insertNode(this.doc.createTextNode(txt));
24426     } ,
24427     
24428      
24429
24430     /**
24431      * Executes a Midas editor command on the editor document and performs necessary focus and
24432      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24433      * @param {String} cmd The Midas command
24434      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24435      */
24436     relayCmd : function(cmd, value){
24437         this.win.focus();
24438         this.execCmd(cmd, value);
24439         this.owner.fireEvent('editorevent', this);
24440         //this.updateToolbar();
24441         this.owner.deferFocus();
24442     },
24443
24444     /**
24445      * Executes a Midas editor command directly on the editor document.
24446      * For visual commands, you should use {@link #relayCmd} instead.
24447      * <b>This should only be called after the editor is initialized.</b>
24448      * @param {String} cmd The Midas command
24449      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24450      */
24451     execCmd : function(cmd, value){
24452         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24453         this.syncValue();
24454     },
24455  
24456  
24457    
24458     /**
24459      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24460      * to insert tRoo.
24461      * @param {String} text | dom node.. 
24462      */
24463     insertAtCursor : function(text)
24464     {
24465         
24466         if(!this.activated){
24467             return;
24468         }
24469         /*
24470         if(Roo.isIE){
24471             this.win.focus();
24472             var r = this.doc.selection.createRange();
24473             if(r){
24474                 r.collapse(true);
24475                 r.pasteHTML(text);
24476                 this.syncValue();
24477                 this.deferFocus();
24478             
24479             }
24480             return;
24481         }
24482         */
24483         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24484             this.win.focus();
24485             
24486             
24487             // from jquery ui (MIT licenced)
24488             var range, node;
24489             var win = this.win;
24490             
24491             if (win.getSelection && win.getSelection().getRangeAt) {
24492                 range = win.getSelection().getRangeAt(0);
24493                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24494                 range.insertNode(node);
24495             } else if (win.document.selection && win.document.selection.createRange) {
24496                 // no firefox support
24497                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24498                 win.document.selection.createRange().pasteHTML(txt);
24499             } else {
24500                 // no firefox support
24501                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24502                 this.execCmd('InsertHTML', txt);
24503             } 
24504             
24505             this.syncValue();
24506             
24507             this.deferFocus();
24508         }
24509     },
24510  // private
24511     mozKeyPress : function(e){
24512         if(e.ctrlKey){
24513             var c = e.getCharCode(), cmd;
24514           
24515             if(c > 0){
24516                 c = String.fromCharCode(c).toLowerCase();
24517                 switch(c){
24518                     case 'b':
24519                         cmd = 'bold';
24520                         break;
24521                     case 'i':
24522                         cmd = 'italic';
24523                         break;
24524                     
24525                     case 'u':
24526                         cmd = 'underline';
24527                         break;
24528                     
24529                     case 'v':
24530                         this.cleanUpPaste.defer(100, this);
24531                         return;
24532                         
24533                 }
24534                 if(cmd){
24535                     this.win.focus();
24536                     this.execCmd(cmd);
24537                     this.deferFocus();
24538                     e.preventDefault();
24539                 }
24540                 
24541             }
24542         }
24543     },
24544
24545     // private
24546     fixKeys : function(){ // load time branching for fastest keydown performance
24547         if(Roo.isIE){
24548             return function(e){
24549                 var k = e.getKey(), r;
24550                 if(k == e.TAB){
24551                     e.stopEvent();
24552                     r = this.doc.selection.createRange();
24553                     if(r){
24554                         r.collapse(true);
24555                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24556                         this.deferFocus();
24557                     }
24558                     return;
24559                 }
24560                 
24561                 if(k == e.ENTER){
24562                     r = this.doc.selection.createRange();
24563                     if(r){
24564                         var target = r.parentElement();
24565                         if(!target || target.tagName.toLowerCase() != 'li'){
24566                             e.stopEvent();
24567                             r.pasteHTML('<br />');
24568                             r.collapse(false);
24569                             r.select();
24570                         }
24571                     }
24572                 }
24573                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24574                     this.cleanUpPaste.defer(100, this);
24575                     return;
24576                 }
24577                 
24578                 
24579             };
24580         }else if(Roo.isOpera){
24581             return function(e){
24582                 var k = e.getKey();
24583                 if(k == e.TAB){
24584                     e.stopEvent();
24585                     this.win.focus();
24586                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24587                     this.deferFocus();
24588                 }
24589                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24590                     this.cleanUpPaste.defer(100, this);
24591                     return;
24592                 }
24593                 
24594             };
24595         }else if(Roo.isSafari){
24596             return function(e){
24597                 var k = e.getKey();
24598                 
24599                 if(k == e.TAB){
24600                     e.stopEvent();
24601                     this.execCmd('InsertText','\t');
24602                     this.deferFocus();
24603                     return;
24604                 }
24605                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24606                     this.cleanUpPaste.defer(100, this);
24607                     return;
24608                 }
24609                 
24610              };
24611         }
24612     }(),
24613     
24614     getAllAncestors: function()
24615     {
24616         var p = this.getSelectedNode();
24617         var a = [];
24618         if (!p) {
24619             a.push(p); // push blank onto stack..
24620             p = this.getParentElement();
24621         }
24622         
24623         
24624         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24625             a.push(p);
24626             p = p.parentNode;
24627         }
24628         a.push(this.doc.body);
24629         return a;
24630     },
24631     lastSel : false,
24632     lastSelNode : false,
24633     
24634     
24635     getSelection : function() 
24636     {
24637         this.assignDocWin();
24638         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24639     },
24640     
24641     getSelectedNode: function() 
24642     {
24643         // this may only work on Gecko!!!
24644         
24645         // should we cache this!!!!
24646         
24647         
24648         
24649          
24650         var range = this.createRange(this.getSelection()).cloneRange();
24651         
24652         if (Roo.isIE) {
24653             var parent = range.parentElement();
24654             while (true) {
24655                 var testRange = range.duplicate();
24656                 testRange.moveToElementText(parent);
24657                 if (testRange.inRange(range)) {
24658                     break;
24659                 }
24660                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24661                     break;
24662                 }
24663                 parent = parent.parentElement;
24664             }
24665             return parent;
24666         }
24667         
24668         // is ancestor a text element.
24669         var ac =  range.commonAncestorContainer;
24670         if (ac.nodeType == 3) {
24671             ac = ac.parentNode;
24672         }
24673         
24674         var ar = ac.childNodes;
24675          
24676         var nodes = [];
24677         var other_nodes = [];
24678         var has_other_nodes = false;
24679         for (var i=0;i<ar.length;i++) {
24680             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24681                 continue;
24682             }
24683             // fullly contained node.
24684             
24685             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24686                 nodes.push(ar[i]);
24687                 continue;
24688             }
24689             
24690             // probably selected..
24691             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24692                 other_nodes.push(ar[i]);
24693                 continue;
24694             }
24695             // outer..
24696             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24697                 continue;
24698             }
24699             
24700             
24701             has_other_nodes = true;
24702         }
24703         if (!nodes.length && other_nodes.length) {
24704             nodes= other_nodes;
24705         }
24706         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24707             return false;
24708         }
24709         
24710         return nodes[0];
24711     },
24712     createRange: function(sel)
24713     {
24714         // this has strange effects when using with 
24715         // top toolbar - not sure if it's a great idea.
24716         //this.editor.contentWindow.focus();
24717         if (typeof sel != "undefined") {
24718             try {
24719                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24720             } catch(e) {
24721                 return this.doc.createRange();
24722             }
24723         } else {
24724             return this.doc.createRange();
24725         }
24726     },
24727     getParentElement: function()
24728     {
24729         
24730         this.assignDocWin();
24731         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24732         
24733         var range = this.createRange(sel);
24734          
24735         try {
24736             var p = range.commonAncestorContainer;
24737             while (p.nodeType == 3) { // text node
24738                 p = p.parentNode;
24739             }
24740             return p;
24741         } catch (e) {
24742             return null;
24743         }
24744     
24745     },
24746     /***
24747      *
24748      * Range intersection.. the hard stuff...
24749      *  '-1' = before
24750      *  '0' = hits..
24751      *  '1' = after.
24752      *         [ -- selected range --- ]
24753      *   [fail]                        [fail]
24754      *
24755      *    basically..
24756      *      if end is before start or  hits it. fail.
24757      *      if start is after end or hits it fail.
24758      *
24759      *   if either hits (but other is outside. - then it's not 
24760      *   
24761      *    
24762      **/
24763     
24764     
24765     // @see http://www.thismuchiknow.co.uk/?p=64.
24766     rangeIntersectsNode : function(range, node)
24767     {
24768         var nodeRange = node.ownerDocument.createRange();
24769         try {
24770             nodeRange.selectNode(node);
24771         } catch (e) {
24772             nodeRange.selectNodeContents(node);
24773         }
24774     
24775         var rangeStartRange = range.cloneRange();
24776         rangeStartRange.collapse(true);
24777     
24778         var rangeEndRange = range.cloneRange();
24779         rangeEndRange.collapse(false);
24780     
24781         var nodeStartRange = nodeRange.cloneRange();
24782         nodeStartRange.collapse(true);
24783     
24784         var nodeEndRange = nodeRange.cloneRange();
24785         nodeEndRange.collapse(false);
24786     
24787         return rangeStartRange.compareBoundaryPoints(
24788                  Range.START_TO_START, nodeEndRange) == -1 &&
24789                rangeEndRange.compareBoundaryPoints(
24790                  Range.START_TO_START, nodeStartRange) == 1;
24791         
24792          
24793     },
24794     rangeCompareNode : function(range, node)
24795     {
24796         var nodeRange = node.ownerDocument.createRange();
24797         try {
24798             nodeRange.selectNode(node);
24799         } catch (e) {
24800             nodeRange.selectNodeContents(node);
24801         }
24802         
24803         
24804         range.collapse(true);
24805     
24806         nodeRange.collapse(true);
24807      
24808         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24809         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24810          
24811         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24812         
24813         var nodeIsBefore   =  ss == 1;
24814         var nodeIsAfter    = ee == -1;
24815         
24816         if (nodeIsBefore && nodeIsAfter) {
24817             return 0; // outer
24818         }
24819         if (!nodeIsBefore && nodeIsAfter) {
24820             return 1; //right trailed.
24821         }
24822         
24823         if (nodeIsBefore && !nodeIsAfter) {
24824             return 2;  // left trailed.
24825         }
24826         // fully contined.
24827         return 3;
24828     },
24829
24830     // private? - in a new class?
24831     cleanUpPaste :  function()
24832     {
24833         // cleans up the whole document..
24834         Roo.log('cleanuppaste');
24835         
24836         this.cleanUpChildren(this.doc.body);
24837         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24838         if (clean != this.doc.body.innerHTML) {
24839             this.doc.body.innerHTML = clean;
24840         }
24841         
24842     },
24843     
24844     cleanWordChars : function(input) {// change the chars to hex code
24845         var he = Roo.HtmlEditorCore;
24846         
24847         var output = input;
24848         Roo.each(he.swapCodes, function(sw) { 
24849             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24850             
24851             output = output.replace(swapper, sw[1]);
24852         });
24853         
24854         return output;
24855     },
24856     
24857     
24858     cleanUpChildren : function (n)
24859     {
24860         if (!n.childNodes.length) {
24861             return;
24862         }
24863         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24864            this.cleanUpChild(n.childNodes[i]);
24865         }
24866     },
24867     
24868     
24869         
24870     
24871     cleanUpChild : function (node)
24872     {
24873         var ed = this;
24874         //console.log(node);
24875         if (node.nodeName == "#text") {
24876             // clean up silly Windows -- stuff?
24877             return; 
24878         }
24879         if (node.nodeName == "#comment") {
24880             node.parentNode.removeChild(node);
24881             // clean up silly Windows -- stuff?
24882             return; 
24883         }
24884         var lcname = node.tagName.toLowerCase();
24885         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24886         // whitelist of tags..
24887         
24888         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24889             // remove node.
24890             node.parentNode.removeChild(node);
24891             return;
24892             
24893         }
24894         
24895         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24896         
24897         // spans with no attributes - just remove them..
24898         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24899             remove_keep_children = true;
24900         }
24901         
24902         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24903         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24904         
24905         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24906         //    remove_keep_children = true;
24907         //}
24908         
24909         if (remove_keep_children) {
24910             this.cleanUpChildren(node);
24911             // inserts everything just before this node...
24912             while (node.childNodes.length) {
24913                 var cn = node.childNodes[0];
24914                 node.removeChild(cn);
24915                 node.parentNode.insertBefore(cn, node);
24916             }
24917             node.parentNode.removeChild(node);
24918             return;
24919         }
24920         
24921         if (!node.attributes || !node.attributes.length) {
24922             
24923           
24924             
24925             
24926             this.cleanUpChildren(node);
24927             return;
24928         }
24929         
24930         function cleanAttr(n,v)
24931         {
24932             
24933             if (v.match(/^\./) || v.match(/^\//)) {
24934                 return;
24935             }
24936             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24937                 return;
24938             }
24939             if (v.match(/^#/)) {
24940                 return;
24941             }
24942             if (v.match(/^\{/)) { // allow template editing.
24943                 return;
24944             }
24945 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24946             node.removeAttribute(n);
24947             
24948         }
24949         
24950         var cwhite = this.cwhite;
24951         var cblack = this.cblack;
24952             
24953         function cleanStyle(n,v)
24954         {
24955             if (v.match(/expression/)) { //XSS?? should we even bother..
24956                 node.removeAttribute(n);
24957                 return;
24958             }
24959             
24960             var parts = v.split(/;/);
24961             var clean = [];
24962             
24963             Roo.each(parts, function(p) {
24964                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24965                 if (!p.length) {
24966                     return true;
24967                 }
24968                 var l = p.split(':').shift().replace(/\s+/g,'');
24969                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24970                 
24971                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24972 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24973                     //node.removeAttribute(n);
24974                     return true;
24975                 }
24976                 //Roo.log()
24977                 // only allow 'c whitelisted system attributes'
24978                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24979 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24980                     //node.removeAttribute(n);
24981                     return true;
24982                 }
24983                 
24984                 
24985                  
24986                 
24987                 clean.push(p);
24988                 return true;
24989             });
24990             if (clean.length) { 
24991                 node.setAttribute(n, clean.join(';'));
24992             } else {
24993                 node.removeAttribute(n);
24994             }
24995             
24996         }
24997         
24998         
24999         for (var i = node.attributes.length-1; i > -1 ; i--) {
25000             var a = node.attributes[i];
25001             //console.log(a);
25002             
25003             if (a.name.toLowerCase().substr(0,2)=='on')  {
25004                 node.removeAttribute(a.name);
25005                 continue;
25006             }
25007             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25008                 node.removeAttribute(a.name);
25009                 continue;
25010             }
25011             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25012                 cleanAttr(a.name,a.value); // fixme..
25013                 continue;
25014             }
25015             if (a.name == 'style') {
25016                 cleanStyle(a.name,a.value);
25017                 continue;
25018             }
25019             /// clean up MS crap..
25020             // tecnically this should be a list of valid class'es..
25021             
25022             
25023             if (a.name == 'class') {
25024                 if (a.value.match(/^Mso/)) {
25025                     node.removeAttribute('class');
25026                 }
25027                 
25028                 if (a.value.match(/^body$/)) {
25029                     node.removeAttribute('class');
25030                 }
25031                 continue;
25032             }
25033             
25034             // style cleanup!?
25035             // class cleanup?
25036             
25037         }
25038         
25039         
25040         this.cleanUpChildren(node);
25041         
25042         
25043     },
25044     
25045     /**
25046      * Clean up MS wordisms...
25047      */
25048     cleanWord : function(node)
25049     {
25050         if (!node) {
25051             this.cleanWord(this.doc.body);
25052             return;
25053         }
25054         
25055         if(
25056                 node.nodeName == 'SPAN' &&
25057                 !node.hasAttributes() &&
25058                 node.childNodes.length == 1 &&
25059                 node.firstChild.nodeName == "#text"  
25060         ) {
25061             var textNode = node.firstChild;
25062             node.removeChild(textNode);
25063             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25064                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25065             }
25066             node.parentNode.insertBefore(textNode, node);
25067             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25068                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25069             }
25070             node.parentNode.removeChild(node);
25071         }
25072         
25073         if (node.nodeName == "#text") {
25074             // clean up silly Windows -- stuff?
25075             return; 
25076         }
25077         if (node.nodeName == "#comment") {
25078             node.parentNode.removeChild(node);
25079             // clean up silly Windows -- stuff?
25080             return; 
25081         }
25082         
25083         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25084             node.parentNode.removeChild(node);
25085             return;
25086         }
25087         //Roo.log(node.tagName);
25088         // remove - but keep children..
25089         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25090             //Roo.log('-- removed');
25091             while (node.childNodes.length) {
25092                 var cn = node.childNodes[0];
25093                 node.removeChild(cn);
25094                 node.parentNode.insertBefore(cn, node);
25095                 // move node to parent - and clean it..
25096                 this.cleanWord(cn);
25097             }
25098             node.parentNode.removeChild(node);
25099             /// no need to iterate chidlren = it's got none..
25100             //this.iterateChildren(node, this.cleanWord);
25101             return;
25102         }
25103         // clean styles
25104         if (node.className.length) {
25105             
25106             var cn = node.className.split(/\W+/);
25107             var cna = [];
25108             Roo.each(cn, function(cls) {
25109                 if (cls.match(/Mso[a-zA-Z]+/)) {
25110                     return;
25111                 }
25112                 cna.push(cls);
25113             });
25114             node.className = cna.length ? cna.join(' ') : '';
25115             if (!cna.length) {
25116                 node.removeAttribute("class");
25117             }
25118         }
25119         
25120         if (node.hasAttribute("lang")) {
25121             node.removeAttribute("lang");
25122         }
25123         
25124         if (node.hasAttribute("style")) {
25125             
25126             var styles = node.getAttribute("style").split(";");
25127             var nstyle = [];
25128             Roo.each(styles, function(s) {
25129                 if (!s.match(/:/)) {
25130                     return;
25131                 }
25132                 var kv = s.split(":");
25133                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25134                     return;
25135                 }
25136                 // what ever is left... we allow.
25137                 nstyle.push(s);
25138             });
25139             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25140             if (!nstyle.length) {
25141                 node.removeAttribute('style');
25142             }
25143         }
25144         this.iterateChildren(node, this.cleanWord);
25145         
25146         
25147         
25148     },
25149     /**
25150      * iterateChildren of a Node, calling fn each time, using this as the scole..
25151      * @param {DomNode} node node to iterate children of.
25152      * @param {Function} fn method of this class to call on each item.
25153      */
25154     iterateChildren : function(node, fn)
25155     {
25156         if (!node.childNodes.length) {
25157                 return;
25158         }
25159         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25160            fn.call(this, node.childNodes[i])
25161         }
25162     },
25163     
25164     
25165     /**
25166      * cleanTableWidths.
25167      *
25168      * Quite often pasting from word etc.. results in tables with column and widths.
25169      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25170      *
25171      */
25172     cleanTableWidths : function(node)
25173     {
25174          
25175          
25176         if (!node) {
25177             this.cleanTableWidths(this.doc.body);
25178             return;
25179         }
25180         
25181         // ignore list...
25182         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25183             return; 
25184         }
25185         Roo.log(node.tagName);
25186         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25187             this.iterateChildren(node, this.cleanTableWidths);
25188             return;
25189         }
25190         if (node.hasAttribute('width')) {
25191             node.removeAttribute('width');
25192         }
25193         
25194          
25195         if (node.hasAttribute("style")) {
25196             // pretty basic...
25197             
25198             var styles = node.getAttribute("style").split(";");
25199             var nstyle = [];
25200             Roo.each(styles, function(s) {
25201                 if (!s.match(/:/)) {
25202                     return;
25203                 }
25204                 var kv = s.split(":");
25205                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25206                     return;
25207                 }
25208                 // what ever is left... we allow.
25209                 nstyle.push(s);
25210             });
25211             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25212             if (!nstyle.length) {
25213                 node.removeAttribute('style');
25214             }
25215         }
25216         
25217         this.iterateChildren(node, this.cleanTableWidths);
25218         
25219         
25220     },
25221     
25222     
25223     
25224     
25225     domToHTML : function(currentElement, depth, nopadtext) {
25226         
25227         depth = depth || 0;
25228         nopadtext = nopadtext || false;
25229     
25230         if (!currentElement) {
25231             return this.domToHTML(this.doc.body);
25232         }
25233         
25234         //Roo.log(currentElement);
25235         var j;
25236         var allText = false;
25237         var nodeName = currentElement.nodeName;
25238         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25239         
25240         if  (nodeName == '#text') {
25241             
25242             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25243         }
25244         
25245         
25246         var ret = '';
25247         if (nodeName != 'BODY') {
25248              
25249             var i = 0;
25250             // Prints the node tagName, such as <A>, <IMG>, etc
25251             if (tagName) {
25252                 var attr = [];
25253                 for(i = 0; i < currentElement.attributes.length;i++) {
25254                     // quoting?
25255                     var aname = currentElement.attributes.item(i).name;
25256                     if (!currentElement.attributes.item(i).value.length) {
25257                         continue;
25258                     }
25259                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25260                 }
25261                 
25262                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25263             } 
25264             else {
25265                 
25266                 // eack
25267             }
25268         } else {
25269             tagName = false;
25270         }
25271         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25272             return ret;
25273         }
25274         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25275             nopadtext = true;
25276         }
25277         
25278         
25279         // Traverse the tree
25280         i = 0;
25281         var currentElementChild = currentElement.childNodes.item(i);
25282         var allText = true;
25283         var innerHTML  = '';
25284         lastnode = '';
25285         while (currentElementChild) {
25286             // Formatting code (indent the tree so it looks nice on the screen)
25287             var nopad = nopadtext;
25288             if (lastnode == 'SPAN') {
25289                 nopad  = true;
25290             }
25291             // text
25292             if  (currentElementChild.nodeName == '#text') {
25293                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25294                 toadd = nopadtext ? toadd : toadd.trim();
25295                 if (!nopad && toadd.length > 80) {
25296                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25297                 }
25298                 innerHTML  += toadd;
25299                 
25300                 i++;
25301                 currentElementChild = currentElement.childNodes.item(i);
25302                 lastNode = '';
25303                 continue;
25304             }
25305             allText = false;
25306             
25307             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25308                 
25309             // Recursively traverse the tree structure of the child node
25310             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25311             lastnode = currentElementChild.nodeName;
25312             i++;
25313             currentElementChild=currentElement.childNodes.item(i);
25314         }
25315         
25316         ret += innerHTML;
25317         
25318         if (!allText) {
25319                 // The remaining code is mostly for formatting the tree
25320             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25321         }
25322         
25323         
25324         if (tagName) {
25325             ret+= "</"+tagName+">";
25326         }
25327         return ret;
25328         
25329     },
25330         
25331     applyBlacklists : function()
25332     {
25333         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25334         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25335         
25336         this.white = [];
25337         this.black = [];
25338         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25339             if (b.indexOf(tag) > -1) {
25340                 return;
25341             }
25342             this.white.push(tag);
25343             
25344         }, this);
25345         
25346         Roo.each(w, function(tag) {
25347             if (b.indexOf(tag) > -1) {
25348                 return;
25349             }
25350             if (this.white.indexOf(tag) > -1) {
25351                 return;
25352             }
25353             this.white.push(tag);
25354             
25355         }, this);
25356         
25357         
25358         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25359             if (w.indexOf(tag) > -1) {
25360                 return;
25361             }
25362             this.black.push(tag);
25363             
25364         }, this);
25365         
25366         Roo.each(b, function(tag) {
25367             if (w.indexOf(tag) > -1) {
25368                 return;
25369             }
25370             if (this.black.indexOf(tag) > -1) {
25371                 return;
25372             }
25373             this.black.push(tag);
25374             
25375         }, this);
25376         
25377         
25378         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25379         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25380         
25381         this.cwhite = [];
25382         this.cblack = [];
25383         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25384             if (b.indexOf(tag) > -1) {
25385                 return;
25386             }
25387             this.cwhite.push(tag);
25388             
25389         }, this);
25390         
25391         Roo.each(w, function(tag) {
25392             if (b.indexOf(tag) > -1) {
25393                 return;
25394             }
25395             if (this.cwhite.indexOf(tag) > -1) {
25396                 return;
25397             }
25398             this.cwhite.push(tag);
25399             
25400         }, this);
25401         
25402         
25403         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25404             if (w.indexOf(tag) > -1) {
25405                 return;
25406             }
25407             this.cblack.push(tag);
25408             
25409         }, this);
25410         
25411         Roo.each(b, function(tag) {
25412             if (w.indexOf(tag) > -1) {
25413                 return;
25414             }
25415             if (this.cblack.indexOf(tag) > -1) {
25416                 return;
25417             }
25418             this.cblack.push(tag);
25419             
25420         }, this);
25421     },
25422     
25423     setStylesheets : function(stylesheets)
25424     {
25425         if(typeof(stylesheets) == 'string'){
25426             Roo.get(this.iframe.contentDocument.head).createChild({
25427                 tag : 'link',
25428                 rel : 'stylesheet',
25429                 type : 'text/css',
25430                 href : stylesheets
25431             });
25432             
25433             return;
25434         }
25435         var _this = this;
25436      
25437         Roo.each(stylesheets, function(s) {
25438             if(!s.length){
25439                 return;
25440             }
25441             
25442             Roo.get(_this.iframe.contentDocument.head).createChild({
25443                 tag : 'link',
25444                 rel : 'stylesheet',
25445                 type : 'text/css',
25446                 href : s
25447             });
25448         });
25449
25450         
25451     },
25452     
25453     removeStylesheets : function()
25454     {
25455         var _this = this;
25456         
25457         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25458             s.remove();
25459         });
25460     },
25461     
25462     setStyle : function(style)
25463     {
25464         Roo.get(this.iframe.contentDocument.head).createChild({
25465             tag : 'style',
25466             type : 'text/css',
25467             html : style
25468         });
25469
25470         return;
25471     }
25472     
25473     // hide stuff that is not compatible
25474     /**
25475      * @event blur
25476      * @hide
25477      */
25478     /**
25479      * @event change
25480      * @hide
25481      */
25482     /**
25483      * @event focus
25484      * @hide
25485      */
25486     /**
25487      * @event specialkey
25488      * @hide
25489      */
25490     /**
25491      * @cfg {String} fieldClass @hide
25492      */
25493     /**
25494      * @cfg {String} focusClass @hide
25495      */
25496     /**
25497      * @cfg {String} autoCreate @hide
25498      */
25499     /**
25500      * @cfg {String} inputType @hide
25501      */
25502     /**
25503      * @cfg {String} invalidClass @hide
25504      */
25505     /**
25506      * @cfg {String} invalidText @hide
25507      */
25508     /**
25509      * @cfg {String} msgFx @hide
25510      */
25511     /**
25512      * @cfg {String} validateOnBlur @hide
25513      */
25514 });
25515
25516 Roo.HtmlEditorCore.white = [
25517         'area', 'br', 'img', 'input', 'hr', 'wbr',
25518         
25519        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25520        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25521        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25522        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25523        'table',   'ul',         'xmp', 
25524        
25525        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25526       'thead',   'tr', 
25527      
25528       'dir', 'menu', 'ol', 'ul', 'dl',
25529        
25530       'embed',  'object'
25531 ];
25532
25533
25534 Roo.HtmlEditorCore.black = [
25535     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25536         'applet', // 
25537         'base',   'basefont', 'bgsound', 'blink',  'body', 
25538         'frame',  'frameset', 'head',    'html',   'ilayer', 
25539         'iframe', 'layer',  'link',     'meta',    'object',   
25540         'script', 'style' ,'title',  'xml' // clean later..
25541 ];
25542 Roo.HtmlEditorCore.clean = [
25543     'script', 'style', 'title', 'xml'
25544 ];
25545 Roo.HtmlEditorCore.remove = [
25546     'font'
25547 ];
25548 // attributes..
25549
25550 Roo.HtmlEditorCore.ablack = [
25551     'on'
25552 ];
25553     
25554 Roo.HtmlEditorCore.aclean = [ 
25555     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25556 ];
25557
25558 // protocols..
25559 Roo.HtmlEditorCore.pwhite= [
25560         'http',  'https',  'mailto'
25561 ];
25562
25563 // white listed style attributes.
25564 Roo.HtmlEditorCore.cwhite= [
25565       //  'text-align', /// default is to allow most things..
25566       
25567          
25568 //        'font-size'//??
25569 ];
25570
25571 // black listed style attributes.
25572 Roo.HtmlEditorCore.cblack= [
25573       //  'font-size' -- this can be set by the project 
25574 ];
25575
25576
25577 Roo.HtmlEditorCore.swapCodes   =[ 
25578     [    8211, "--" ], 
25579     [    8212, "--" ], 
25580     [    8216,  "'" ],  
25581     [    8217, "'" ],  
25582     [    8220, '"' ],  
25583     [    8221, '"' ],  
25584     [    8226, "*" ],  
25585     [    8230, "..." ]
25586 ]; 
25587
25588     /*
25589  * - LGPL
25590  *
25591  * HtmlEditor
25592  * 
25593  */
25594
25595 /**
25596  * @class Roo.bootstrap.HtmlEditor
25597  * @extends Roo.bootstrap.TextArea
25598  * Bootstrap HtmlEditor class
25599
25600  * @constructor
25601  * Create a new HtmlEditor
25602  * @param {Object} config The config object
25603  */
25604
25605 Roo.bootstrap.HtmlEditor = function(config){
25606     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25607     if (!this.toolbars) {
25608         this.toolbars = [];
25609     }
25610     
25611     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25612     this.addEvents({
25613             /**
25614              * @event initialize
25615              * Fires when the editor is fully initialized (including the iframe)
25616              * @param {HtmlEditor} this
25617              */
25618             initialize: true,
25619             /**
25620              * @event activate
25621              * Fires when the editor is first receives the focus. Any insertion must wait
25622              * until after this event.
25623              * @param {HtmlEditor} this
25624              */
25625             activate: true,
25626              /**
25627              * @event beforesync
25628              * Fires before the textarea is updated with content from the editor iframe. Return false
25629              * to cancel the sync.
25630              * @param {HtmlEditor} this
25631              * @param {String} html
25632              */
25633             beforesync: true,
25634              /**
25635              * @event beforepush
25636              * Fires before the iframe editor is updated with content from the textarea. Return false
25637              * to cancel the push.
25638              * @param {HtmlEditor} this
25639              * @param {String} html
25640              */
25641             beforepush: true,
25642              /**
25643              * @event sync
25644              * Fires when the textarea is updated with content from the editor iframe.
25645              * @param {HtmlEditor} this
25646              * @param {String} html
25647              */
25648             sync: true,
25649              /**
25650              * @event push
25651              * Fires when the iframe editor is updated with content from the textarea.
25652              * @param {HtmlEditor} this
25653              * @param {String} html
25654              */
25655             push: true,
25656              /**
25657              * @event editmodechange
25658              * Fires when the editor switches edit modes
25659              * @param {HtmlEditor} this
25660              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25661              */
25662             editmodechange: true,
25663             /**
25664              * @event editorevent
25665              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25666              * @param {HtmlEditor} this
25667              */
25668             editorevent: true,
25669             /**
25670              * @event firstfocus
25671              * Fires when on first focus - needed by toolbars..
25672              * @param {HtmlEditor} this
25673              */
25674             firstfocus: true,
25675             /**
25676              * @event autosave
25677              * Auto save the htmlEditor value as a file into Events
25678              * @param {HtmlEditor} this
25679              */
25680             autosave: true,
25681             /**
25682              * @event savedpreview
25683              * preview the saved version of htmlEditor
25684              * @param {HtmlEditor} this
25685              */
25686             savedpreview: true
25687         });
25688 };
25689
25690
25691 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25692     
25693     
25694       /**
25695      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25696      */
25697     toolbars : false,
25698     
25699      /**
25700     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25701     */
25702     btns : [],
25703    
25704      /**
25705      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25706      *                        Roo.resizable.
25707      */
25708     resizable : false,
25709      /**
25710      * @cfg {Number} height (in pixels)
25711      */   
25712     height: 300,
25713    /**
25714      * @cfg {Number} width (in pixels)
25715      */   
25716     width: false,
25717     
25718     /**
25719      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25720      * 
25721      */
25722     stylesheets: false,
25723     
25724     // id of frame..
25725     frameId: false,
25726     
25727     // private properties
25728     validationEvent : false,
25729     deferHeight: true,
25730     initialized : false,
25731     activated : false,
25732     
25733     onFocus : Roo.emptyFn,
25734     iframePad:3,
25735     hideMode:'offsets',
25736     
25737     tbContainer : false,
25738     
25739     bodyCls : '',
25740     
25741     toolbarContainer :function() {
25742         return this.wrap.select('.x-html-editor-tb',true).first();
25743     },
25744
25745     /**
25746      * Protected method that will not generally be called directly. It
25747      * is called when the editor creates its toolbar. Override this method if you need to
25748      * add custom toolbar buttons.
25749      * @param {HtmlEditor} editor
25750      */
25751     createToolbar : function(){
25752         Roo.log('renewing');
25753         Roo.log("create toolbars");
25754         
25755         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25756         this.toolbars[0].render(this.toolbarContainer());
25757         
25758         return;
25759         
25760 //        if (!editor.toolbars || !editor.toolbars.length) {
25761 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25762 //        }
25763 //        
25764 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25765 //            editor.toolbars[i] = Roo.factory(
25766 //                    typeof(editor.toolbars[i]) == 'string' ?
25767 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25768 //                Roo.bootstrap.HtmlEditor);
25769 //            editor.toolbars[i].init(editor);
25770 //        }
25771     },
25772
25773      
25774     // private
25775     onRender : function(ct, position)
25776     {
25777        // Roo.log("Call onRender: " + this.xtype);
25778         var _t = this;
25779         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25780       
25781         this.wrap = this.inputEl().wrap({
25782             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25783         });
25784         
25785         this.editorcore.onRender(ct, position);
25786          
25787         if (this.resizable) {
25788             this.resizeEl = new Roo.Resizable(this.wrap, {
25789                 pinned : true,
25790                 wrap: true,
25791                 dynamic : true,
25792                 minHeight : this.height,
25793                 height: this.height,
25794                 handles : this.resizable,
25795                 width: this.width,
25796                 listeners : {
25797                     resize : function(r, w, h) {
25798                         _t.onResize(w,h); // -something
25799                     }
25800                 }
25801             });
25802             
25803         }
25804         this.createToolbar(this);
25805        
25806         
25807         if(!this.width && this.resizable){
25808             this.setSize(this.wrap.getSize());
25809         }
25810         if (this.resizeEl) {
25811             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25812             // should trigger onReize..
25813         }
25814         
25815     },
25816
25817     // private
25818     onResize : function(w, h)
25819     {
25820         Roo.log('resize: ' +w + ',' + h );
25821         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25822         var ew = false;
25823         var eh = false;
25824         
25825         if(this.inputEl() ){
25826             if(typeof w == 'number'){
25827                 var aw = w - this.wrap.getFrameWidth('lr');
25828                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25829                 ew = aw;
25830             }
25831             if(typeof h == 'number'){
25832                  var tbh = -11;  // fixme it needs to tool bar size!
25833                 for (var i =0; i < this.toolbars.length;i++) {
25834                     // fixme - ask toolbars for heights?
25835                     tbh += this.toolbars[i].el.getHeight();
25836                     //if (this.toolbars[i].footer) {
25837                     //    tbh += this.toolbars[i].footer.el.getHeight();
25838                     //}
25839                 }
25840               
25841                 
25842                 
25843                 
25844                 
25845                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25846                 ah -= 5; // knock a few pixes off for look..
25847                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25848                 var eh = ah;
25849             }
25850         }
25851         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25852         this.editorcore.onResize(ew,eh);
25853         
25854     },
25855
25856     /**
25857      * Toggles the editor between standard and source edit mode.
25858      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25859      */
25860     toggleSourceEdit : function(sourceEditMode)
25861     {
25862         this.editorcore.toggleSourceEdit(sourceEditMode);
25863         
25864         if(this.editorcore.sourceEditMode){
25865             Roo.log('editor - showing textarea');
25866             
25867 //            Roo.log('in');
25868 //            Roo.log(this.syncValue());
25869             this.syncValue();
25870             this.inputEl().removeClass(['hide', 'x-hidden']);
25871             this.inputEl().dom.removeAttribute('tabIndex');
25872             this.inputEl().focus();
25873         }else{
25874             Roo.log('editor - hiding textarea');
25875 //            Roo.log('out')
25876 //            Roo.log(this.pushValue()); 
25877             this.pushValue();
25878             
25879             this.inputEl().addClass(['hide', 'x-hidden']);
25880             this.inputEl().dom.setAttribute('tabIndex', -1);
25881             //this.deferFocus();
25882         }
25883          
25884         if(this.resizable){
25885             this.setSize(this.wrap.getSize());
25886         }
25887         
25888         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25889     },
25890  
25891     // private (for BoxComponent)
25892     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25893
25894     // private (for BoxComponent)
25895     getResizeEl : function(){
25896         return this.wrap;
25897     },
25898
25899     // private (for BoxComponent)
25900     getPositionEl : function(){
25901         return this.wrap;
25902     },
25903
25904     // private
25905     initEvents : function(){
25906         this.originalValue = this.getValue();
25907     },
25908
25909 //    /**
25910 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25911 //     * @method
25912 //     */
25913 //    markInvalid : Roo.emptyFn,
25914 //    /**
25915 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25916 //     * @method
25917 //     */
25918 //    clearInvalid : Roo.emptyFn,
25919
25920     setValue : function(v){
25921         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25922         this.editorcore.pushValue();
25923     },
25924
25925      
25926     // private
25927     deferFocus : function(){
25928         this.focus.defer(10, this);
25929     },
25930
25931     // doc'ed in Field
25932     focus : function(){
25933         this.editorcore.focus();
25934         
25935     },
25936       
25937
25938     // private
25939     onDestroy : function(){
25940         
25941         
25942         
25943         if(this.rendered){
25944             
25945             for (var i =0; i < this.toolbars.length;i++) {
25946                 // fixme - ask toolbars for heights?
25947                 this.toolbars[i].onDestroy();
25948             }
25949             
25950             this.wrap.dom.innerHTML = '';
25951             this.wrap.remove();
25952         }
25953     },
25954
25955     // private
25956     onFirstFocus : function(){
25957         //Roo.log("onFirstFocus");
25958         this.editorcore.onFirstFocus();
25959          for (var i =0; i < this.toolbars.length;i++) {
25960             this.toolbars[i].onFirstFocus();
25961         }
25962         
25963     },
25964     
25965     // private
25966     syncValue : function()
25967     {   
25968         this.editorcore.syncValue();
25969     },
25970     
25971     pushValue : function()
25972     {   
25973         this.editorcore.pushValue();
25974     }
25975      
25976     
25977     // hide stuff that is not compatible
25978     /**
25979      * @event blur
25980      * @hide
25981      */
25982     /**
25983      * @event change
25984      * @hide
25985      */
25986     /**
25987      * @event focus
25988      * @hide
25989      */
25990     /**
25991      * @event specialkey
25992      * @hide
25993      */
25994     /**
25995      * @cfg {String} fieldClass @hide
25996      */
25997     /**
25998      * @cfg {String} focusClass @hide
25999      */
26000     /**
26001      * @cfg {String} autoCreate @hide
26002      */
26003     /**
26004      * @cfg {String} inputType @hide
26005      */
26006      
26007     /**
26008      * @cfg {String} invalidText @hide
26009      */
26010     /**
26011      * @cfg {String} msgFx @hide
26012      */
26013     /**
26014      * @cfg {String} validateOnBlur @hide
26015      */
26016 });
26017  
26018     
26019    
26020    
26021    
26022       
26023 Roo.namespace('Roo.bootstrap.htmleditor');
26024 /**
26025  * @class Roo.bootstrap.HtmlEditorToolbar1
26026  * Basic Toolbar
26027  * 
26028  * @example
26029  * Usage:
26030  *
26031  new Roo.bootstrap.HtmlEditor({
26032     ....
26033     toolbars : [
26034         new Roo.bootstrap.HtmlEditorToolbar1({
26035             disable : { fonts: 1 , format: 1, ..., ... , ...],
26036             btns : [ .... ]
26037         })
26038     }
26039      
26040  * 
26041  * @cfg {Object} disable List of elements to disable..
26042  * @cfg {Array} btns List of additional buttons.
26043  * 
26044  * 
26045  * NEEDS Extra CSS? 
26046  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26047  */
26048  
26049 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26050 {
26051     
26052     Roo.apply(this, config);
26053     
26054     // default disabled, based on 'good practice'..
26055     this.disable = this.disable || {};
26056     Roo.applyIf(this.disable, {
26057         fontSize : true,
26058         colors : true,
26059         specialElements : true
26060     });
26061     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26062     
26063     this.editor = config.editor;
26064     this.editorcore = config.editor.editorcore;
26065     
26066     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26067     
26068     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26069     // dont call parent... till later.
26070 }
26071 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26072      
26073     bar : true,
26074     
26075     editor : false,
26076     editorcore : false,
26077     
26078     
26079     formats : [
26080         "p" ,  
26081         "h1","h2","h3","h4","h5","h6", 
26082         "pre", "code", 
26083         "abbr", "acronym", "address", "cite", "samp", "var",
26084         'div','span'
26085     ],
26086     
26087     onRender : function(ct, position)
26088     {
26089        // Roo.log("Call onRender: " + this.xtype);
26090         
26091        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26092        Roo.log(this.el);
26093        this.el.dom.style.marginBottom = '0';
26094        var _this = this;
26095        var editorcore = this.editorcore;
26096        var editor= this.editor;
26097        
26098        var children = [];
26099        var btn = function(id,cmd , toggle, handler, html){
26100        
26101             var  event = toggle ? 'toggle' : 'click';
26102        
26103             var a = {
26104                 size : 'sm',
26105                 xtype: 'Button',
26106                 xns: Roo.bootstrap,
26107                 //glyphicon : id,
26108                 fa: id,
26109                 cmd : id || cmd,
26110                 enableToggle:toggle !== false,
26111                 html : html || '',
26112                 pressed : toggle ? false : null,
26113                 listeners : {}
26114             };
26115             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26116                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26117             };
26118             children.push(a);
26119             return a;
26120        }
26121        
26122     //    var cb_box = function...
26123         
26124         var style = {
26125                 xtype: 'Button',
26126                 size : 'sm',
26127                 xns: Roo.bootstrap,
26128                 fa : 'font',
26129                 //html : 'submit'
26130                 menu : {
26131                     xtype: 'Menu',
26132                     xns: Roo.bootstrap,
26133                     items:  []
26134                 }
26135         };
26136         Roo.each(this.formats, function(f) {
26137             style.menu.items.push({
26138                 xtype :'MenuItem',
26139                 xns: Roo.bootstrap,
26140                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26141                 tagname : f,
26142                 listeners : {
26143                     click : function()
26144                     {
26145                         editorcore.insertTag(this.tagname);
26146                         editor.focus();
26147                     }
26148                 }
26149                 
26150             });
26151         });
26152         children.push(style);   
26153         
26154         btn('bold',false,true);
26155         btn('italic',false,true);
26156         btn('align-left', 'justifyleft',true);
26157         btn('align-center', 'justifycenter',true);
26158         btn('align-right' , 'justifyright',true);
26159         btn('link', false, false, function(btn) {
26160             //Roo.log("create link?");
26161             var url = prompt(this.createLinkText, this.defaultLinkValue);
26162             if(url && url != 'http:/'+'/'){
26163                 this.editorcore.relayCmd('createlink', url);
26164             }
26165         }),
26166         btn('list','insertunorderedlist',true);
26167         btn('pencil', false,true, function(btn){
26168                 Roo.log(this);
26169                 this.toggleSourceEdit(btn.pressed);
26170         });
26171         
26172         if (this.editor.btns.length > 0) {
26173             for (var i = 0; i<this.editor.btns.length; i++) {
26174                 children.push(this.editor.btns[i]);
26175             }
26176         }
26177         
26178         /*
26179         var cog = {
26180                 xtype: 'Button',
26181                 size : 'sm',
26182                 xns: Roo.bootstrap,
26183                 glyphicon : 'cog',
26184                 //html : 'submit'
26185                 menu : {
26186                     xtype: 'Menu',
26187                     xns: Roo.bootstrap,
26188                     items:  []
26189                 }
26190         };
26191         
26192         cog.menu.items.push({
26193             xtype :'MenuItem',
26194             xns: Roo.bootstrap,
26195             html : Clean styles,
26196             tagname : f,
26197             listeners : {
26198                 click : function()
26199                 {
26200                     editorcore.insertTag(this.tagname);
26201                     editor.focus();
26202                 }
26203             }
26204             
26205         });
26206        */
26207         
26208          
26209        this.xtype = 'NavSimplebar';
26210         
26211         for(var i=0;i< children.length;i++) {
26212             
26213             this.buttons.add(this.addxtypeChild(children[i]));
26214             
26215         }
26216         
26217         editor.on('editorevent', this.updateToolbar, this);
26218     },
26219     onBtnClick : function(id)
26220     {
26221        this.editorcore.relayCmd(id);
26222        this.editorcore.focus();
26223     },
26224     
26225     /**
26226      * Protected method that will not generally be called directly. It triggers
26227      * a toolbar update by reading the markup state of the current selection in the editor.
26228      */
26229     updateToolbar: function(){
26230
26231         if(!this.editorcore.activated){
26232             this.editor.onFirstFocus(); // is this neeed?
26233             return;
26234         }
26235
26236         var btns = this.buttons; 
26237         var doc = this.editorcore.doc;
26238         btns.get('bold').setActive(doc.queryCommandState('bold'));
26239         btns.get('italic').setActive(doc.queryCommandState('italic'));
26240         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26241         
26242         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26243         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26244         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26245         
26246         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26247         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26248          /*
26249         
26250         var ans = this.editorcore.getAllAncestors();
26251         if (this.formatCombo) {
26252             
26253             
26254             var store = this.formatCombo.store;
26255             this.formatCombo.setValue("");
26256             for (var i =0; i < ans.length;i++) {
26257                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26258                     // select it..
26259                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26260                     break;
26261                 }
26262             }
26263         }
26264         
26265         
26266         
26267         // hides menus... - so this cant be on a menu...
26268         Roo.bootstrap.MenuMgr.hideAll();
26269         */
26270         Roo.bootstrap.MenuMgr.hideAll();
26271         //this.editorsyncValue();
26272     },
26273     onFirstFocus: function() {
26274         this.buttons.each(function(item){
26275            item.enable();
26276         });
26277     },
26278     toggleSourceEdit : function(sourceEditMode){
26279         
26280           
26281         if(sourceEditMode){
26282             Roo.log("disabling buttons");
26283            this.buttons.each( function(item){
26284                 if(item.cmd != 'pencil'){
26285                     item.disable();
26286                 }
26287             });
26288           
26289         }else{
26290             Roo.log("enabling buttons");
26291             if(this.editorcore.initialized){
26292                 this.buttons.each( function(item){
26293                     item.enable();
26294                 });
26295             }
26296             
26297         }
26298         Roo.log("calling toggole on editor");
26299         // tell the editor that it's been pressed..
26300         this.editor.toggleSourceEdit(sourceEditMode);
26301        
26302     }
26303 });
26304
26305
26306
26307
26308  
26309 /*
26310  * - LGPL
26311  */
26312
26313 /**
26314  * @class Roo.bootstrap.Markdown
26315  * @extends Roo.bootstrap.TextArea
26316  * Bootstrap Showdown editable area
26317  * @cfg {string} content
26318  * 
26319  * @constructor
26320  * Create a new Showdown
26321  */
26322
26323 Roo.bootstrap.Markdown = function(config){
26324     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26325    
26326 };
26327
26328 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26329     
26330     editing :false,
26331     
26332     initEvents : function()
26333     {
26334         
26335         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26336         this.markdownEl = this.el.createChild({
26337             cls : 'roo-markdown-area'
26338         });
26339         this.inputEl().addClass('d-none');
26340         if (this.getValue() == '') {
26341             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26342             
26343         } else {
26344             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26345         }
26346         this.markdownEl.on('click', this.toggleTextEdit, this);
26347         this.on('blur', this.toggleTextEdit, this);
26348         this.on('specialkey', this.resizeTextArea, this);
26349     },
26350     
26351     toggleTextEdit : function()
26352     {
26353         var sh = this.markdownEl.getHeight();
26354         this.inputEl().addClass('d-none');
26355         this.markdownEl.addClass('d-none');
26356         if (!this.editing) {
26357             // show editor?
26358             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26359             this.inputEl().removeClass('d-none');
26360             this.inputEl().focus();
26361             this.editing = true;
26362             return;
26363         }
26364         // show showdown...
26365         this.updateMarkdown();
26366         this.markdownEl.removeClass('d-none');
26367         this.editing = false;
26368         return;
26369     },
26370     updateMarkdown : function()
26371     {
26372         if (this.getValue() == '') {
26373             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26374             return;
26375         }
26376  
26377         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26378     },
26379     
26380     resizeTextArea: function () {
26381         
26382         var sh = 100;
26383         Roo.log([sh, this.getValue().split("\n").length * 30]);
26384         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26385     },
26386     setValue : function(val)
26387     {
26388         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26389         if (!this.editing) {
26390             this.updateMarkdown();
26391         }
26392         
26393     },
26394     focus : function()
26395     {
26396         if (!this.editing) {
26397             this.toggleTextEdit();
26398         }
26399         
26400     }
26401
26402
26403 });
26404 /**
26405  * @class Roo.bootstrap.Table.AbstractSelectionModel
26406  * @extends Roo.util.Observable
26407  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26408  * implemented by descendant classes.  This class should not be directly instantiated.
26409  * @constructor
26410  */
26411 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26412     this.locked = false;
26413     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26414 };
26415
26416
26417 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26418     /** @ignore Called by the grid automatically. Do not call directly. */
26419     init : function(grid){
26420         this.grid = grid;
26421         this.initEvents();
26422     },
26423
26424     /**
26425      * Locks the selections.
26426      */
26427     lock : function(){
26428         this.locked = true;
26429     },
26430
26431     /**
26432      * Unlocks the selections.
26433      */
26434     unlock : function(){
26435         this.locked = false;
26436     },
26437
26438     /**
26439      * Returns true if the selections are locked.
26440      * @return {Boolean}
26441      */
26442     isLocked : function(){
26443         return this.locked;
26444     },
26445     
26446     
26447     initEvents : function ()
26448     {
26449         
26450     }
26451 });
26452 /**
26453  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26454  * @class Roo.bootstrap.Table.RowSelectionModel
26455  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26456  * It supports multiple selections and keyboard selection/navigation. 
26457  * @constructor
26458  * @param {Object} config
26459  */
26460
26461 Roo.bootstrap.Table.RowSelectionModel = function(config){
26462     Roo.apply(this, config);
26463     this.selections = new Roo.util.MixedCollection(false, function(o){
26464         return o.id;
26465     });
26466
26467     this.last = false;
26468     this.lastActive = false;
26469
26470     this.addEvents({
26471         /**
26472              * @event selectionchange
26473              * Fires when the selection changes
26474              * @param {SelectionModel} this
26475              */
26476             "selectionchange" : true,
26477         /**
26478              * @event afterselectionchange
26479              * Fires after the selection changes (eg. by key press or clicking)
26480              * @param {SelectionModel} this
26481              */
26482             "afterselectionchange" : true,
26483         /**
26484              * @event beforerowselect
26485              * Fires when a row is selected being selected, return false to cancel.
26486              * @param {SelectionModel} this
26487              * @param {Number} rowIndex The selected index
26488              * @param {Boolean} keepExisting False if other selections will be cleared
26489              */
26490             "beforerowselect" : true,
26491         /**
26492              * @event rowselect
26493              * Fires when a row is selected.
26494              * @param {SelectionModel} this
26495              * @param {Number} rowIndex The selected index
26496              * @param {Roo.data.Record} r The record
26497              */
26498             "rowselect" : true,
26499         /**
26500              * @event rowdeselect
26501              * Fires when a row is deselected.
26502              * @param {SelectionModel} this
26503              * @param {Number} rowIndex The selected index
26504              */
26505         "rowdeselect" : true
26506     });
26507     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26508     this.locked = false;
26509  };
26510
26511 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26512     /**
26513      * @cfg {Boolean} singleSelect
26514      * True to allow selection of only one row at a time (defaults to false)
26515      */
26516     singleSelect : false,
26517
26518     // private
26519     initEvents : function()
26520     {
26521
26522         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26523         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26524         //}else{ // allow click to work like normal
26525          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26526         //}
26527         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26528         this.grid.on("rowclick", this.handleMouseDown, this);
26529         
26530         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26531             "up" : function(e){
26532                 if(!e.shiftKey){
26533                     this.selectPrevious(e.shiftKey);
26534                 }else if(this.last !== false && this.lastActive !== false){
26535                     var last = this.last;
26536                     this.selectRange(this.last,  this.lastActive-1);
26537                     this.grid.getView().focusRow(this.lastActive);
26538                     if(last !== false){
26539                         this.last = last;
26540                     }
26541                 }else{
26542                     this.selectFirstRow();
26543                 }
26544                 this.fireEvent("afterselectionchange", this);
26545             },
26546             "down" : function(e){
26547                 if(!e.shiftKey){
26548                     this.selectNext(e.shiftKey);
26549                 }else if(this.last !== false && this.lastActive !== false){
26550                     var last = this.last;
26551                     this.selectRange(this.last,  this.lastActive+1);
26552                     this.grid.getView().focusRow(this.lastActive);
26553                     if(last !== false){
26554                         this.last = last;
26555                     }
26556                 }else{
26557                     this.selectFirstRow();
26558                 }
26559                 this.fireEvent("afterselectionchange", this);
26560             },
26561             scope: this
26562         });
26563         this.grid.store.on('load', function(){
26564             this.selections.clear();
26565         },this);
26566         /*
26567         var view = this.grid.view;
26568         view.on("refresh", this.onRefresh, this);
26569         view.on("rowupdated", this.onRowUpdated, this);
26570         view.on("rowremoved", this.onRemove, this);
26571         */
26572     },
26573
26574     // private
26575     onRefresh : function()
26576     {
26577         var ds = this.grid.store, i, v = this.grid.view;
26578         var s = this.selections;
26579         s.each(function(r){
26580             if((i = ds.indexOfId(r.id)) != -1){
26581                 v.onRowSelect(i);
26582             }else{
26583                 s.remove(r);
26584             }
26585         });
26586     },
26587
26588     // private
26589     onRemove : function(v, index, r){
26590         this.selections.remove(r);
26591     },
26592
26593     // private
26594     onRowUpdated : function(v, index, r){
26595         if(this.isSelected(r)){
26596             v.onRowSelect(index);
26597         }
26598     },
26599
26600     /**
26601      * Select records.
26602      * @param {Array} records The records to select
26603      * @param {Boolean} keepExisting (optional) True to keep existing selections
26604      */
26605     selectRecords : function(records, keepExisting)
26606     {
26607         if(!keepExisting){
26608             this.clearSelections();
26609         }
26610             var ds = this.grid.store;
26611         for(var i = 0, len = records.length; i < len; i++){
26612             this.selectRow(ds.indexOf(records[i]), true);
26613         }
26614     },
26615
26616     /**
26617      * Gets the number of selected rows.
26618      * @return {Number}
26619      */
26620     getCount : function(){
26621         return this.selections.length;
26622     },
26623
26624     /**
26625      * Selects the first row in the grid.
26626      */
26627     selectFirstRow : function(){
26628         this.selectRow(0);
26629     },
26630
26631     /**
26632      * Select the last row.
26633      * @param {Boolean} keepExisting (optional) True to keep existing selections
26634      */
26635     selectLastRow : function(keepExisting){
26636         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26637         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26638     },
26639
26640     /**
26641      * Selects the row immediately following the last selected row.
26642      * @param {Boolean} keepExisting (optional) True to keep existing selections
26643      */
26644     selectNext : function(keepExisting)
26645     {
26646             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26647             this.selectRow(this.last+1, keepExisting);
26648             this.grid.getView().focusRow(this.last);
26649         }
26650     },
26651
26652     /**
26653      * Selects the row that precedes the last selected row.
26654      * @param {Boolean} keepExisting (optional) True to keep existing selections
26655      */
26656     selectPrevious : function(keepExisting){
26657         if(this.last){
26658             this.selectRow(this.last-1, keepExisting);
26659             this.grid.getView().focusRow(this.last);
26660         }
26661     },
26662
26663     /**
26664      * Returns the selected records
26665      * @return {Array} Array of selected records
26666      */
26667     getSelections : function(){
26668         return [].concat(this.selections.items);
26669     },
26670
26671     /**
26672      * Returns the first selected record.
26673      * @return {Record}
26674      */
26675     getSelected : function(){
26676         return this.selections.itemAt(0);
26677     },
26678
26679
26680     /**
26681      * Clears all selections.
26682      */
26683     clearSelections : function(fast)
26684     {
26685         if(this.locked) {
26686             return;
26687         }
26688         if(fast !== true){
26689                 var ds = this.grid.store;
26690             var s = this.selections;
26691             s.each(function(r){
26692                 this.deselectRow(ds.indexOfId(r.id));
26693             }, this);
26694             s.clear();
26695         }else{
26696             this.selections.clear();
26697         }
26698         this.last = false;
26699     },
26700
26701
26702     /**
26703      * Selects all rows.
26704      */
26705     selectAll : function(){
26706         if(this.locked) {
26707             return;
26708         }
26709         this.selections.clear();
26710         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26711             this.selectRow(i, true);
26712         }
26713     },
26714
26715     /**
26716      * Returns True if there is a selection.
26717      * @return {Boolean}
26718      */
26719     hasSelection : function(){
26720         return this.selections.length > 0;
26721     },
26722
26723     /**
26724      * Returns True if the specified row is selected.
26725      * @param {Number/Record} record The record or index of the record to check
26726      * @return {Boolean}
26727      */
26728     isSelected : function(index){
26729             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26730         return (r && this.selections.key(r.id) ? true : false);
26731     },
26732
26733     /**
26734      * Returns True if the specified record id is selected.
26735      * @param {String} id The id of record to check
26736      * @return {Boolean}
26737      */
26738     isIdSelected : function(id){
26739         return (this.selections.key(id) ? true : false);
26740     },
26741
26742
26743     // private
26744     handleMouseDBClick : function(e, t){
26745         
26746     },
26747     // private
26748     handleMouseDown : function(e, t)
26749     {
26750             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26751         if(this.isLocked() || rowIndex < 0 ){
26752             return;
26753         };
26754         if(e.shiftKey && this.last !== false){
26755             var last = this.last;
26756             this.selectRange(last, rowIndex, e.ctrlKey);
26757             this.last = last; // reset the last
26758             t.focus();
26759     
26760         }else{
26761             var isSelected = this.isSelected(rowIndex);
26762             //Roo.log("select row:" + rowIndex);
26763             if(isSelected){
26764                 this.deselectRow(rowIndex);
26765             } else {
26766                         this.selectRow(rowIndex, true);
26767             }
26768     
26769             /*
26770                 if(e.button !== 0 && isSelected){
26771                 alert('rowIndex 2: ' + rowIndex);
26772                     view.focusRow(rowIndex);
26773                 }else if(e.ctrlKey && isSelected){
26774                     this.deselectRow(rowIndex);
26775                 }else if(!isSelected){
26776                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26777                     view.focusRow(rowIndex);
26778                 }
26779             */
26780         }
26781         this.fireEvent("afterselectionchange", this);
26782     },
26783     // private
26784     handleDragableRowClick :  function(grid, rowIndex, e) 
26785     {
26786         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26787             this.selectRow(rowIndex, false);
26788             grid.view.focusRow(rowIndex);
26789              this.fireEvent("afterselectionchange", this);
26790         }
26791     },
26792     
26793     /**
26794      * Selects multiple rows.
26795      * @param {Array} rows Array of the indexes of the row to select
26796      * @param {Boolean} keepExisting (optional) True to keep existing selections
26797      */
26798     selectRows : function(rows, keepExisting){
26799         if(!keepExisting){
26800             this.clearSelections();
26801         }
26802         for(var i = 0, len = rows.length; i < len; i++){
26803             this.selectRow(rows[i], true);
26804         }
26805     },
26806
26807     /**
26808      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26809      * @param {Number} startRow The index of the first row in the range
26810      * @param {Number} endRow The index of the last row in the range
26811      * @param {Boolean} keepExisting (optional) True to retain existing selections
26812      */
26813     selectRange : function(startRow, endRow, keepExisting){
26814         if(this.locked) {
26815             return;
26816         }
26817         if(!keepExisting){
26818             this.clearSelections();
26819         }
26820         if(startRow <= endRow){
26821             for(var i = startRow; i <= endRow; i++){
26822                 this.selectRow(i, true);
26823             }
26824         }else{
26825             for(var i = startRow; i >= endRow; i--){
26826                 this.selectRow(i, true);
26827             }
26828         }
26829     },
26830
26831     /**
26832      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26833      * @param {Number} startRow The index of the first row in the range
26834      * @param {Number} endRow The index of the last row in the range
26835      */
26836     deselectRange : function(startRow, endRow, preventViewNotify){
26837         if(this.locked) {
26838             return;
26839         }
26840         for(var i = startRow; i <= endRow; i++){
26841             this.deselectRow(i, preventViewNotify);
26842         }
26843     },
26844
26845     /**
26846      * Selects a row.
26847      * @param {Number} row The index of the row to select
26848      * @param {Boolean} keepExisting (optional) True to keep existing selections
26849      */
26850     selectRow : function(index, keepExisting, preventViewNotify)
26851     {
26852             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26853             return;
26854         }
26855         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26856             if(!keepExisting || this.singleSelect){
26857                 this.clearSelections();
26858             }
26859             
26860             var r = this.grid.store.getAt(index);
26861             //console.log('selectRow - record id :' + r.id);
26862             
26863             this.selections.add(r);
26864             this.last = this.lastActive = index;
26865             if(!preventViewNotify){
26866                 var proxy = new Roo.Element(
26867                                 this.grid.getRowDom(index)
26868                 );
26869                 proxy.addClass('bg-info info');
26870             }
26871             this.fireEvent("rowselect", this, index, r);
26872             this.fireEvent("selectionchange", this);
26873         }
26874     },
26875
26876     /**
26877      * Deselects a row.
26878      * @param {Number} row The index of the row to deselect
26879      */
26880     deselectRow : function(index, preventViewNotify)
26881     {
26882         if(this.locked) {
26883             return;
26884         }
26885         if(this.last == index){
26886             this.last = false;
26887         }
26888         if(this.lastActive == index){
26889             this.lastActive = false;
26890         }
26891         
26892         var r = this.grid.store.getAt(index);
26893         if (!r) {
26894             return;
26895         }
26896         
26897         this.selections.remove(r);
26898         //.console.log('deselectRow - record id :' + r.id);
26899         if(!preventViewNotify){
26900         
26901             var proxy = new Roo.Element(
26902                 this.grid.getRowDom(index)
26903             );
26904             proxy.removeClass('bg-info info');
26905         }
26906         this.fireEvent("rowdeselect", this, index);
26907         this.fireEvent("selectionchange", this);
26908     },
26909
26910     // private
26911     restoreLast : function(){
26912         if(this._last){
26913             this.last = this._last;
26914         }
26915     },
26916
26917     // private
26918     acceptsNav : function(row, col, cm){
26919         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26920     },
26921
26922     // private
26923     onEditorKey : function(field, e){
26924         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26925         if(k == e.TAB){
26926             e.stopEvent();
26927             ed.completeEdit();
26928             if(e.shiftKey){
26929                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26930             }else{
26931                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26932             }
26933         }else if(k == e.ENTER && !e.ctrlKey){
26934             e.stopEvent();
26935             ed.completeEdit();
26936             if(e.shiftKey){
26937                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26938             }else{
26939                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26940             }
26941         }else if(k == e.ESC){
26942             ed.cancelEdit();
26943         }
26944         if(newCell){
26945             g.startEditing(newCell[0], newCell[1]);
26946         }
26947     }
26948 });
26949 /*
26950  * Based on:
26951  * Ext JS Library 1.1.1
26952  * Copyright(c) 2006-2007, Ext JS, LLC.
26953  *
26954  * Originally Released Under LGPL - original licence link has changed is not relivant.
26955  *
26956  * Fork - LGPL
26957  * <script type="text/javascript">
26958  */
26959  
26960 /**
26961  * @class Roo.bootstrap.PagingToolbar
26962  * @extends Roo.bootstrap.NavSimplebar
26963  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26964  * @constructor
26965  * Create a new PagingToolbar
26966  * @param {Object} config The config object
26967  * @param {Roo.data.Store} store
26968  */
26969 Roo.bootstrap.PagingToolbar = function(config)
26970 {
26971     // old args format still supported... - xtype is prefered..
26972         // created from xtype...
26973     
26974     this.ds = config.dataSource;
26975     
26976     if (config.store && !this.ds) {
26977         this.store= Roo.factory(config.store, Roo.data);
26978         this.ds = this.store;
26979         this.ds.xmodule = this.xmodule || false;
26980     }
26981     
26982     this.toolbarItems = [];
26983     if (config.items) {
26984         this.toolbarItems = config.items;
26985     }
26986     
26987     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26988     
26989     this.cursor = 0;
26990     
26991     if (this.ds) { 
26992         this.bind(this.ds);
26993     }
26994     
26995     if (Roo.bootstrap.version == 4) {
26996         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26997     } else {
26998         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26999     }
27000     
27001 };
27002
27003 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27004     /**
27005      * @cfg {Roo.data.Store} dataSource
27006      * The underlying data store providing the paged data
27007      */
27008     /**
27009      * @cfg {String/HTMLElement/Element} container
27010      * container The id or element that will contain the toolbar
27011      */
27012     /**
27013      * @cfg {Boolean} displayInfo
27014      * True to display the displayMsg (defaults to false)
27015      */
27016     /**
27017      * @cfg {Number} pageSize
27018      * The number of records to display per page (defaults to 20)
27019      */
27020     pageSize: 20,
27021     /**
27022      * @cfg {String} displayMsg
27023      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27024      */
27025     displayMsg : 'Displaying {0} - {1} of {2}',
27026     /**
27027      * @cfg {String} emptyMsg
27028      * The message to display when no records are found (defaults to "No data to display")
27029      */
27030     emptyMsg : 'No data to display',
27031     /**
27032      * Customizable piece of the default paging text (defaults to "Page")
27033      * @type String
27034      */
27035     beforePageText : "Page",
27036     /**
27037      * Customizable piece of the default paging text (defaults to "of %0")
27038      * @type String
27039      */
27040     afterPageText : "of {0}",
27041     /**
27042      * Customizable piece of the default paging text (defaults to "First Page")
27043      * @type String
27044      */
27045     firstText : "First Page",
27046     /**
27047      * Customizable piece of the default paging text (defaults to "Previous Page")
27048      * @type String
27049      */
27050     prevText : "Previous Page",
27051     /**
27052      * Customizable piece of the default paging text (defaults to "Next Page")
27053      * @type String
27054      */
27055     nextText : "Next Page",
27056     /**
27057      * Customizable piece of the default paging text (defaults to "Last Page")
27058      * @type String
27059      */
27060     lastText : "Last Page",
27061     /**
27062      * Customizable piece of the default paging text (defaults to "Refresh")
27063      * @type String
27064      */
27065     refreshText : "Refresh",
27066
27067     buttons : false,
27068     // private
27069     onRender : function(ct, position) 
27070     {
27071         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27072         this.navgroup.parentId = this.id;
27073         this.navgroup.onRender(this.el, null);
27074         // add the buttons to the navgroup
27075         
27076         if(this.displayInfo){
27077             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27078             this.displayEl = this.el.select('.x-paging-info', true).first();
27079 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27080 //            this.displayEl = navel.el.select('span',true).first();
27081         }
27082         
27083         var _this = this;
27084         
27085         if(this.buttons){
27086             Roo.each(_this.buttons, function(e){ // this might need to use render????
27087                Roo.factory(e).render(_this.el);
27088             });
27089         }
27090             
27091         Roo.each(_this.toolbarItems, function(e) {
27092             _this.navgroup.addItem(e);
27093         });
27094         
27095         
27096         this.first = this.navgroup.addItem({
27097             tooltip: this.firstText,
27098             cls: "prev btn-outline-secondary",
27099             html : ' <i class="fa fa-step-backward"></i>',
27100             disabled: true,
27101             preventDefault: true,
27102             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27103         });
27104         
27105         this.prev =  this.navgroup.addItem({
27106             tooltip: this.prevText,
27107             cls: "prev btn-outline-secondary",
27108             html : ' <i class="fa fa-backward"></i>',
27109             disabled: true,
27110             preventDefault: true,
27111             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27112         });
27113     //this.addSeparator();
27114         
27115         
27116         var field = this.navgroup.addItem( {
27117             tagtype : 'span',
27118             cls : 'x-paging-position  btn-outline-secondary',
27119              disabled: true,
27120             html : this.beforePageText  +
27121                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27122                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27123          } ); //?? escaped?
27124         
27125         this.field = field.el.select('input', true).first();
27126         this.field.on("keydown", this.onPagingKeydown, this);
27127         this.field.on("focus", function(){this.dom.select();});
27128     
27129     
27130         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27131         //this.field.setHeight(18);
27132         //this.addSeparator();
27133         this.next = this.navgroup.addItem({
27134             tooltip: this.nextText,
27135             cls: "next btn-outline-secondary",
27136             html : ' <i class="fa fa-forward"></i>',
27137             disabled: true,
27138             preventDefault: true,
27139             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27140         });
27141         this.last = this.navgroup.addItem({
27142             tooltip: this.lastText,
27143             html : ' <i class="fa fa-step-forward"></i>',
27144             cls: "next btn-outline-secondary",
27145             disabled: true,
27146             preventDefault: true,
27147             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27148         });
27149     //this.addSeparator();
27150         this.loading = this.navgroup.addItem({
27151             tooltip: this.refreshText,
27152             cls: "btn-outline-secondary",
27153             html : ' <i class="fa fa-refresh"></i>',
27154             preventDefault: true,
27155             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27156         });
27157         
27158     },
27159
27160     // private
27161     updateInfo : function(){
27162         if(this.displayEl){
27163             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27164             var msg = count == 0 ?
27165                 this.emptyMsg :
27166                 String.format(
27167                     this.displayMsg,
27168                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27169                 );
27170             this.displayEl.update(msg);
27171         }
27172     },
27173
27174     // private
27175     onLoad : function(ds, r, o)
27176     {
27177         this.cursor = o.params.start ? o.params.start : 0;
27178         
27179         var d = this.getPageData(),
27180             ap = d.activePage,
27181             ps = d.pages;
27182         
27183         
27184         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27185         this.field.dom.value = ap;
27186         this.first.setDisabled(ap == 1);
27187         this.prev.setDisabled(ap == 1);
27188         this.next.setDisabled(ap == ps);
27189         this.last.setDisabled(ap == ps);
27190         this.loading.enable();
27191         this.updateInfo();
27192     },
27193
27194     // private
27195     getPageData : function(){
27196         var total = this.ds.getTotalCount();
27197         return {
27198             total : total,
27199             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27200             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27201         };
27202     },
27203
27204     // private
27205     onLoadError : function(){
27206         this.loading.enable();
27207     },
27208
27209     // private
27210     onPagingKeydown : function(e){
27211         var k = e.getKey();
27212         var d = this.getPageData();
27213         if(k == e.RETURN){
27214             var v = this.field.dom.value, pageNum;
27215             if(!v || isNaN(pageNum = parseInt(v, 10))){
27216                 this.field.dom.value = d.activePage;
27217                 return;
27218             }
27219             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27220             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27221             e.stopEvent();
27222         }
27223         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))
27224         {
27225           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27226           this.field.dom.value = pageNum;
27227           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27228           e.stopEvent();
27229         }
27230         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27231         {
27232           var v = this.field.dom.value, pageNum; 
27233           var increment = (e.shiftKey) ? 10 : 1;
27234           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27235                 increment *= -1;
27236           }
27237           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27238             this.field.dom.value = d.activePage;
27239             return;
27240           }
27241           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27242           {
27243             this.field.dom.value = parseInt(v, 10) + increment;
27244             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27245             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27246           }
27247           e.stopEvent();
27248         }
27249     },
27250
27251     // private
27252     beforeLoad : function(){
27253         if(this.loading){
27254             this.loading.disable();
27255         }
27256     },
27257
27258     // private
27259     onClick : function(which){
27260         
27261         var ds = this.ds;
27262         if (!ds) {
27263             return;
27264         }
27265         
27266         switch(which){
27267             case "first":
27268                 ds.load({params:{start: 0, limit: this.pageSize}});
27269             break;
27270             case "prev":
27271                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27272             break;
27273             case "next":
27274                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27275             break;
27276             case "last":
27277                 var total = ds.getTotalCount();
27278                 var extra = total % this.pageSize;
27279                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27280                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27281             break;
27282             case "refresh":
27283                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27284             break;
27285         }
27286     },
27287
27288     /**
27289      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27290      * @param {Roo.data.Store} store The data store to unbind
27291      */
27292     unbind : function(ds){
27293         ds.un("beforeload", this.beforeLoad, this);
27294         ds.un("load", this.onLoad, this);
27295         ds.un("loadexception", this.onLoadError, this);
27296         ds.un("remove", this.updateInfo, this);
27297         ds.un("add", this.updateInfo, this);
27298         this.ds = undefined;
27299     },
27300
27301     /**
27302      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27303      * @param {Roo.data.Store} store The data store to bind
27304      */
27305     bind : function(ds){
27306         ds.on("beforeload", this.beforeLoad, this);
27307         ds.on("load", this.onLoad, this);
27308         ds.on("loadexception", this.onLoadError, this);
27309         ds.on("remove", this.updateInfo, this);
27310         ds.on("add", this.updateInfo, this);
27311         this.ds = ds;
27312     }
27313 });/*
27314  * - LGPL
27315  *
27316  * element
27317  * 
27318  */
27319
27320 /**
27321  * @class Roo.bootstrap.MessageBar
27322  * @extends Roo.bootstrap.Component
27323  * Bootstrap MessageBar class
27324  * @cfg {String} html contents of the MessageBar
27325  * @cfg {String} weight (info | success | warning | danger) default info
27326  * @cfg {String} beforeClass insert the bar before the given class
27327  * @cfg {Boolean} closable (true | false) default false
27328  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27329  * 
27330  * @constructor
27331  * Create a new Element
27332  * @param {Object} config The config object
27333  */
27334
27335 Roo.bootstrap.MessageBar = function(config){
27336     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27337 };
27338
27339 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27340     
27341     html: '',
27342     weight: 'info',
27343     closable: false,
27344     fixed: false,
27345     beforeClass: 'bootstrap-sticky-wrap',
27346     
27347     getAutoCreate : function(){
27348         
27349         var cfg = {
27350             tag: 'div',
27351             cls: 'alert alert-dismissable alert-' + this.weight,
27352             cn: [
27353                 {
27354                     tag: 'span',
27355                     cls: 'message',
27356                     html: this.html || ''
27357                 }
27358             ]
27359         };
27360         
27361         if(this.fixed){
27362             cfg.cls += ' alert-messages-fixed';
27363         }
27364         
27365         if(this.closable){
27366             cfg.cn.push({
27367                 tag: 'button',
27368                 cls: 'close',
27369                 html: 'x'
27370             });
27371         }
27372         
27373         return cfg;
27374     },
27375     
27376     onRender : function(ct, position)
27377     {
27378         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27379         
27380         if(!this.el){
27381             var cfg = Roo.apply({},  this.getAutoCreate());
27382             cfg.id = Roo.id();
27383             
27384             if (this.cls) {
27385                 cfg.cls += ' ' + this.cls;
27386             }
27387             if (this.style) {
27388                 cfg.style = this.style;
27389             }
27390             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27391             
27392             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27393         }
27394         
27395         this.el.select('>button.close').on('click', this.hide, this);
27396         
27397     },
27398     
27399     show : function()
27400     {
27401         if (!this.rendered) {
27402             this.render();
27403         }
27404         
27405         this.el.show();
27406         
27407         this.fireEvent('show', this);
27408         
27409     },
27410     
27411     hide : function()
27412     {
27413         if (!this.rendered) {
27414             this.render();
27415         }
27416         
27417         this.el.hide();
27418         
27419         this.fireEvent('hide', this);
27420     },
27421     
27422     update : function()
27423     {
27424 //        var e = this.el.dom.firstChild;
27425 //        
27426 //        if(this.closable){
27427 //            e = e.nextSibling;
27428 //        }
27429 //        
27430 //        e.data = this.html || '';
27431
27432         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27433     }
27434    
27435 });
27436
27437  
27438
27439      /*
27440  * - LGPL
27441  *
27442  * Graph
27443  * 
27444  */
27445
27446
27447 /**
27448  * @class Roo.bootstrap.Graph
27449  * @extends Roo.bootstrap.Component
27450  * Bootstrap Graph class
27451 > Prameters
27452  -sm {number} sm 4
27453  -md {number} md 5
27454  @cfg {String} graphtype  bar | vbar | pie
27455  @cfg {number} g_x coodinator | centre x (pie)
27456  @cfg {number} g_y coodinator | centre y (pie)
27457  @cfg {number} g_r radius (pie)
27458  @cfg {number} g_height height of the chart (respected by all elements in the set)
27459  @cfg {number} g_width width of the chart (respected by all elements in the set)
27460  @cfg {Object} title The title of the chart
27461     
27462  -{Array}  values
27463  -opts (object) options for the chart 
27464      o {
27465      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27466      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27467      o vgutter (number)
27468      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.
27469      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27470      o to
27471      o stretch (boolean)
27472      o }
27473  -opts (object) options for the pie
27474      o{
27475      o cut
27476      o startAngle (number)
27477      o endAngle (number)
27478      } 
27479  *
27480  * @constructor
27481  * Create a new Input
27482  * @param {Object} config The config object
27483  */
27484
27485 Roo.bootstrap.Graph = function(config){
27486     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27487     
27488     this.addEvents({
27489         // img events
27490         /**
27491          * @event click
27492          * The img click event for the img.
27493          * @param {Roo.EventObject} e
27494          */
27495         "click" : true
27496     });
27497 };
27498
27499 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27500     
27501     sm: 4,
27502     md: 5,
27503     graphtype: 'bar',
27504     g_height: 250,
27505     g_width: 400,
27506     g_x: 50,
27507     g_y: 50,
27508     g_r: 30,
27509     opts:{
27510         //g_colors: this.colors,
27511         g_type: 'soft',
27512         g_gutter: '20%'
27513
27514     },
27515     title : false,
27516
27517     getAutoCreate : function(){
27518         
27519         var cfg = {
27520             tag: 'div',
27521             html : null
27522         };
27523         
27524         
27525         return  cfg;
27526     },
27527
27528     onRender : function(ct,position){
27529         
27530         
27531         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27532         
27533         if (typeof(Raphael) == 'undefined') {
27534             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27535             return;
27536         }
27537         
27538         this.raphael = Raphael(this.el.dom);
27539         
27540                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27541                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27542                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27543                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27544                 /*
27545                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27546                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27547                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27548                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27549                 
27550                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27551                 r.barchart(330, 10, 300, 220, data1);
27552                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27553                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27554                 */
27555                 
27556                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27557                 // r.barchart(30, 30, 560, 250,  xdata, {
27558                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27559                 //     axis : "0 0 1 1",
27560                 //     axisxlabels :  xdata
27561                 //     //yvalues : cols,
27562                    
27563                 // });
27564 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27565 //        
27566 //        this.load(null,xdata,{
27567 //                axis : "0 0 1 1",
27568 //                axisxlabels :  xdata
27569 //                });
27570
27571     },
27572
27573     load : function(graphtype,xdata,opts)
27574     {
27575         this.raphael.clear();
27576         if(!graphtype) {
27577             graphtype = this.graphtype;
27578         }
27579         if(!opts){
27580             opts = this.opts;
27581         }
27582         var r = this.raphael,
27583             fin = function () {
27584                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27585             },
27586             fout = function () {
27587                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27588             },
27589             pfin = function() {
27590                 this.sector.stop();
27591                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27592
27593                 if (this.label) {
27594                     this.label[0].stop();
27595                     this.label[0].attr({ r: 7.5 });
27596                     this.label[1].attr({ "font-weight": 800 });
27597                 }
27598             },
27599             pfout = function() {
27600                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27601
27602                 if (this.label) {
27603                     this.label[0].animate({ r: 5 }, 500, "bounce");
27604                     this.label[1].attr({ "font-weight": 400 });
27605                 }
27606             };
27607
27608         switch(graphtype){
27609             case 'bar':
27610                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27611                 break;
27612             case 'hbar':
27613                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27614                 break;
27615             case 'pie':
27616 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27617 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27618 //            
27619                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27620                 
27621                 break;
27622
27623         }
27624         
27625         if(this.title){
27626             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27627         }
27628         
27629     },
27630     
27631     setTitle: function(o)
27632     {
27633         this.title = o;
27634     },
27635     
27636     initEvents: function() {
27637         
27638         if(!this.href){
27639             this.el.on('click', this.onClick, this);
27640         }
27641     },
27642     
27643     onClick : function(e)
27644     {
27645         Roo.log('img onclick');
27646         this.fireEvent('click', this, e);
27647     }
27648    
27649 });
27650
27651  
27652 /*
27653  * - LGPL
27654  *
27655  * numberBox
27656  * 
27657  */
27658 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27659
27660 /**
27661  * @class Roo.bootstrap.dash.NumberBox
27662  * @extends Roo.bootstrap.Component
27663  * Bootstrap NumberBox class
27664  * @cfg {String} headline Box headline
27665  * @cfg {String} content Box content
27666  * @cfg {String} icon Box icon
27667  * @cfg {String} footer Footer text
27668  * @cfg {String} fhref Footer href
27669  * 
27670  * @constructor
27671  * Create a new NumberBox
27672  * @param {Object} config The config object
27673  */
27674
27675
27676 Roo.bootstrap.dash.NumberBox = function(config){
27677     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27678     
27679 };
27680
27681 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27682     
27683     headline : '',
27684     content : '',
27685     icon : '',
27686     footer : '',
27687     fhref : '',
27688     ficon : '',
27689     
27690     getAutoCreate : function(){
27691         
27692         var cfg = {
27693             tag : 'div',
27694             cls : 'small-box ',
27695             cn : [
27696                 {
27697                     tag : 'div',
27698                     cls : 'inner',
27699                     cn :[
27700                         {
27701                             tag : 'h3',
27702                             cls : 'roo-headline',
27703                             html : this.headline
27704                         },
27705                         {
27706                             tag : 'p',
27707                             cls : 'roo-content',
27708                             html : this.content
27709                         }
27710                     ]
27711                 }
27712             ]
27713         };
27714         
27715         if(this.icon){
27716             cfg.cn.push({
27717                 tag : 'div',
27718                 cls : 'icon',
27719                 cn :[
27720                     {
27721                         tag : 'i',
27722                         cls : 'ion ' + this.icon
27723                     }
27724                 ]
27725             });
27726         }
27727         
27728         if(this.footer){
27729             var footer = {
27730                 tag : 'a',
27731                 cls : 'small-box-footer',
27732                 href : this.fhref || '#',
27733                 html : this.footer
27734             };
27735             
27736             cfg.cn.push(footer);
27737             
27738         }
27739         
27740         return  cfg;
27741     },
27742
27743     onRender : function(ct,position){
27744         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27745
27746
27747        
27748                 
27749     },
27750
27751     setHeadline: function (value)
27752     {
27753         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27754     },
27755     
27756     setFooter: function (value, href)
27757     {
27758         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27759         
27760         if(href){
27761             this.el.select('a.small-box-footer',true).first().attr('href', href);
27762         }
27763         
27764     },
27765
27766     setContent: function (value)
27767     {
27768         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27769     },
27770
27771     initEvents: function() 
27772     {   
27773         
27774     }
27775     
27776 });
27777
27778  
27779 /*
27780  * - LGPL
27781  *
27782  * TabBox
27783  * 
27784  */
27785 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27786
27787 /**
27788  * @class Roo.bootstrap.dash.TabBox
27789  * @extends Roo.bootstrap.Component
27790  * Bootstrap TabBox class
27791  * @cfg {String} title Title of the TabBox
27792  * @cfg {String} icon Icon of the TabBox
27793  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27794  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27795  * 
27796  * @constructor
27797  * Create a new TabBox
27798  * @param {Object} config The config object
27799  */
27800
27801
27802 Roo.bootstrap.dash.TabBox = function(config){
27803     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27804     this.addEvents({
27805         // raw events
27806         /**
27807          * @event addpane
27808          * When a pane is added
27809          * @param {Roo.bootstrap.dash.TabPane} pane
27810          */
27811         "addpane" : true,
27812         /**
27813          * @event activatepane
27814          * When a pane is activated
27815          * @param {Roo.bootstrap.dash.TabPane} pane
27816          */
27817         "activatepane" : true
27818         
27819          
27820     });
27821     
27822     this.panes = [];
27823 };
27824
27825 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27826
27827     title : '',
27828     icon : false,
27829     showtabs : true,
27830     tabScrollable : false,
27831     
27832     getChildContainer : function()
27833     {
27834         return this.el.select('.tab-content', true).first();
27835     },
27836     
27837     getAutoCreate : function(){
27838         
27839         var header = {
27840             tag: 'li',
27841             cls: 'pull-left header',
27842             html: this.title,
27843             cn : []
27844         };
27845         
27846         if(this.icon){
27847             header.cn.push({
27848                 tag: 'i',
27849                 cls: 'fa ' + this.icon
27850             });
27851         }
27852         
27853         var h = {
27854             tag: 'ul',
27855             cls: 'nav nav-tabs pull-right',
27856             cn: [
27857                 header
27858             ]
27859         };
27860         
27861         if(this.tabScrollable){
27862             h = {
27863                 tag: 'div',
27864                 cls: 'tab-header',
27865                 cn: [
27866                     {
27867                         tag: 'ul',
27868                         cls: 'nav nav-tabs pull-right',
27869                         cn: [
27870                             header
27871                         ]
27872                     }
27873                 ]
27874             };
27875         }
27876         
27877         var cfg = {
27878             tag: 'div',
27879             cls: 'nav-tabs-custom',
27880             cn: [
27881                 h,
27882                 {
27883                     tag: 'div',
27884                     cls: 'tab-content no-padding',
27885                     cn: []
27886                 }
27887             ]
27888         };
27889
27890         return  cfg;
27891     },
27892     initEvents : function()
27893     {
27894         //Roo.log('add add pane handler');
27895         this.on('addpane', this.onAddPane, this);
27896     },
27897      /**
27898      * Updates the box title
27899      * @param {String} html to set the title to.
27900      */
27901     setTitle : function(value)
27902     {
27903         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27904     },
27905     onAddPane : function(pane)
27906     {
27907         this.panes.push(pane);
27908         //Roo.log('addpane');
27909         //Roo.log(pane);
27910         // tabs are rendere left to right..
27911         if(!this.showtabs){
27912             return;
27913         }
27914         
27915         var ctr = this.el.select('.nav-tabs', true).first();
27916          
27917          
27918         var existing = ctr.select('.nav-tab',true);
27919         var qty = existing.getCount();;
27920         
27921         
27922         var tab = ctr.createChild({
27923             tag : 'li',
27924             cls : 'nav-tab' + (qty ? '' : ' active'),
27925             cn : [
27926                 {
27927                     tag : 'a',
27928                     href:'#',
27929                     html : pane.title
27930                 }
27931             ]
27932         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27933         pane.tab = tab;
27934         
27935         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27936         if (!qty) {
27937             pane.el.addClass('active');
27938         }
27939         
27940                 
27941     },
27942     onTabClick : function(ev,un,ob,pane)
27943     {
27944         //Roo.log('tab - prev default');
27945         ev.preventDefault();
27946         
27947         
27948         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27949         pane.tab.addClass('active');
27950         //Roo.log(pane.title);
27951         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27952         // technically we should have a deactivate event.. but maybe add later.
27953         // and it should not de-activate the selected tab...
27954         this.fireEvent('activatepane', pane);
27955         pane.el.addClass('active');
27956         pane.fireEvent('activate');
27957         
27958         
27959     },
27960     
27961     getActivePane : function()
27962     {
27963         var r = false;
27964         Roo.each(this.panes, function(p) {
27965             if(p.el.hasClass('active')){
27966                 r = p;
27967                 return false;
27968             }
27969             
27970             return;
27971         });
27972         
27973         return r;
27974     }
27975     
27976     
27977 });
27978
27979  
27980 /*
27981  * - LGPL
27982  *
27983  * Tab pane
27984  * 
27985  */
27986 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27987 /**
27988  * @class Roo.bootstrap.TabPane
27989  * @extends Roo.bootstrap.Component
27990  * Bootstrap TabPane class
27991  * @cfg {Boolean} active (false | true) Default false
27992  * @cfg {String} title title of panel
27993
27994  * 
27995  * @constructor
27996  * Create a new TabPane
27997  * @param {Object} config The config object
27998  */
27999
28000 Roo.bootstrap.dash.TabPane = function(config){
28001     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28002     
28003     this.addEvents({
28004         // raw events
28005         /**
28006          * @event activate
28007          * When a pane is activated
28008          * @param {Roo.bootstrap.dash.TabPane} pane
28009          */
28010         "activate" : true
28011          
28012     });
28013 };
28014
28015 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28016     
28017     active : false,
28018     title : '',
28019     
28020     // the tabBox that this is attached to.
28021     tab : false,
28022      
28023     getAutoCreate : function() 
28024     {
28025         var cfg = {
28026             tag: 'div',
28027             cls: 'tab-pane'
28028         };
28029         
28030         if(this.active){
28031             cfg.cls += ' active';
28032         }
28033         
28034         return cfg;
28035     },
28036     initEvents  : function()
28037     {
28038         //Roo.log('trigger add pane handler');
28039         this.parent().fireEvent('addpane', this)
28040     },
28041     
28042      /**
28043      * Updates the tab title 
28044      * @param {String} html to set the title to.
28045      */
28046     setTitle: function(str)
28047     {
28048         if (!this.tab) {
28049             return;
28050         }
28051         this.title = str;
28052         this.tab.select('a', true).first().dom.innerHTML = str;
28053         
28054     }
28055     
28056     
28057     
28058 });
28059
28060  
28061
28062
28063  /*
28064  * - LGPL
28065  *
28066  * menu
28067  * 
28068  */
28069 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28070
28071 /**
28072  * @class Roo.bootstrap.menu.Menu
28073  * @extends Roo.bootstrap.Component
28074  * Bootstrap Menu class - container for Menu
28075  * @cfg {String} html Text of the menu
28076  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28077  * @cfg {String} icon Font awesome icon
28078  * @cfg {String} pos Menu align to (top | bottom) default bottom
28079  * 
28080  * 
28081  * @constructor
28082  * Create a new Menu
28083  * @param {Object} config The config object
28084  */
28085
28086
28087 Roo.bootstrap.menu.Menu = function(config){
28088     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28089     
28090     this.addEvents({
28091         /**
28092          * @event beforeshow
28093          * Fires before this menu is displayed
28094          * @param {Roo.bootstrap.menu.Menu} this
28095          */
28096         beforeshow : true,
28097         /**
28098          * @event beforehide
28099          * Fires before this menu is hidden
28100          * @param {Roo.bootstrap.menu.Menu} this
28101          */
28102         beforehide : true,
28103         /**
28104          * @event show
28105          * Fires after this menu is displayed
28106          * @param {Roo.bootstrap.menu.Menu} this
28107          */
28108         show : true,
28109         /**
28110          * @event hide
28111          * Fires after this menu is hidden
28112          * @param {Roo.bootstrap.menu.Menu} this
28113          */
28114         hide : true,
28115         /**
28116          * @event click
28117          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28118          * @param {Roo.bootstrap.menu.Menu} this
28119          * @param {Roo.EventObject} e
28120          */
28121         click : true
28122     });
28123     
28124 };
28125
28126 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28127     
28128     submenu : false,
28129     html : '',
28130     weight : 'default',
28131     icon : false,
28132     pos : 'bottom',
28133     
28134     
28135     getChildContainer : function() {
28136         if(this.isSubMenu){
28137             return this.el;
28138         }
28139         
28140         return this.el.select('ul.dropdown-menu', true).first();  
28141     },
28142     
28143     getAutoCreate : function()
28144     {
28145         var text = [
28146             {
28147                 tag : 'span',
28148                 cls : 'roo-menu-text',
28149                 html : this.html
28150             }
28151         ];
28152         
28153         if(this.icon){
28154             text.unshift({
28155                 tag : 'i',
28156                 cls : 'fa ' + this.icon
28157             })
28158         }
28159         
28160         
28161         var cfg = {
28162             tag : 'div',
28163             cls : 'btn-group',
28164             cn : [
28165                 {
28166                     tag : 'button',
28167                     cls : 'dropdown-button btn btn-' + this.weight,
28168                     cn : text
28169                 },
28170                 {
28171                     tag : 'button',
28172                     cls : 'dropdown-toggle btn btn-' + this.weight,
28173                     cn : [
28174                         {
28175                             tag : 'span',
28176                             cls : 'caret'
28177                         }
28178                     ]
28179                 },
28180                 {
28181                     tag : 'ul',
28182                     cls : 'dropdown-menu'
28183                 }
28184             ]
28185             
28186         };
28187         
28188         if(this.pos == 'top'){
28189             cfg.cls += ' dropup';
28190         }
28191         
28192         if(this.isSubMenu){
28193             cfg = {
28194                 tag : 'ul',
28195                 cls : 'dropdown-menu'
28196             }
28197         }
28198         
28199         return cfg;
28200     },
28201     
28202     onRender : function(ct, position)
28203     {
28204         this.isSubMenu = ct.hasClass('dropdown-submenu');
28205         
28206         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28207     },
28208     
28209     initEvents : function() 
28210     {
28211         if(this.isSubMenu){
28212             return;
28213         }
28214         
28215         this.hidden = true;
28216         
28217         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28218         this.triggerEl.on('click', this.onTriggerPress, this);
28219         
28220         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28221         this.buttonEl.on('click', this.onClick, this);
28222         
28223     },
28224     
28225     list : function()
28226     {
28227         if(this.isSubMenu){
28228             return this.el;
28229         }
28230         
28231         return this.el.select('ul.dropdown-menu', true).first();
28232     },
28233     
28234     onClick : function(e)
28235     {
28236         this.fireEvent("click", this, e);
28237     },
28238     
28239     onTriggerPress  : function(e)
28240     {   
28241         if (this.isVisible()) {
28242             this.hide();
28243         } else {
28244             this.show();
28245         }
28246     },
28247     
28248     isVisible : function(){
28249         return !this.hidden;
28250     },
28251     
28252     show : function()
28253     {
28254         this.fireEvent("beforeshow", this);
28255         
28256         this.hidden = false;
28257         this.el.addClass('open');
28258         
28259         Roo.get(document).on("mouseup", this.onMouseUp, this);
28260         
28261         this.fireEvent("show", this);
28262         
28263         
28264     },
28265     
28266     hide : function()
28267     {
28268         this.fireEvent("beforehide", this);
28269         
28270         this.hidden = true;
28271         this.el.removeClass('open');
28272         
28273         Roo.get(document).un("mouseup", this.onMouseUp);
28274         
28275         this.fireEvent("hide", this);
28276     },
28277     
28278     onMouseUp : function()
28279     {
28280         this.hide();
28281     }
28282     
28283 });
28284
28285  
28286  /*
28287  * - LGPL
28288  *
28289  * menu item
28290  * 
28291  */
28292 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28293
28294 /**
28295  * @class Roo.bootstrap.menu.Item
28296  * @extends Roo.bootstrap.Component
28297  * Bootstrap MenuItem class
28298  * @cfg {Boolean} submenu (true | false) default false
28299  * @cfg {String} html text of the item
28300  * @cfg {String} href the link
28301  * @cfg {Boolean} disable (true | false) default false
28302  * @cfg {Boolean} preventDefault (true | false) default true
28303  * @cfg {String} icon Font awesome icon
28304  * @cfg {String} pos Submenu align to (left | right) default right 
28305  * 
28306  * 
28307  * @constructor
28308  * Create a new Item
28309  * @param {Object} config The config object
28310  */
28311
28312
28313 Roo.bootstrap.menu.Item = function(config){
28314     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28315     this.addEvents({
28316         /**
28317          * @event mouseover
28318          * Fires when the mouse is hovering over this menu
28319          * @param {Roo.bootstrap.menu.Item} this
28320          * @param {Roo.EventObject} e
28321          */
28322         mouseover : true,
28323         /**
28324          * @event mouseout
28325          * Fires when the mouse exits this menu
28326          * @param {Roo.bootstrap.menu.Item} this
28327          * @param {Roo.EventObject} e
28328          */
28329         mouseout : true,
28330         // raw events
28331         /**
28332          * @event click
28333          * The raw click event for the entire grid.
28334          * @param {Roo.EventObject} e
28335          */
28336         click : true
28337     });
28338 };
28339
28340 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28341     
28342     submenu : false,
28343     href : '',
28344     html : '',
28345     preventDefault: true,
28346     disable : false,
28347     icon : false,
28348     pos : 'right',
28349     
28350     getAutoCreate : function()
28351     {
28352         var text = [
28353             {
28354                 tag : 'span',
28355                 cls : 'roo-menu-item-text',
28356                 html : this.html
28357             }
28358         ];
28359         
28360         if(this.icon){
28361             text.unshift({
28362                 tag : 'i',
28363                 cls : 'fa ' + this.icon
28364             })
28365         }
28366         
28367         var cfg = {
28368             tag : 'li',
28369             cn : [
28370                 {
28371                     tag : 'a',
28372                     href : this.href || '#',
28373                     cn : text
28374                 }
28375             ]
28376         };
28377         
28378         if(this.disable){
28379             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28380         }
28381         
28382         if(this.submenu){
28383             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28384             
28385             if(this.pos == 'left'){
28386                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28387             }
28388         }
28389         
28390         return cfg;
28391     },
28392     
28393     initEvents : function() 
28394     {
28395         this.el.on('mouseover', this.onMouseOver, this);
28396         this.el.on('mouseout', this.onMouseOut, this);
28397         
28398         this.el.select('a', true).first().on('click', this.onClick, this);
28399         
28400     },
28401     
28402     onClick : function(e)
28403     {
28404         if(this.preventDefault){
28405             e.preventDefault();
28406         }
28407         
28408         this.fireEvent("click", this, e);
28409     },
28410     
28411     onMouseOver : function(e)
28412     {
28413         if(this.submenu && this.pos == 'left'){
28414             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28415         }
28416         
28417         this.fireEvent("mouseover", this, e);
28418     },
28419     
28420     onMouseOut : function(e)
28421     {
28422         this.fireEvent("mouseout", this, e);
28423     }
28424 });
28425
28426  
28427
28428  /*
28429  * - LGPL
28430  *
28431  * menu separator
28432  * 
28433  */
28434 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28435
28436 /**
28437  * @class Roo.bootstrap.menu.Separator
28438  * @extends Roo.bootstrap.Component
28439  * Bootstrap Separator class
28440  * 
28441  * @constructor
28442  * Create a new Separator
28443  * @param {Object} config The config object
28444  */
28445
28446
28447 Roo.bootstrap.menu.Separator = function(config){
28448     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28449 };
28450
28451 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28452     
28453     getAutoCreate : function(){
28454         var cfg = {
28455             tag : 'li',
28456             cls: 'divider'
28457         };
28458         
28459         return cfg;
28460     }
28461    
28462 });
28463
28464  
28465
28466  /*
28467  * - LGPL
28468  *
28469  * Tooltip
28470  * 
28471  */
28472
28473 /**
28474  * @class Roo.bootstrap.Tooltip
28475  * Bootstrap Tooltip class
28476  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28477  * to determine which dom element triggers the tooltip.
28478  * 
28479  * It needs to add support for additional attributes like tooltip-position
28480  * 
28481  * @constructor
28482  * Create a new Toolti
28483  * @param {Object} config The config object
28484  */
28485
28486 Roo.bootstrap.Tooltip = function(config){
28487     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28488     
28489     this.alignment = Roo.bootstrap.Tooltip.alignment;
28490     
28491     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28492         this.alignment = config.alignment;
28493     }
28494     
28495 };
28496
28497 Roo.apply(Roo.bootstrap.Tooltip, {
28498     /**
28499      * @function init initialize tooltip monitoring.
28500      * @static
28501      */
28502     currentEl : false,
28503     currentTip : false,
28504     currentRegion : false,
28505     
28506     //  init : delay?
28507     
28508     init : function()
28509     {
28510         Roo.get(document).on('mouseover', this.enter ,this);
28511         Roo.get(document).on('mouseout', this.leave, this);
28512          
28513         
28514         this.currentTip = new Roo.bootstrap.Tooltip();
28515     },
28516     
28517     enter : function(ev)
28518     {
28519         var dom = ev.getTarget();
28520         
28521         //Roo.log(['enter',dom]);
28522         var el = Roo.fly(dom);
28523         if (this.currentEl) {
28524             //Roo.log(dom);
28525             //Roo.log(this.currentEl);
28526             //Roo.log(this.currentEl.contains(dom));
28527             if (this.currentEl == el) {
28528                 return;
28529             }
28530             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28531                 return;
28532             }
28533
28534         }
28535         
28536         if (this.currentTip.el) {
28537             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28538         }    
28539         //Roo.log(ev);
28540         
28541         if(!el || el.dom == document){
28542             return;
28543         }
28544         
28545         var bindEl = el;
28546         
28547         // you can not look for children, as if el is the body.. then everythign is the child..
28548         if (!el.attr('tooltip')) { //
28549             if (!el.select("[tooltip]").elements.length) {
28550                 return;
28551             }
28552             // is the mouse over this child...?
28553             bindEl = el.select("[tooltip]").first();
28554             var xy = ev.getXY();
28555             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28556                 //Roo.log("not in region.");
28557                 return;
28558             }
28559             //Roo.log("child element over..");
28560             
28561         }
28562         this.currentEl = bindEl;
28563         this.currentTip.bind(bindEl);
28564         this.currentRegion = Roo.lib.Region.getRegion(dom);
28565         this.currentTip.enter();
28566         
28567     },
28568     leave : function(ev)
28569     {
28570         var dom = ev.getTarget();
28571         //Roo.log(['leave',dom]);
28572         if (!this.currentEl) {
28573             return;
28574         }
28575         
28576         
28577         if (dom != this.currentEl.dom) {
28578             return;
28579         }
28580         var xy = ev.getXY();
28581         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28582             return;
28583         }
28584         // only activate leave if mouse cursor is outside... bounding box..
28585         
28586         
28587         
28588         
28589         if (this.currentTip) {
28590             this.currentTip.leave();
28591         }
28592         //Roo.log('clear currentEl');
28593         this.currentEl = false;
28594         
28595         
28596     },
28597     alignment : {
28598         'left' : ['r-l', [-2,0], 'right'],
28599         'right' : ['l-r', [2,0], 'left'],
28600         'bottom' : ['t-b', [0,2], 'top'],
28601         'top' : [ 'b-t', [0,-2], 'bottom']
28602     }
28603     
28604 });
28605
28606
28607 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28608     
28609     
28610     bindEl : false,
28611     
28612     delay : null, // can be { show : 300 , hide: 500}
28613     
28614     timeout : null,
28615     
28616     hoverState : null, //???
28617     
28618     placement : 'bottom', 
28619     
28620     alignment : false,
28621     
28622     getAutoCreate : function(){
28623     
28624         var cfg = {
28625            cls : 'tooltip',   
28626            role : 'tooltip',
28627            cn : [
28628                 {
28629                     cls : 'tooltip-arrow arrow'
28630                 },
28631                 {
28632                     cls : 'tooltip-inner'
28633                 }
28634            ]
28635         };
28636         
28637         return cfg;
28638     },
28639     bind : function(el)
28640     {
28641         this.bindEl = el;
28642     },
28643     
28644     initEvents : function()
28645     {
28646         this.arrowEl = this.el.select('.arrow', true).first();
28647         this.innerEl = this.el.select('.tooltip-inner', true).first();
28648     },
28649     
28650     enter : function () {
28651        
28652         if (this.timeout != null) {
28653             clearTimeout(this.timeout);
28654         }
28655         
28656         this.hoverState = 'in';
28657          //Roo.log("enter - show");
28658         if (!this.delay || !this.delay.show) {
28659             this.show();
28660             return;
28661         }
28662         var _t = this;
28663         this.timeout = setTimeout(function () {
28664             if (_t.hoverState == 'in') {
28665                 _t.show();
28666             }
28667         }, this.delay.show);
28668     },
28669     leave : function()
28670     {
28671         clearTimeout(this.timeout);
28672     
28673         this.hoverState = 'out';
28674          if (!this.delay || !this.delay.hide) {
28675             this.hide();
28676             return;
28677         }
28678        
28679         var _t = this;
28680         this.timeout = setTimeout(function () {
28681             //Roo.log("leave - timeout");
28682             
28683             if (_t.hoverState == 'out') {
28684                 _t.hide();
28685                 Roo.bootstrap.Tooltip.currentEl = false;
28686             }
28687         }, delay);
28688     },
28689     
28690     show : function (msg)
28691     {
28692         if (!this.el) {
28693             this.render(document.body);
28694         }
28695         // set content.
28696         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28697         
28698         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28699         
28700         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28701         
28702         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28703                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28704         
28705         var placement = typeof this.placement == 'function' ?
28706             this.placement.call(this, this.el, on_el) :
28707             this.placement;
28708             
28709         var autoToken = /\s?auto?\s?/i;
28710         var autoPlace = autoToken.test(placement);
28711         if (autoPlace) {
28712             placement = placement.replace(autoToken, '') || 'top';
28713         }
28714         
28715         //this.el.detach()
28716         //this.el.setXY([0,0]);
28717         this.el.show();
28718         //this.el.dom.style.display='block';
28719         
28720         //this.el.appendTo(on_el);
28721         
28722         var p = this.getPosition();
28723         var box = this.el.getBox();
28724         
28725         if (autoPlace) {
28726             // fixme..
28727         }
28728         
28729         var align = this.alignment[placement];
28730         
28731         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28732         
28733         if(placement == 'top' || placement == 'bottom'){
28734             if(xy[0] < 0){
28735                 placement = 'right';
28736             }
28737             
28738             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28739                 placement = 'left';
28740             }
28741             
28742             var scroll = Roo.select('body', true).first().getScroll();
28743             
28744             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28745                 placement = 'top';
28746             }
28747             
28748             align = this.alignment[placement];
28749             
28750             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28751             
28752         }
28753         
28754         this.el.alignTo(this.bindEl, align[0],align[1]);
28755         //var arrow = this.el.select('.arrow',true).first();
28756         //arrow.set(align[2], 
28757         
28758         this.el.addClass(placement);
28759         this.el.addClass("bs-tooltip-"+ placement);
28760         
28761         this.el.addClass('in fade show');
28762         
28763         this.hoverState = null;
28764         
28765         if (this.el.hasClass('fade')) {
28766             // fade it?
28767         }
28768         
28769         
28770         
28771         
28772         
28773     },
28774     hide : function()
28775     {
28776          
28777         if (!this.el) {
28778             return;
28779         }
28780         //this.el.setXY([0,0]);
28781         this.el.removeClass(['show', 'in']);
28782         //this.el.hide();
28783         
28784     }
28785     
28786 });
28787  
28788
28789  /*
28790  * - LGPL
28791  *
28792  * Location Picker
28793  * 
28794  */
28795
28796 /**
28797  * @class Roo.bootstrap.LocationPicker
28798  * @extends Roo.bootstrap.Component
28799  * Bootstrap LocationPicker class
28800  * @cfg {Number} latitude Position when init default 0
28801  * @cfg {Number} longitude Position when init default 0
28802  * @cfg {Number} zoom default 15
28803  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28804  * @cfg {Boolean} mapTypeControl default false
28805  * @cfg {Boolean} disableDoubleClickZoom default false
28806  * @cfg {Boolean} scrollwheel default true
28807  * @cfg {Boolean} streetViewControl default false
28808  * @cfg {Number} radius default 0
28809  * @cfg {String} locationName
28810  * @cfg {Boolean} draggable default true
28811  * @cfg {Boolean} enableAutocomplete default false
28812  * @cfg {Boolean} enableReverseGeocode default true
28813  * @cfg {String} markerTitle
28814  * 
28815  * @constructor
28816  * Create a new LocationPicker
28817  * @param {Object} config The config object
28818  */
28819
28820
28821 Roo.bootstrap.LocationPicker = function(config){
28822     
28823     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28824     
28825     this.addEvents({
28826         /**
28827          * @event initial
28828          * Fires when the picker initialized.
28829          * @param {Roo.bootstrap.LocationPicker} this
28830          * @param {Google Location} location
28831          */
28832         initial : true,
28833         /**
28834          * @event positionchanged
28835          * Fires when the picker position changed.
28836          * @param {Roo.bootstrap.LocationPicker} this
28837          * @param {Google Location} location
28838          */
28839         positionchanged : true,
28840         /**
28841          * @event resize
28842          * Fires when the map resize.
28843          * @param {Roo.bootstrap.LocationPicker} this
28844          */
28845         resize : true,
28846         /**
28847          * @event show
28848          * Fires when the map show.
28849          * @param {Roo.bootstrap.LocationPicker} this
28850          */
28851         show : true,
28852         /**
28853          * @event hide
28854          * Fires when the map hide.
28855          * @param {Roo.bootstrap.LocationPicker} this
28856          */
28857         hide : true,
28858         /**
28859          * @event mapClick
28860          * Fires when click the map.
28861          * @param {Roo.bootstrap.LocationPicker} this
28862          * @param {Map event} e
28863          */
28864         mapClick : true,
28865         /**
28866          * @event mapRightClick
28867          * Fires when right click the map.
28868          * @param {Roo.bootstrap.LocationPicker} this
28869          * @param {Map event} e
28870          */
28871         mapRightClick : true,
28872         /**
28873          * @event markerClick
28874          * Fires when click the marker.
28875          * @param {Roo.bootstrap.LocationPicker} this
28876          * @param {Map event} e
28877          */
28878         markerClick : true,
28879         /**
28880          * @event markerRightClick
28881          * Fires when right click the marker.
28882          * @param {Roo.bootstrap.LocationPicker} this
28883          * @param {Map event} e
28884          */
28885         markerRightClick : true,
28886         /**
28887          * @event OverlayViewDraw
28888          * Fires when OverlayView Draw
28889          * @param {Roo.bootstrap.LocationPicker} this
28890          */
28891         OverlayViewDraw : true,
28892         /**
28893          * @event OverlayViewOnAdd
28894          * Fires when OverlayView Draw
28895          * @param {Roo.bootstrap.LocationPicker} this
28896          */
28897         OverlayViewOnAdd : true,
28898         /**
28899          * @event OverlayViewOnRemove
28900          * Fires when OverlayView Draw
28901          * @param {Roo.bootstrap.LocationPicker} this
28902          */
28903         OverlayViewOnRemove : true,
28904         /**
28905          * @event OverlayViewShow
28906          * Fires when OverlayView Draw
28907          * @param {Roo.bootstrap.LocationPicker} this
28908          * @param {Pixel} cpx
28909          */
28910         OverlayViewShow : true,
28911         /**
28912          * @event OverlayViewHide
28913          * Fires when OverlayView Draw
28914          * @param {Roo.bootstrap.LocationPicker} this
28915          */
28916         OverlayViewHide : true,
28917         /**
28918          * @event loadexception
28919          * Fires when load google lib failed.
28920          * @param {Roo.bootstrap.LocationPicker} this
28921          */
28922         loadexception : true
28923     });
28924         
28925 };
28926
28927 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28928     
28929     gMapContext: false,
28930     
28931     latitude: 0,
28932     longitude: 0,
28933     zoom: 15,
28934     mapTypeId: false,
28935     mapTypeControl: false,
28936     disableDoubleClickZoom: false,
28937     scrollwheel: true,
28938     streetViewControl: false,
28939     radius: 0,
28940     locationName: '',
28941     draggable: true,
28942     enableAutocomplete: false,
28943     enableReverseGeocode: true,
28944     markerTitle: '',
28945     
28946     getAutoCreate: function()
28947     {
28948
28949         var cfg = {
28950             tag: 'div',
28951             cls: 'roo-location-picker'
28952         };
28953         
28954         return cfg
28955     },
28956     
28957     initEvents: function(ct, position)
28958     {       
28959         if(!this.el.getWidth() || this.isApplied()){
28960             return;
28961         }
28962         
28963         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28964         
28965         this.initial();
28966     },
28967     
28968     initial: function()
28969     {
28970         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28971             this.fireEvent('loadexception', this);
28972             return;
28973         }
28974         
28975         if(!this.mapTypeId){
28976             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28977         }
28978         
28979         this.gMapContext = this.GMapContext();
28980         
28981         this.initOverlayView();
28982         
28983         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28984         
28985         var _this = this;
28986                 
28987         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28988             _this.setPosition(_this.gMapContext.marker.position);
28989         });
28990         
28991         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28992             _this.fireEvent('mapClick', this, event);
28993             
28994         });
28995
28996         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28997             _this.fireEvent('mapRightClick', this, event);
28998             
28999         });
29000         
29001         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29002             _this.fireEvent('markerClick', this, event);
29003             
29004         });
29005
29006         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29007             _this.fireEvent('markerRightClick', this, event);
29008             
29009         });
29010         
29011         this.setPosition(this.gMapContext.location);
29012         
29013         this.fireEvent('initial', this, this.gMapContext.location);
29014     },
29015     
29016     initOverlayView: function()
29017     {
29018         var _this = this;
29019         
29020         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29021             
29022             draw: function()
29023             {
29024                 _this.fireEvent('OverlayViewDraw', _this);
29025             },
29026             
29027             onAdd: function()
29028             {
29029                 _this.fireEvent('OverlayViewOnAdd', _this);
29030             },
29031             
29032             onRemove: function()
29033             {
29034                 _this.fireEvent('OverlayViewOnRemove', _this);
29035             },
29036             
29037             show: function(cpx)
29038             {
29039                 _this.fireEvent('OverlayViewShow', _this, cpx);
29040             },
29041             
29042             hide: function()
29043             {
29044                 _this.fireEvent('OverlayViewHide', _this);
29045             }
29046             
29047         });
29048     },
29049     
29050     fromLatLngToContainerPixel: function(event)
29051     {
29052         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29053     },
29054     
29055     isApplied: function() 
29056     {
29057         return this.getGmapContext() == false ? false : true;
29058     },
29059     
29060     getGmapContext: function() 
29061     {
29062         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29063     },
29064     
29065     GMapContext: function() 
29066     {
29067         var position = new google.maps.LatLng(this.latitude, this.longitude);
29068         
29069         var _map = new google.maps.Map(this.el.dom, {
29070             center: position,
29071             zoom: this.zoom,
29072             mapTypeId: this.mapTypeId,
29073             mapTypeControl: this.mapTypeControl,
29074             disableDoubleClickZoom: this.disableDoubleClickZoom,
29075             scrollwheel: this.scrollwheel,
29076             streetViewControl: this.streetViewControl,
29077             locationName: this.locationName,
29078             draggable: this.draggable,
29079             enableAutocomplete: this.enableAutocomplete,
29080             enableReverseGeocode: this.enableReverseGeocode
29081         });
29082         
29083         var _marker = new google.maps.Marker({
29084             position: position,
29085             map: _map,
29086             title: this.markerTitle,
29087             draggable: this.draggable
29088         });
29089         
29090         return {
29091             map: _map,
29092             marker: _marker,
29093             circle: null,
29094             location: position,
29095             radius: this.radius,
29096             locationName: this.locationName,
29097             addressComponents: {
29098                 formatted_address: null,
29099                 addressLine1: null,
29100                 addressLine2: null,
29101                 streetName: null,
29102                 streetNumber: null,
29103                 city: null,
29104                 district: null,
29105                 state: null,
29106                 stateOrProvince: null
29107             },
29108             settings: this,
29109             domContainer: this.el.dom,
29110             geodecoder: new google.maps.Geocoder()
29111         };
29112     },
29113     
29114     drawCircle: function(center, radius, options) 
29115     {
29116         if (this.gMapContext.circle != null) {
29117             this.gMapContext.circle.setMap(null);
29118         }
29119         if (radius > 0) {
29120             radius *= 1;
29121             options = Roo.apply({}, options, {
29122                 strokeColor: "#0000FF",
29123                 strokeOpacity: .35,
29124                 strokeWeight: 2,
29125                 fillColor: "#0000FF",
29126                 fillOpacity: .2
29127             });
29128             
29129             options.map = this.gMapContext.map;
29130             options.radius = radius;
29131             options.center = center;
29132             this.gMapContext.circle = new google.maps.Circle(options);
29133             return this.gMapContext.circle;
29134         }
29135         
29136         return null;
29137     },
29138     
29139     setPosition: function(location) 
29140     {
29141         this.gMapContext.location = location;
29142         this.gMapContext.marker.setPosition(location);
29143         this.gMapContext.map.panTo(location);
29144         this.drawCircle(location, this.gMapContext.radius, {});
29145         
29146         var _this = this;
29147         
29148         if (this.gMapContext.settings.enableReverseGeocode) {
29149             this.gMapContext.geodecoder.geocode({
29150                 latLng: this.gMapContext.location
29151             }, function(results, status) {
29152                 
29153                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29154                     _this.gMapContext.locationName = results[0].formatted_address;
29155                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29156                     
29157                     _this.fireEvent('positionchanged', this, location);
29158                 }
29159             });
29160             
29161             return;
29162         }
29163         
29164         this.fireEvent('positionchanged', this, location);
29165     },
29166     
29167     resize: function()
29168     {
29169         google.maps.event.trigger(this.gMapContext.map, "resize");
29170         
29171         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29172         
29173         this.fireEvent('resize', this);
29174     },
29175     
29176     setPositionByLatLng: function(latitude, longitude)
29177     {
29178         this.setPosition(new google.maps.LatLng(latitude, longitude));
29179     },
29180     
29181     getCurrentPosition: function() 
29182     {
29183         return {
29184             latitude: this.gMapContext.location.lat(),
29185             longitude: this.gMapContext.location.lng()
29186         };
29187     },
29188     
29189     getAddressName: function() 
29190     {
29191         return this.gMapContext.locationName;
29192     },
29193     
29194     getAddressComponents: function() 
29195     {
29196         return this.gMapContext.addressComponents;
29197     },
29198     
29199     address_component_from_google_geocode: function(address_components) 
29200     {
29201         var result = {};
29202         
29203         for (var i = 0; i < address_components.length; i++) {
29204             var component = address_components[i];
29205             if (component.types.indexOf("postal_code") >= 0) {
29206                 result.postalCode = component.short_name;
29207             } else if (component.types.indexOf("street_number") >= 0) {
29208                 result.streetNumber = component.short_name;
29209             } else if (component.types.indexOf("route") >= 0) {
29210                 result.streetName = component.short_name;
29211             } else if (component.types.indexOf("neighborhood") >= 0) {
29212                 result.city = component.short_name;
29213             } else if (component.types.indexOf("locality") >= 0) {
29214                 result.city = component.short_name;
29215             } else if (component.types.indexOf("sublocality") >= 0) {
29216                 result.district = component.short_name;
29217             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29218                 result.stateOrProvince = component.short_name;
29219             } else if (component.types.indexOf("country") >= 0) {
29220                 result.country = component.short_name;
29221             }
29222         }
29223         
29224         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29225         result.addressLine2 = "";
29226         return result;
29227     },
29228     
29229     setZoomLevel: function(zoom)
29230     {
29231         this.gMapContext.map.setZoom(zoom);
29232     },
29233     
29234     show: function()
29235     {
29236         if(!this.el){
29237             return;
29238         }
29239         
29240         this.el.show();
29241         
29242         this.resize();
29243         
29244         this.fireEvent('show', this);
29245     },
29246     
29247     hide: function()
29248     {
29249         if(!this.el){
29250             return;
29251         }
29252         
29253         this.el.hide();
29254         
29255         this.fireEvent('hide', this);
29256     }
29257     
29258 });
29259
29260 Roo.apply(Roo.bootstrap.LocationPicker, {
29261     
29262     OverlayView : function(map, options)
29263     {
29264         options = options || {};
29265         
29266         this.setMap(map);
29267     }
29268     
29269     
29270 });/**
29271  * @class Roo.bootstrap.Alert
29272  * @extends Roo.bootstrap.Component
29273  * Bootstrap Alert class - shows an alert area box
29274  * eg
29275  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29276   Enter a valid email address
29277 </div>
29278  * @licence LGPL
29279  * @cfg {String} title The title of alert
29280  * @cfg {String} html The content of alert
29281  * @cfg {String} weight (  success | info | warning | danger )
29282  * @cfg {String} faicon font-awesomeicon
29283  * 
29284  * @constructor
29285  * Create a new alert
29286  * @param {Object} config The config object
29287  */
29288
29289
29290 Roo.bootstrap.Alert = function(config){
29291     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29292     
29293 };
29294
29295 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29296     
29297     title: '',
29298     html: '',
29299     weight: false,
29300     faicon: false,
29301     
29302     getAutoCreate : function()
29303     {
29304         
29305         var cfg = {
29306             tag : 'div',
29307             cls : 'alert',
29308             cn : [
29309                 {
29310                     tag : 'i',
29311                     cls : 'roo-alert-icon'
29312                     
29313                 },
29314                 {
29315                     tag : 'b',
29316                     cls : 'roo-alert-title',
29317                     html : this.title
29318                 },
29319                 {
29320                     tag : 'span',
29321                     cls : 'roo-alert-text',
29322                     html : this.html
29323                 }
29324             ]
29325         };
29326         
29327         if(this.faicon){
29328             cfg.cn[0].cls += ' fa ' + this.faicon;
29329         }
29330         
29331         if(this.weight){
29332             cfg.cls += ' alert-' + this.weight;
29333         }
29334         
29335         return cfg;
29336     },
29337     
29338     initEvents: function() 
29339     {
29340         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29341     },
29342     
29343     setTitle : function(str)
29344     {
29345         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29346     },
29347     
29348     setText : function(str)
29349     {
29350         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29351     },
29352     
29353     setWeight : function(weight)
29354     {
29355         if(this.weight){
29356             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29357         }
29358         
29359         this.weight = weight;
29360         
29361         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29362     },
29363     
29364     setIcon : function(icon)
29365     {
29366         if(this.faicon){
29367             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29368         }
29369         
29370         this.faicon = icon;
29371         
29372         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29373     },
29374     
29375     hide: function() 
29376     {
29377         this.el.hide();   
29378     },
29379     
29380     show: function() 
29381     {  
29382         this.el.show();   
29383     }
29384     
29385 });
29386
29387  
29388 /*
29389 * Licence: LGPL
29390 */
29391
29392 /**
29393  * @class Roo.bootstrap.UploadCropbox
29394  * @extends Roo.bootstrap.Component
29395  * Bootstrap UploadCropbox class
29396  * @cfg {String} emptyText show when image has been loaded
29397  * @cfg {String} rotateNotify show when image too small to rotate
29398  * @cfg {Number} errorTimeout default 3000
29399  * @cfg {Number} minWidth default 300
29400  * @cfg {Number} minHeight default 300
29401  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29402  * @cfg {Boolean} isDocument (true|false) default false
29403  * @cfg {String} url action url
29404  * @cfg {String} paramName default 'imageUpload'
29405  * @cfg {String} method default POST
29406  * @cfg {Boolean} loadMask (true|false) default true
29407  * @cfg {Boolean} loadingText default 'Loading...'
29408  * 
29409  * @constructor
29410  * Create a new UploadCropbox
29411  * @param {Object} config The config object
29412  */
29413
29414 Roo.bootstrap.UploadCropbox = function(config){
29415     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29416     
29417     this.addEvents({
29418         /**
29419          * @event beforeselectfile
29420          * Fire before select file
29421          * @param {Roo.bootstrap.UploadCropbox} this
29422          */
29423         "beforeselectfile" : true,
29424         /**
29425          * @event initial
29426          * Fire after initEvent
29427          * @param {Roo.bootstrap.UploadCropbox} this
29428          */
29429         "initial" : true,
29430         /**
29431          * @event crop
29432          * Fire after initEvent
29433          * @param {Roo.bootstrap.UploadCropbox} this
29434          * @param {String} data
29435          */
29436         "crop" : true,
29437         /**
29438          * @event prepare
29439          * Fire when preparing the file data
29440          * @param {Roo.bootstrap.UploadCropbox} this
29441          * @param {Object} file
29442          */
29443         "prepare" : true,
29444         /**
29445          * @event exception
29446          * Fire when get exception
29447          * @param {Roo.bootstrap.UploadCropbox} this
29448          * @param {XMLHttpRequest} xhr
29449          */
29450         "exception" : true,
29451         /**
29452          * @event beforeloadcanvas
29453          * Fire before load the canvas
29454          * @param {Roo.bootstrap.UploadCropbox} this
29455          * @param {String} src
29456          */
29457         "beforeloadcanvas" : true,
29458         /**
29459          * @event trash
29460          * Fire when trash image
29461          * @param {Roo.bootstrap.UploadCropbox} this
29462          */
29463         "trash" : true,
29464         /**
29465          * @event download
29466          * Fire when download the image
29467          * @param {Roo.bootstrap.UploadCropbox} this
29468          */
29469         "download" : true,
29470         /**
29471          * @event footerbuttonclick
29472          * Fire when footerbuttonclick
29473          * @param {Roo.bootstrap.UploadCropbox} this
29474          * @param {String} type
29475          */
29476         "footerbuttonclick" : true,
29477         /**
29478          * @event resize
29479          * Fire when resize
29480          * @param {Roo.bootstrap.UploadCropbox} this
29481          */
29482         "resize" : true,
29483         /**
29484          * @event rotate
29485          * Fire when rotate the image
29486          * @param {Roo.bootstrap.UploadCropbox} this
29487          * @param {String} pos
29488          */
29489         "rotate" : true,
29490         /**
29491          * @event inspect
29492          * Fire when inspect the file
29493          * @param {Roo.bootstrap.UploadCropbox} this
29494          * @param {Object} file
29495          */
29496         "inspect" : true,
29497         /**
29498          * @event upload
29499          * Fire when xhr upload the file
29500          * @param {Roo.bootstrap.UploadCropbox} this
29501          * @param {Object} data
29502          */
29503         "upload" : true,
29504         /**
29505          * @event arrange
29506          * Fire when arrange the file data
29507          * @param {Roo.bootstrap.UploadCropbox} this
29508          * @param {Object} formData
29509          */
29510         "arrange" : true
29511     });
29512     
29513     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29514 };
29515
29516 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29517     
29518     emptyText : 'Click to upload image',
29519     rotateNotify : 'Image is too small to rotate',
29520     errorTimeout : 3000,
29521     scale : 0,
29522     baseScale : 1,
29523     rotate : 0,
29524     dragable : false,
29525     pinching : false,
29526     mouseX : 0,
29527     mouseY : 0,
29528     cropData : false,
29529     minWidth : 300,
29530     minHeight : 300,
29531     file : false,
29532     exif : {},
29533     baseRotate : 1,
29534     cropType : 'image/jpeg',
29535     buttons : false,
29536     canvasLoaded : false,
29537     isDocument : false,
29538     method : 'POST',
29539     paramName : 'imageUpload',
29540     loadMask : true,
29541     loadingText : 'Loading...',
29542     maskEl : false,
29543     
29544     getAutoCreate : function()
29545     {
29546         var cfg = {
29547             tag : 'div',
29548             cls : 'roo-upload-cropbox',
29549             cn : [
29550                 {
29551                     tag : 'input',
29552                     cls : 'roo-upload-cropbox-selector',
29553                     type : 'file'
29554                 },
29555                 {
29556                     tag : 'div',
29557                     cls : 'roo-upload-cropbox-body',
29558                     style : 'cursor:pointer',
29559                     cn : [
29560                         {
29561                             tag : 'div',
29562                             cls : 'roo-upload-cropbox-preview'
29563                         },
29564                         {
29565                             tag : 'div',
29566                             cls : 'roo-upload-cropbox-thumb'
29567                         },
29568                         {
29569                             tag : 'div',
29570                             cls : 'roo-upload-cropbox-empty-notify',
29571                             html : this.emptyText
29572                         },
29573                         {
29574                             tag : 'div',
29575                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29576                             html : this.rotateNotify
29577                         }
29578                     ]
29579                 },
29580                 {
29581                     tag : 'div',
29582                     cls : 'roo-upload-cropbox-footer',
29583                     cn : {
29584                         tag : 'div',
29585                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29586                         cn : []
29587                     }
29588                 }
29589             ]
29590         };
29591         
29592         return cfg;
29593     },
29594     
29595     onRender : function(ct, position)
29596     {
29597         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29598         
29599         if (this.buttons.length) {
29600             
29601             Roo.each(this.buttons, function(bb) {
29602                 
29603                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29604                 
29605                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29606                 
29607             }, this);
29608         }
29609         
29610         if(this.loadMask){
29611             this.maskEl = this.el;
29612         }
29613     },
29614     
29615     initEvents : function()
29616     {
29617         this.urlAPI = (window.createObjectURL && window) || 
29618                                 (window.URL && URL.revokeObjectURL && URL) || 
29619                                 (window.webkitURL && webkitURL);
29620                         
29621         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29622         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29623         
29624         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29625         this.selectorEl.hide();
29626         
29627         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29628         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29629         
29630         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29631         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29632         this.thumbEl.hide();
29633         
29634         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29635         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29636         
29637         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29638         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29639         this.errorEl.hide();
29640         
29641         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29642         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29643         this.footerEl.hide();
29644         
29645         this.setThumbBoxSize();
29646         
29647         this.bind();
29648         
29649         this.resize();
29650         
29651         this.fireEvent('initial', this);
29652     },
29653
29654     bind : function()
29655     {
29656         var _this = this;
29657         
29658         window.addEventListener("resize", function() { _this.resize(); } );
29659         
29660         this.bodyEl.on('click', this.beforeSelectFile, this);
29661         
29662         if(Roo.isTouch){
29663             this.bodyEl.on('touchstart', this.onTouchStart, this);
29664             this.bodyEl.on('touchmove', this.onTouchMove, this);
29665             this.bodyEl.on('touchend', this.onTouchEnd, this);
29666         }
29667         
29668         if(!Roo.isTouch){
29669             this.bodyEl.on('mousedown', this.onMouseDown, this);
29670             this.bodyEl.on('mousemove', this.onMouseMove, this);
29671             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29672             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29673             Roo.get(document).on('mouseup', this.onMouseUp, this);
29674         }
29675         
29676         this.selectorEl.on('change', this.onFileSelected, this);
29677     },
29678     
29679     reset : function()
29680     {    
29681         this.scale = 0;
29682         this.baseScale = 1;
29683         this.rotate = 0;
29684         this.baseRotate = 1;
29685         this.dragable = false;
29686         this.pinching = false;
29687         this.mouseX = 0;
29688         this.mouseY = 0;
29689         this.cropData = false;
29690         this.notifyEl.dom.innerHTML = this.emptyText;
29691         
29692         this.selectorEl.dom.value = '';
29693         
29694     },
29695     
29696     resize : function()
29697     {
29698         if(this.fireEvent('resize', this) != false){
29699             this.setThumbBoxPosition();
29700             this.setCanvasPosition();
29701         }
29702     },
29703     
29704     onFooterButtonClick : function(e, el, o, type)
29705     {
29706         switch (type) {
29707             case 'rotate-left' :
29708                 this.onRotateLeft(e);
29709                 break;
29710             case 'rotate-right' :
29711                 this.onRotateRight(e);
29712                 break;
29713             case 'picture' :
29714                 this.beforeSelectFile(e);
29715                 break;
29716             case 'trash' :
29717                 this.trash(e);
29718                 break;
29719             case 'crop' :
29720                 this.crop(e);
29721                 break;
29722             case 'download' :
29723                 this.download(e);
29724                 break;
29725             default :
29726                 break;
29727         }
29728         
29729         this.fireEvent('footerbuttonclick', this, type);
29730     },
29731     
29732     beforeSelectFile : function(e)
29733     {
29734         e.preventDefault();
29735         
29736         if(this.fireEvent('beforeselectfile', this) != false){
29737             this.selectorEl.dom.click();
29738         }
29739     },
29740     
29741     onFileSelected : function(e)
29742     {
29743         e.preventDefault();
29744         
29745         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29746             return;
29747         }
29748         
29749         var file = this.selectorEl.dom.files[0];
29750         
29751         if(this.fireEvent('inspect', this, file) != false){
29752             this.prepare(file);
29753         }
29754         
29755     },
29756     
29757     trash : function(e)
29758     {
29759         this.fireEvent('trash', this);
29760     },
29761     
29762     download : function(e)
29763     {
29764         this.fireEvent('download', this);
29765     },
29766     
29767     loadCanvas : function(src)
29768     {   
29769         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29770             
29771             this.reset();
29772             
29773             this.imageEl = document.createElement('img');
29774             
29775             var _this = this;
29776             
29777             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29778             
29779             this.imageEl.src = src;
29780         }
29781     },
29782     
29783     onLoadCanvas : function()
29784     {   
29785         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29786         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29787         
29788         this.bodyEl.un('click', this.beforeSelectFile, this);
29789         
29790         this.notifyEl.hide();
29791         this.thumbEl.show();
29792         this.footerEl.show();
29793         
29794         this.baseRotateLevel();
29795         
29796         if(this.isDocument){
29797             this.setThumbBoxSize();
29798         }
29799         
29800         this.setThumbBoxPosition();
29801         
29802         this.baseScaleLevel();
29803         
29804         this.draw();
29805         
29806         this.resize();
29807         
29808         this.canvasLoaded = true;
29809         
29810         if(this.loadMask){
29811             this.maskEl.unmask();
29812         }
29813         
29814     },
29815     
29816     setCanvasPosition : function()
29817     {   
29818         if(!this.canvasEl){
29819             return;
29820         }
29821         
29822         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29823         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29824         
29825         this.previewEl.setLeft(pw);
29826         this.previewEl.setTop(ph);
29827         
29828     },
29829     
29830     onMouseDown : function(e)
29831     {   
29832         e.stopEvent();
29833         
29834         this.dragable = true;
29835         this.pinching = false;
29836         
29837         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29838             this.dragable = false;
29839             return;
29840         }
29841         
29842         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29843         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29844         
29845     },
29846     
29847     onMouseMove : function(e)
29848     {   
29849         e.stopEvent();
29850         
29851         if(!this.canvasLoaded){
29852             return;
29853         }
29854         
29855         if (!this.dragable){
29856             return;
29857         }
29858         
29859         var minX = Math.ceil(this.thumbEl.getLeft(true));
29860         var minY = Math.ceil(this.thumbEl.getTop(true));
29861         
29862         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29863         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29864         
29865         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29866         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29867         
29868         x = x - this.mouseX;
29869         y = y - this.mouseY;
29870         
29871         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29872         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29873         
29874         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29875         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29876         
29877         this.previewEl.setLeft(bgX);
29878         this.previewEl.setTop(bgY);
29879         
29880         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29881         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29882     },
29883     
29884     onMouseUp : function(e)
29885     {   
29886         e.stopEvent();
29887         
29888         this.dragable = false;
29889     },
29890     
29891     onMouseWheel : function(e)
29892     {   
29893         e.stopEvent();
29894         
29895         this.startScale = this.scale;
29896         
29897         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29898         
29899         if(!this.zoomable()){
29900             this.scale = this.startScale;
29901             return;
29902         }
29903         
29904         this.draw();
29905         
29906         return;
29907     },
29908     
29909     zoomable : function()
29910     {
29911         var minScale = this.thumbEl.getWidth() / this.minWidth;
29912         
29913         if(this.minWidth < this.minHeight){
29914             minScale = this.thumbEl.getHeight() / this.minHeight;
29915         }
29916         
29917         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29918         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29919         
29920         if(
29921                 this.isDocument &&
29922                 (this.rotate == 0 || this.rotate == 180) && 
29923                 (
29924                     width > this.imageEl.OriginWidth || 
29925                     height > this.imageEl.OriginHeight ||
29926                     (width < this.minWidth && height < this.minHeight)
29927                 )
29928         ){
29929             return false;
29930         }
29931         
29932         if(
29933                 this.isDocument &&
29934                 (this.rotate == 90 || this.rotate == 270) && 
29935                 (
29936                     width > this.imageEl.OriginWidth || 
29937                     height > this.imageEl.OriginHeight ||
29938                     (width < this.minHeight && height < this.minWidth)
29939                 )
29940         ){
29941             return false;
29942         }
29943         
29944         if(
29945                 !this.isDocument &&
29946                 (this.rotate == 0 || this.rotate == 180) && 
29947                 (
29948                     width < this.minWidth || 
29949                     width > this.imageEl.OriginWidth || 
29950                     height < this.minHeight || 
29951                     height > this.imageEl.OriginHeight
29952                 )
29953         ){
29954             return false;
29955         }
29956         
29957         if(
29958                 !this.isDocument &&
29959                 (this.rotate == 90 || this.rotate == 270) && 
29960                 (
29961                     width < this.minHeight || 
29962                     width > this.imageEl.OriginWidth || 
29963                     height < this.minWidth || 
29964                     height > this.imageEl.OriginHeight
29965                 )
29966         ){
29967             return false;
29968         }
29969         
29970         return true;
29971         
29972     },
29973     
29974     onRotateLeft : function(e)
29975     {   
29976         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29977             
29978             var minScale = this.thumbEl.getWidth() / this.minWidth;
29979             
29980             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29981             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29982             
29983             this.startScale = this.scale;
29984             
29985             while (this.getScaleLevel() < minScale){
29986             
29987                 this.scale = this.scale + 1;
29988                 
29989                 if(!this.zoomable()){
29990                     break;
29991                 }
29992                 
29993                 if(
29994                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29995                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29996                 ){
29997                     continue;
29998                 }
29999                 
30000                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30001
30002                 this.draw();
30003                 
30004                 return;
30005             }
30006             
30007             this.scale = this.startScale;
30008             
30009             this.onRotateFail();
30010             
30011             return false;
30012         }
30013         
30014         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30015
30016         if(this.isDocument){
30017             this.setThumbBoxSize();
30018             this.setThumbBoxPosition();
30019             this.setCanvasPosition();
30020         }
30021         
30022         this.draw();
30023         
30024         this.fireEvent('rotate', this, 'left');
30025         
30026     },
30027     
30028     onRotateRight : function(e)
30029     {
30030         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30031             
30032             var minScale = this.thumbEl.getWidth() / this.minWidth;
30033         
30034             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30035             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30036             
30037             this.startScale = this.scale;
30038             
30039             while (this.getScaleLevel() < minScale){
30040             
30041                 this.scale = this.scale + 1;
30042                 
30043                 if(!this.zoomable()){
30044                     break;
30045                 }
30046                 
30047                 if(
30048                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30049                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30050                 ){
30051                     continue;
30052                 }
30053                 
30054                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30055
30056                 this.draw();
30057                 
30058                 return;
30059             }
30060             
30061             this.scale = this.startScale;
30062             
30063             this.onRotateFail();
30064             
30065             return false;
30066         }
30067         
30068         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30069
30070         if(this.isDocument){
30071             this.setThumbBoxSize();
30072             this.setThumbBoxPosition();
30073             this.setCanvasPosition();
30074         }
30075         
30076         this.draw();
30077         
30078         this.fireEvent('rotate', this, 'right');
30079     },
30080     
30081     onRotateFail : function()
30082     {
30083         this.errorEl.show(true);
30084         
30085         var _this = this;
30086         
30087         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30088     },
30089     
30090     draw : function()
30091     {
30092         this.previewEl.dom.innerHTML = '';
30093         
30094         var canvasEl = document.createElement("canvas");
30095         
30096         var contextEl = canvasEl.getContext("2d");
30097         
30098         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30099         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30100         var center = this.imageEl.OriginWidth / 2;
30101         
30102         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30103             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30104             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30105             center = this.imageEl.OriginHeight / 2;
30106         }
30107         
30108         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30109         
30110         contextEl.translate(center, center);
30111         contextEl.rotate(this.rotate * Math.PI / 180);
30112
30113         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30114         
30115         this.canvasEl = document.createElement("canvas");
30116         
30117         this.contextEl = this.canvasEl.getContext("2d");
30118         
30119         switch (this.rotate) {
30120             case 0 :
30121                 
30122                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30123                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30124                 
30125                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30126                 
30127                 break;
30128             case 90 : 
30129                 
30130                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30131                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30132                 
30133                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30134                     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);
30135                     break;
30136                 }
30137                 
30138                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30139                 
30140                 break;
30141             case 180 :
30142                 
30143                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30144                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30145                 
30146                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30147                     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);
30148                     break;
30149                 }
30150                 
30151                 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);
30152                 
30153                 break;
30154             case 270 :
30155                 
30156                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30157                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30158         
30159                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30160                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30161                     break;
30162                 }
30163                 
30164                 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);
30165                 
30166                 break;
30167             default : 
30168                 break;
30169         }
30170         
30171         this.previewEl.appendChild(this.canvasEl);
30172         
30173         this.setCanvasPosition();
30174     },
30175     
30176     crop : function()
30177     {
30178         if(!this.canvasLoaded){
30179             return;
30180         }
30181         
30182         var imageCanvas = document.createElement("canvas");
30183         
30184         var imageContext = imageCanvas.getContext("2d");
30185         
30186         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30187         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30188         
30189         var center = imageCanvas.width / 2;
30190         
30191         imageContext.translate(center, center);
30192         
30193         imageContext.rotate(this.rotate * Math.PI / 180);
30194         
30195         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30196         
30197         var canvas = document.createElement("canvas");
30198         
30199         var context = canvas.getContext("2d");
30200                 
30201         canvas.width = this.minWidth;
30202         canvas.height = this.minHeight;
30203
30204         switch (this.rotate) {
30205             case 0 :
30206                 
30207                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30208                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30209                 
30210                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30211                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30212                 
30213                 var targetWidth = this.minWidth - 2 * x;
30214                 var targetHeight = this.minHeight - 2 * y;
30215                 
30216                 var scale = 1;
30217                 
30218                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30219                     scale = targetWidth / width;
30220                 }
30221                 
30222                 if(x > 0 && y == 0){
30223                     scale = targetHeight / height;
30224                 }
30225                 
30226                 if(x > 0 && y > 0){
30227                     scale = targetWidth / width;
30228                     
30229                     if(width < height){
30230                         scale = targetHeight / height;
30231                     }
30232                 }
30233                 
30234                 context.scale(scale, scale);
30235                 
30236                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30237                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30238
30239                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30240                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30241
30242                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30243                 
30244                 break;
30245             case 90 : 
30246                 
30247                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30248                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30249                 
30250                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30251                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30252                 
30253                 var targetWidth = this.minWidth - 2 * x;
30254                 var targetHeight = this.minHeight - 2 * y;
30255                 
30256                 var scale = 1;
30257                 
30258                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30259                     scale = targetWidth / width;
30260                 }
30261                 
30262                 if(x > 0 && y == 0){
30263                     scale = targetHeight / height;
30264                 }
30265                 
30266                 if(x > 0 && y > 0){
30267                     scale = targetWidth / width;
30268                     
30269                     if(width < height){
30270                         scale = targetHeight / height;
30271                     }
30272                 }
30273                 
30274                 context.scale(scale, scale);
30275                 
30276                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30277                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30278
30279                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30280                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30281                 
30282                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30283                 
30284                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30285                 
30286                 break;
30287             case 180 :
30288                 
30289                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30290                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30291                 
30292                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30293                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30294                 
30295                 var targetWidth = this.minWidth - 2 * x;
30296                 var targetHeight = this.minHeight - 2 * y;
30297                 
30298                 var scale = 1;
30299                 
30300                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30301                     scale = targetWidth / width;
30302                 }
30303                 
30304                 if(x > 0 && y == 0){
30305                     scale = targetHeight / height;
30306                 }
30307                 
30308                 if(x > 0 && y > 0){
30309                     scale = targetWidth / width;
30310                     
30311                     if(width < height){
30312                         scale = targetHeight / height;
30313                     }
30314                 }
30315                 
30316                 context.scale(scale, scale);
30317                 
30318                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30319                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30320
30321                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30322                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30323
30324                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30325                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30326                 
30327                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30328                 
30329                 break;
30330             case 270 :
30331                 
30332                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30333                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30334                 
30335                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30336                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30337                 
30338                 var targetWidth = this.minWidth - 2 * x;
30339                 var targetHeight = this.minHeight - 2 * y;
30340                 
30341                 var scale = 1;
30342                 
30343                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30344                     scale = targetWidth / width;
30345                 }
30346                 
30347                 if(x > 0 && y == 0){
30348                     scale = targetHeight / height;
30349                 }
30350                 
30351                 if(x > 0 && y > 0){
30352                     scale = targetWidth / width;
30353                     
30354                     if(width < height){
30355                         scale = targetHeight / height;
30356                     }
30357                 }
30358                 
30359                 context.scale(scale, scale);
30360                 
30361                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30362                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30363
30364                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30365                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30366                 
30367                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30368                 
30369                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30370                 
30371                 break;
30372             default : 
30373                 break;
30374         }
30375         
30376         this.cropData = canvas.toDataURL(this.cropType);
30377         
30378         if(this.fireEvent('crop', this, this.cropData) !== false){
30379             this.process(this.file, this.cropData);
30380         }
30381         
30382         return;
30383         
30384     },
30385     
30386     setThumbBoxSize : function()
30387     {
30388         var width, height;
30389         
30390         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30391             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30392             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30393             
30394             this.minWidth = width;
30395             this.minHeight = height;
30396             
30397             if(this.rotate == 90 || this.rotate == 270){
30398                 this.minWidth = height;
30399                 this.minHeight = width;
30400             }
30401         }
30402         
30403         height = 300;
30404         width = Math.ceil(this.minWidth * height / this.minHeight);
30405         
30406         if(this.minWidth > this.minHeight){
30407             width = 300;
30408             height = Math.ceil(this.minHeight * width / this.minWidth);
30409         }
30410         
30411         this.thumbEl.setStyle({
30412             width : width + 'px',
30413             height : height + 'px'
30414         });
30415
30416         return;
30417             
30418     },
30419     
30420     setThumbBoxPosition : function()
30421     {
30422         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30423         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30424         
30425         this.thumbEl.setLeft(x);
30426         this.thumbEl.setTop(y);
30427         
30428     },
30429     
30430     baseRotateLevel : function()
30431     {
30432         this.baseRotate = 1;
30433         
30434         if(
30435                 typeof(this.exif) != 'undefined' &&
30436                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30437                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30438         ){
30439             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30440         }
30441         
30442         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30443         
30444     },
30445     
30446     baseScaleLevel : function()
30447     {
30448         var width, height;
30449         
30450         if(this.isDocument){
30451             
30452             if(this.baseRotate == 6 || this.baseRotate == 8){
30453             
30454                 height = this.thumbEl.getHeight();
30455                 this.baseScale = height / this.imageEl.OriginWidth;
30456
30457                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30458                     width = this.thumbEl.getWidth();
30459                     this.baseScale = width / this.imageEl.OriginHeight;
30460                 }
30461
30462                 return;
30463             }
30464
30465             height = this.thumbEl.getHeight();
30466             this.baseScale = height / this.imageEl.OriginHeight;
30467
30468             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30469                 width = this.thumbEl.getWidth();
30470                 this.baseScale = width / this.imageEl.OriginWidth;
30471             }
30472
30473             return;
30474         }
30475         
30476         if(this.baseRotate == 6 || this.baseRotate == 8){
30477             
30478             width = this.thumbEl.getHeight();
30479             this.baseScale = width / this.imageEl.OriginHeight;
30480             
30481             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30482                 height = this.thumbEl.getWidth();
30483                 this.baseScale = height / this.imageEl.OriginHeight;
30484             }
30485             
30486             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30487                 height = this.thumbEl.getWidth();
30488                 this.baseScale = height / this.imageEl.OriginHeight;
30489                 
30490                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30491                     width = this.thumbEl.getHeight();
30492                     this.baseScale = width / this.imageEl.OriginWidth;
30493                 }
30494             }
30495             
30496             return;
30497         }
30498         
30499         width = this.thumbEl.getWidth();
30500         this.baseScale = width / this.imageEl.OriginWidth;
30501         
30502         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30503             height = this.thumbEl.getHeight();
30504             this.baseScale = height / this.imageEl.OriginHeight;
30505         }
30506         
30507         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30508             
30509             height = this.thumbEl.getHeight();
30510             this.baseScale = height / this.imageEl.OriginHeight;
30511             
30512             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30513                 width = this.thumbEl.getWidth();
30514                 this.baseScale = width / this.imageEl.OriginWidth;
30515             }
30516             
30517         }
30518         
30519         return;
30520     },
30521     
30522     getScaleLevel : function()
30523     {
30524         return this.baseScale * Math.pow(1.1, this.scale);
30525     },
30526     
30527     onTouchStart : function(e)
30528     {
30529         if(!this.canvasLoaded){
30530             this.beforeSelectFile(e);
30531             return;
30532         }
30533         
30534         var touches = e.browserEvent.touches;
30535         
30536         if(!touches){
30537             return;
30538         }
30539         
30540         if(touches.length == 1){
30541             this.onMouseDown(e);
30542             return;
30543         }
30544         
30545         if(touches.length != 2){
30546             return;
30547         }
30548         
30549         var coords = [];
30550         
30551         for(var i = 0, finger; finger = touches[i]; i++){
30552             coords.push(finger.pageX, finger.pageY);
30553         }
30554         
30555         var x = Math.pow(coords[0] - coords[2], 2);
30556         var y = Math.pow(coords[1] - coords[3], 2);
30557         
30558         this.startDistance = Math.sqrt(x + y);
30559         
30560         this.startScale = this.scale;
30561         
30562         this.pinching = true;
30563         this.dragable = false;
30564         
30565     },
30566     
30567     onTouchMove : function(e)
30568     {
30569         if(!this.pinching && !this.dragable){
30570             return;
30571         }
30572         
30573         var touches = e.browserEvent.touches;
30574         
30575         if(!touches){
30576             return;
30577         }
30578         
30579         if(this.dragable){
30580             this.onMouseMove(e);
30581             return;
30582         }
30583         
30584         var coords = [];
30585         
30586         for(var i = 0, finger; finger = touches[i]; i++){
30587             coords.push(finger.pageX, finger.pageY);
30588         }
30589         
30590         var x = Math.pow(coords[0] - coords[2], 2);
30591         var y = Math.pow(coords[1] - coords[3], 2);
30592         
30593         this.endDistance = Math.sqrt(x + y);
30594         
30595         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30596         
30597         if(!this.zoomable()){
30598             this.scale = this.startScale;
30599             return;
30600         }
30601         
30602         this.draw();
30603         
30604     },
30605     
30606     onTouchEnd : function(e)
30607     {
30608         this.pinching = false;
30609         this.dragable = false;
30610         
30611     },
30612     
30613     process : function(file, crop)
30614     {
30615         if(this.loadMask){
30616             this.maskEl.mask(this.loadingText);
30617         }
30618         
30619         this.xhr = new XMLHttpRequest();
30620         
30621         file.xhr = this.xhr;
30622
30623         this.xhr.open(this.method, this.url, true);
30624         
30625         var headers = {
30626             "Accept": "application/json",
30627             "Cache-Control": "no-cache",
30628             "X-Requested-With": "XMLHttpRequest"
30629         };
30630         
30631         for (var headerName in headers) {
30632             var headerValue = headers[headerName];
30633             if (headerValue) {
30634                 this.xhr.setRequestHeader(headerName, headerValue);
30635             }
30636         }
30637         
30638         var _this = this;
30639         
30640         this.xhr.onload = function()
30641         {
30642             _this.xhrOnLoad(_this.xhr);
30643         }
30644         
30645         this.xhr.onerror = function()
30646         {
30647             _this.xhrOnError(_this.xhr);
30648         }
30649         
30650         var formData = new FormData();
30651
30652         formData.append('returnHTML', 'NO');
30653         
30654         if(crop){
30655             formData.append('crop', crop);
30656         }
30657         
30658         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30659             formData.append(this.paramName, file, file.name);
30660         }
30661         
30662         if(typeof(file.filename) != 'undefined'){
30663             formData.append('filename', file.filename);
30664         }
30665         
30666         if(typeof(file.mimetype) != 'undefined'){
30667             formData.append('mimetype', file.mimetype);
30668         }
30669         
30670         if(this.fireEvent('arrange', this, formData) != false){
30671             this.xhr.send(formData);
30672         };
30673     },
30674     
30675     xhrOnLoad : function(xhr)
30676     {
30677         if(this.loadMask){
30678             this.maskEl.unmask();
30679         }
30680         
30681         if (xhr.readyState !== 4) {
30682             this.fireEvent('exception', this, xhr);
30683             return;
30684         }
30685
30686         var response = Roo.decode(xhr.responseText);
30687         
30688         if(!response.success){
30689             this.fireEvent('exception', this, xhr);
30690             return;
30691         }
30692         
30693         var response = Roo.decode(xhr.responseText);
30694         
30695         this.fireEvent('upload', this, response);
30696         
30697     },
30698     
30699     xhrOnError : function()
30700     {
30701         if(this.loadMask){
30702             this.maskEl.unmask();
30703         }
30704         
30705         Roo.log('xhr on error');
30706         
30707         var response = Roo.decode(xhr.responseText);
30708           
30709         Roo.log(response);
30710         
30711     },
30712     
30713     prepare : function(file)
30714     {   
30715         if(this.loadMask){
30716             this.maskEl.mask(this.loadingText);
30717         }
30718         
30719         this.file = false;
30720         this.exif = {};
30721         
30722         if(typeof(file) === 'string'){
30723             this.loadCanvas(file);
30724             return;
30725         }
30726         
30727         if(!file || !this.urlAPI){
30728             return;
30729         }
30730         
30731         this.file = file;
30732         this.cropType = file.type;
30733         
30734         var _this = this;
30735         
30736         if(this.fireEvent('prepare', this, this.file) != false){
30737             
30738             var reader = new FileReader();
30739             
30740             reader.onload = function (e) {
30741                 if (e.target.error) {
30742                     Roo.log(e.target.error);
30743                     return;
30744                 }
30745                 
30746                 var buffer = e.target.result,
30747                     dataView = new DataView(buffer),
30748                     offset = 2,
30749                     maxOffset = dataView.byteLength - 4,
30750                     markerBytes,
30751                     markerLength;
30752                 
30753                 if (dataView.getUint16(0) === 0xffd8) {
30754                     while (offset < maxOffset) {
30755                         markerBytes = dataView.getUint16(offset);
30756                         
30757                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30758                             markerLength = dataView.getUint16(offset + 2) + 2;
30759                             if (offset + markerLength > dataView.byteLength) {
30760                                 Roo.log('Invalid meta data: Invalid segment size.');
30761                                 break;
30762                             }
30763                             
30764                             if(markerBytes == 0xffe1){
30765                                 _this.parseExifData(
30766                                     dataView,
30767                                     offset,
30768                                     markerLength
30769                                 );
30770                             }
30771                             
30772                             offset += markerLength;
30773                             
30774                             continue;
30775                         }
30776                         
30777                         break;
30778                     }
30779                     
30780                 }
30781                 
30782                 var url = _this.urlAPI.createObjectURL(_this.file);
30783                 
30784                 _this.loadCanvas(url);
30785                 
30786                 return;
30787             }
30788             
30789             reader.readAsArrayBuffer(this.file);
30790             
30791         }
30792         
30793     },
30794     
30795     parseExifData : function(dataView, offset, length)
30796     {
30797         var tiffOffset = offset + 10,
30798             littleEndian,
30799             dirOffset;
30800     
30801         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30802             // No Exif data, might be XMP data instead
30803             return;
30804         }
30805         
30806         // Check for the ASCII code for "Exif" (0x45786966):
30807         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30808             // No Exif data, might be XMP data instead
30809             return;
30810         }
30811         if (tiffOffset + 8 > dataView.byteLength) {
30812             Roo.log('Invalid Exif data: Invalid segment size.');
30813             return;
30814         }
30815         // Check for the two null bytes:
30816         if (dataView.getUint16(offset + 8) !== 0x0000) {
30817             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30818             return;
30819         }
30820         // Check the byte alignment:
30821         switch (dataView.getUint16(tiffOffset)) {
30822         case 0x4949:
30823             littleEndian = true;
30824             break;
30825         case 0x4D4D:
30826             littleEndian = false;
30827             break;
30828         default:
30829             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30830             return;
30831         }
30832         // Check for the TIFF tag marker (0x002A):
30833         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30834             Roo.log('Invalid Exif data: Missing TIFF marker.');
30835             return;
30836         }
30837         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30838         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30839         
30840         this.parseExifTags(
30841             dataView,
30842             tiffOffset,
30843             tiffOffset + dirOffset,
30844             littleEndian
30845         );
30846     },
30847     
30848     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30849     {
30850         var tagsNumber,
30851             dirEndOffset,
30852             i;
30853         if (dirOffset + 6 > dataView.byteLength) {
30854             Roo.log('Invalid Exif data: Invalid directory offset.');
30855             return;
30856         }
30857         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30858         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30859         if (dirEndOffset + 4 > dataView.byteLength) {
30860             Roo.log('Invalid Exif data: Invalid directory size.');
30861             return;
30862         }
30863         for (i = 0; i < tagsNumber; i += 1) {
30864             this.parseExifTag(
30865                 dataView,
30866                 tiffOffset,
30867                 dirOffset + 2 + 12 * i, // tag offset
30868                 littleEndian
30869             );
30870         }
30871         // Return the offset to the next directory:
30872         return dataView.getUint32(dirEndOffset, littleEndian);
30873     },
30874     
30875     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30876     {
30877         var tag = dataView.getUint16(offset, littleEndian);
30878         
30879         this.exif[tag] = this.getExifValue(
30880             dataView,
30881             tiffOffset,
30882             offset,
30883             dataView.getUint16(offset + 2, littleEndian), // tag type
30884             dataView.getUint32(offset + 4, littleEndian), // tag length
30885             littleEndian
30886         );
30887     },
30888     
30889     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30890     {
30891         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30892             tagSize,
30893             dataOffset,
30894             values,
30895             i,
30896             str,
30897             c;
30898     
30899         if (!tagType) {
30900             Roo.log('Invalid Exif data: Invalid tag type.');
30901             return;
30902         }
30903         
30904         tagSize = tagType.size * length;
30905         // Determine if the value is contained in the dataOffset bytes,
30906         // or if the value at the dataOffset is a pointer to the actual data:
30907         dataOffset = tagSize > 4 ?
30908                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30909         if (dataOffset + tagSize > dataView.byteLength) {
30910             Roo.log('Invalid Exif data: Invalid data offset.');
30911             return;
30912         }
30913         if (length === 1) {
30914             return tagType.getValue(dataView, dataOffset, littleEndian);
30915         }
30916         values = [];
30917         for (i = 0; i < length; i += 1) {
30918             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30919         }
30920         
30921         if (tagType.ascii) {
30922             str = '';
30923             // Concatenate the chars:
30924             for (i = 0; i < values.length; i += 1) {
30925                 c = values[i];
30926                 // Ignore the terminating NULL byte(s):
30927                 if (c === '\u0000') {
30928                     break;
30929                 }
30930                 str += c;
30931             }
30932             return str;
30933         }
30934         return values;
30935     }
30936     
30937 });
30938
30939 Roo.apply(Roo.bootstrap.UploadCropbox, {
30940     tags : {
30941         'Orientation': 0x0112
30942     },
30943     
30944     Orientation: {
30945             1: 0, //'top-left',
30946 //            2: 'top-right',
30947             3: 180, //'bottom-right',
30948 //            4: 'bottom-left',
30949 //            5: 'left-top',
30950             6: 90, //'right-top',
30951 //            7: 'right-bottom',
30952             8: 270 //'left-bottom'
30953     },
30954     
30955     exifTagTypes : {
30956         // byte, 8-bit unsigned int:
30957         1: {
30958             getValue: function (dataView, dataOffset) {
30959                 return dataView.getUint8(dataOffset);
30960             },
30961             size: 1
30962         },
30963         // ascii, 8-bit byte:
30964         2: {
30965             getValue: function (dataView, dataOffset) {
30966                 return String.fromCharCode(dataView.getUint8(dataOffset));
30967             },
30968             size: 1,
30969             ascii: true
30970         },
30971         // short, 16 bit int:
30972         3: {
30973             getValue: function (dataView, dataOffset, littleEndian) {
30974                 return dataView.getUint16(dataOffset, littleEndian);
30975             },
30976             size: 2
30977         },
30978         // long, 32 bit int:
30979         4: {
30980             getValue: function (dataView, dataOffset, littleEndian) {
30981                 return dataView.getUint32(dataOffset, littleEndian);
30982             },
30983             size: 4
30984         },
30985         // rational = two long values, first is numerator, second is denominator:
30986         5: {
30987             getValue: function (dataView, dataOffset, littleEndian) {
30988                 return dataView.getUint32(dataOffset, littleEndian) /
30989                     dataView.getUint32(dataOffset + 4, littleEndian);
30990             },
30991             size: 8
30992         },
30993         // slong, 32 bit signed int:
30994         9: {
30995             getValue: function (dataView, dataOffset, littleEndian) {
30996                 return dataView.getInt32(dataOffset, littleEndian);
30997             },
30998             size: 4
30999         },
31000         // srational, two slongs, first is numerator, second is denominator:
31001         10: {
31002             getValue: function (dataView, dataOffset, littleEndian) {
31003                 return dataView.getInt32(dataOffset, littleEndian) /
31004                     dataView.getInt32(dataOffset + 4, littleEndian);
31005             },
31006             size: 8
31007         }
31008     },
31009     
31010     footer : {
31011         STANDARD : [
31012             {
31013                 tag : 'div',
31014                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31015                 action : 'rotate-left',
31016                 cn : [
31017                     {
31018                         tag : 'button',
31019                         cls : 'btn btn-default',
31020                         html : '<i class="fa fa-undo"></i>'
31021                     }
31022                 ]
31023             },
31024             {
31025                 tag : 'div',
31026                 cls : 'btn-group roo-upload-cropbox-picture',
31027                 action : 'picture',
31028                 cn : [
31029                     {
31030                         tag : 'button',
31031                         cls : 'btn btn-default',
31032                         html : '<i class="fa fa-picture-o"></i>'
31033                     }
31034                 ]
31035             },
31036             {
31037                 tag : 'div',
31038                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31039                 action : 'rotate-right',
31040                 cn : [
31041                     {
31042                         tag : 'button',
31043                         cls : 'btn btn-default',
31044                         html : '<i class="fa fa-repeat"></i>'
31045                     }
31046                 ]
31047             }
31048         ],
31049         DOCUMENT : [
31050             {
31051                 tag : 'div',
31052                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31053                 action : 'rotate-left',
31054                 cn : [
31055                     {
31056                         tag : 'button',
31057                         cls : 'btn btn-default',
31058                         html : '<i class="fa fa-undo"></i>'
31059                     }
31060                 ]
31061             },
31062             {
31063                 tag : 'div',
31064                 cls : 'btn-group roo-upload-cropbox-download',
31065                 action : 'download',
31066                 cn : [
31067                     {
31068                         tag : 'button',
31069                         cls : 'btn btn-default',
31070                         html : '<i class="fa fa-download"></i>'
31071                     }
31072                 ]
31073             },
31074             {
31075                 tag : 'div',
31076                 cls : 'btn-group roo-upload-cropbox-crop',
31077                 action : 'crop',
31078                 cn : [
31079                     {
31080                         tag : 'button',
31081                         cls : 'btn btn-default',
31082                         html : '<i class="fa fa-crop"></i>'
31083                     }
31084                 ]
31085             },
31086             {
31087                 tag : 'div',
31088                 cls : 'btn-group roo-upload-cropbox-trash',
31089                 action : 'trash',
31090                 cn : [
31091                     {
31092                         tag : 'button',
31093                         cls : 'btn btn-default',
31094                         html : '<i class="fa fa-trash"></i>'
31095                     }
31096                 ]
31097             },
31098             {
31099                 tag : 'div',
31100                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31101                 action : 'rotate-right',
31102                 cn : [
31103                     {
31104                         tag : 'button',
31105                         cls : 'btn btn-default',
31106                         html : '<i class="fa fa-repeat"></i>'
31107                     }
31108                 ]
31109             }
31110         ],
31111         ROTATOR : [
31112             {
31113                 tag : 'div',
31114                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31115                 action : 'rotate-left',
31116                 cn : [
31117                     {
31118                         tag : 'button',
31119                         cls : 'btn btn-default',
31120                         html : '<i class="fa fa-undo"></i>'
31121                     }
31122                 ]
31123             },
31124             {
31125                 tag : 'div',
31126                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31127                 action : 'rotate-right',
31128                 cn : [
31129                     {
31130                         tag : 'button',
31131                         cls : 'btn btn-default',
31132                         html : '<i class="fa fa-repeat"></i>'
31133                     }
31134                 ]
31135             }
31136         ]
31137     }
31138 });
31139
31140 /*
31141 * Licence: LGPL
31142 */
31143
31144 /**
31145  * @class Roo.bootstrap.DocumentManager
31146  * @extends Roo.bootstrap.Component
31147  * Bootstrap DocumentManager class
31148  * @cfg {String} paramName default 'imageUpload'
31149  * @cfg {String} toolTipName default 'filename'
31150  * @cfg {String} method default POST
31151  * @cfg {String} url action url
31152  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31153  * @cfg {Boolean} multiple multiple upload default true
31154  * @cfg {Number} thumbSize default 300
31155  * @cfg {String} fieldLabel
31156  * @cfg {Number} labelWidth default 4
31157  * @cfg {String} labelAlign (left|top) default left
31158  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31159 * @cfg {Number} labellg set the width of label (1-12)
31160  * @cfg {Number} labelmd set the width of label (1-12)
31161  * @cfg {Number} labelsm set the width of label (1-12)
31162  * @cfg {Number} labelxs set the width of label (1-12)
31163  * 
31164  * @constructor
31165  * Create a new DocumentManager
31166  * @param {Object} config The config object
31167  */
31168
31169 Roo.bootstrap.DocumentManager = function(config){
31170     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31171     
31172     this.files = [];
31173     this.delegates = [];
31174     
31175     this.addEvents({
31176         /**
31177          * @event initial
31178          * Fire when initial the DocumentManager
31179          * @param {Roo.bootstrap.DocumentManager} this
31180          */
31181         "initial" : true,
31182         /**
31183          * @event inspect
31184          * inspect selected file
31185          * @param {Roo.bootstrap.DocumentManager} this
31186          * @param {File} file
31187          */
31188         "inspect" : true,
31189         /**
31190          * @event exception
31191          * Fire when xhr load exception
31192          * @param {Roo.bootstrap.DocumentManager} this
31193          * @param {XMLHttpRequest} xhr
31194          */
31195         "exception" : true,
31196         /**
31197          * @event afterupload
31198          * Fire when xhr load exception
31199          * @param {Roo.bootstrap.DocumentManager} this
31200          * @param {XMLHttpRequest} xhr
31201          */
31202         "afterupload" : true,
31203         /**
31204          * @event prepare
31205          * prepare the form data
31206          * @param {Roo.bootstrap.DocumentManager} this
31207          * @param {Object} formData
31208          */
31209         "prepare" : true,
31210         /**
31211          * @event remove
31212          * Fire when remove the file
31213          * @param {Roo.bootstrap.DocumentManager} this
31214          * @param {Object} file
31215          */
31216         "remove" : true,
31217         /**
31218          * @event refresh
31219          * Fire after refresh the file
31220          * @param {Roo.bootstrap.DocumentManager} this
31221          */
31222         "refresh" : true,
31223         /**
31224          * @event click
31225          * Fire after click the image
31226          * @param {Roo.bootstrap.DocumentManager} this
31227          * @param {Object} file
31228          */
31229         "click" : true,
31230         /**
31231          * @event edit
31232          * Fire when upload a image and editable set to true
31233          * @param {Roo.bootstrap.DocumentManager} this
31234          * @param {Object} file
31235          */
31236         "edit" : true,
31237         /**
31238          * @event beforeselectfile
31239          * Fire before select file
31240          * @param {Roo.bootstrap.DocumentManager} this
31241          */
31242         "beforeselectfile" : true,
31243         /**
31244          * @event process
31245          * Fire before process file
31246          * @param {Roo.bootstrap.DocumentManager} this
31247          * @param {Object} file
31248          */
31249         "process" : true,
31250         /**
31251          * @event previewrendered
31252          * Fire when preview rendered
31253          * @param {Roo.bootstrap.DocumentManager} this
31254          * @param {Object} file
31255          */
31256         "previewrendered" : true,
31257         /**
31258          */
31259         "previewResize" : true
31260         
31261     });
31262 };
31263
31264 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31265     
31266     boxes : 0,
31267     inputName : '',
31268     thumbSize : 300,
31269     multiple : true,
31270     files : false,
31271     method : 'POST',
31272     url : '',
31273     paramName : 'imageUpload',
31274     toolTipName : 'filename',
31275     fieldLabel : '',
31276     labelWidth : 4,
31277     labelAlign : 'left',
31278     editable : true,
31279     delegates : false,
31280     xhr : false, 
31281     
31282     labellg : 0,
31283     labelmd : 0,
31284     labelsm : 0,
31285     labelxs : 0,
31286     
31287     getAutoCreate : function()
31288     {   
31289         var managerWidget = {
31290             tag : 'div',
31291             cls : 'roo-document-manager',
31292             cn : [
31293                 {
31294                     tag : 'input',
31295                     cls : 'roo-document-manager-selector',
31296                     type : 'file'
31297                 },
31298                 {
31299                     tag : 'div',
31300                     cls : 'roo-document-manager-uploader',
31301                     cn : [
31302                         {
31303                             tag : 'div',
31304                             cls : 'roo-document-manager-upload-btn',
31305                             html : '<i class="fa fa-plus"></i>'
31306                         }
31307                     ]
31308                     
31309                 }
31310             ]
31311         };
31312         
31313         var content = [
31314             {
31315                 tag : 'div',
31316                 cls : 'column col-md-12',
31317                 cn : managerWidget
31318             }
31319         ];
31320         
31321         if(this.fieldLabel.length){
31322             
31323             content = [
31324                 {
31325                     tag : 'div',
31326                     cls : 'column col-md-12',
31327                     html : this.fieldLabel
31328                 },
31329                 {
31330                     tag : 'div',
31331                     cls : 'column col-md-12',
31332                     cn : managerWidget
31333                 }
31334             ];
31335
31336             if(this.labelAlign == 'left'){
31337                 content = [
31338                     {
31339                         tag : 'div',
31340                         cls : 'column',
31341                         html : this.fieldLabel
31342                     },
31343                     {
31344                         tag : 'div',
31345                         cls : 'column',
31346                         cn : managerWidget
31347                     }
31348                 ];
31349                 
31350                 if(this.labelWidth > 12){
31351                     content[0].style = "width: " + this.labelWidth + 'px';
31352                 }
31353
31354                 if(this.labelWidth < 13 && this.labelmd == 0){
31355                     this.labelmd = this.labelWidth;
31356                 }
31357
31358                 if(this.labellg > 0){
31359                     content[0].cls += ' col-lg-' + this.labellg;
31360                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31361                 }
31362
31363                 if(this.labelmd > 0){
31364                     content[0].cls += ' col-md-' + this.labelmd;
31365                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31366                 }
31367
31368                 if(this.labelsm > 0){
31369                     content[0].cls += ' col-sm-' + this.labelsm;
31370                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31371                 }
31372
31373                 if(this.labelxs > 0){
31374                     content[0].cls += ' col-xs-' + this.labelxs;
31375                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31376                 }
31377                 
31378             }
31379         }
31380         
31381         var cfg = {
31382             tag : 'div',
31383             cls : 'row clearfix',
31384             cn : content
31385         };
31386         
31387         return cfg;
31388         
31389     },
31390     
31391     initEvents : function()
31392     {
31393         this.managerEl = this.el.select('.roo-document-manager', true).first();
31394         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31395         
31396         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31397         this.selectorEl.hide();
31398         
31399         if(this.multiple){
31400             this.selectorEl.attr('multiple', 'multiple');
31401         }
31402         
31403         this.selectorEl.on('change', this.onFileSelected, this);
31404         
31405         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31406         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31407         
31408         this.uploader.on('click', this.onUploaderClick, this);
31409         
31410         this.renderProgressDialog();
31411         
31412         var _this = this;
31413         
31414         window.addEventListener("resize", function() { _this.refresh(); } );
31415         
31416         this.fireEvent('initial', this);
31417     },
31418     
31419     renderProgressDialog : function()
31420     {
31421         var _this = this;
31422         
31423         this.progressDialog = new Roo.bootstrap.Modal({
31424             cls : 'roo-document-manager-progress-dialog',
31425             allow_close : false,
31426             animate : false,
31427             title : '',
31428             buttons : [
31429                 {
31430                     name  :'cancel',
31431                     weight : 'danger',
31432                     html : 'Cancel'
31433                 }
31434             ], 
31435             listeners : { 
31436                 btnclick : function() {
31437                     _this.uploadCancel();
31438                     this.hide();
31439                 }
31440             }
31441         });
31442          
31443         this.progressDialog.render(Roo.get(document.body));
31444          
31445         this.progress = new Roo.bootstrap.Progress({
31446             cls : 'roo-document-manager-progress',
31447             active : true,
31448             striped : true
31449         });
31450         
31451         this.progress.render(this.progressDialog.getChildContainer());
31452         
31453         this.progressBar = new Roo.bootstrap.ProgressBar({
31454             cls : 'roo-document-manager-progress-bar',
31455             aria_valuenow : 0,
31456             aria_valuemin : 0,
31457             aria_valuemax : 12,
31458             panel : 'success'
31459         });
31460         
31461         this.progressBar.render(this.progress.getChildContainer());
31462     },
31463     
31464     onUploaderClick : function(e)
31465     {
31466         e.preventDefault();
31467      
31468         if(this.fireEvent('beforeselectfile', this) != false){
31469             this.selectorEl.dom.click();
31470         }
31471         
31472     },
31473     
31474     onFileSelected : function(e)
31475     {
31476         e.preventDefault();
31477         
31478         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31479             return;
31480         }
31481         
31482         Roo.each(this.selectorEl.dom.files, function(file){
31483             if(this.fireEvent('inspect', this, file) != false){
31484                 this.files.push(file);
31485             }
31486         }, this);
31487         
31488         this.queue();
31489         
31490     },
31491     
31492     queue : function()
31493     {
31494         this.selectorEl.dom.value = '';
31495         
31496         if(!this.files || !this.files.length){
31497             return;
31498         }
31499         
31500         if(this.boxes > 0 && this.files.length > this.boxes){
31501             this.files = this.files.slice(0, this.boxes);
31502         }
31503         
31504         this.uploader.show();
31505         
31506         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31507             this.uploader.hide();
31508         }
31509         
31510         var _this = this;
31511         
31512         var files = [];
31513         
31514         var docs = [];
31515         
31516         Roo.each(this.files, function(file){
31517             
31518             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31519                 var f = this.renderPreview(file);
31520                 files.push(f);
31521                 return;
31522             }
31523             
31524             if(file.type.indexOf('image') != -1){
31525                 this.delegates.push(
31526                     (function(){
31527                         _this.process(file);
31528                     }).createDelegate(this)
31529                 );
31530         
31531                 return;
31532             }
31533             
31534             docs.push(
31535                 (function(){
31536                     _this.process(file);
31537                 }).createDelegate(this)
31538             );
31539             
31540         }, this);
31541         
31542         this.files = files;
31543         
31544         this.delegates = this.delegates.concat(docs);
31545         
31546         if(!this.delegates.length){
31547             this.refresh();
31548             return;
31549         }
31550         
31551         this.progressBar.aria_valuemax = this.delegates.length;
31552         
31553         this.arrange();
31554         
31555         return;
31556     },
31557     
31558     arrange : function()
31559     {
31560         if(!this.delegates.length){
31561             this.progressDialog.hide();
31562             this.refresh();
31563             return;
31564         }
31565         
31566         var delegate = this.delegates.shift();
31567         
31568         this.progressDialog.show();
31569         
31570         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31571         
31572         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31573         
31574         delegate();
31575     },
31576     
31577     refresh : function()
31578     {
31579         this.uploader.show();
31580         
31581         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31582             this.uploader.hide();
31583         }
31584         
31585         Roo.isTouch ? this.closable(false) : this.closable(true);
31586         
31587         this.fireEvent('refresh', this);
31588     },
31589     
31590     onRemove : function(e, el, o)
31591     {
31592         e.preventDefault();
31593         
31594         this.fireEvent('remove', this, o);
31595         
31596     },
31597     
31598     remove : function(o)
31599     {
31600         var files = [];
31601         
31602         Roo.each(this.files, function(file){
31603             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31604                 files.push(file);
31605                 return;
31606             }
31607
31608             o.target.remove();
31609
31610         }, this);
31611         
31612         this.files = files;
31613         
31614         this.refresh();
31615     },
31616     
31617     clear : function()
31618     {
31619         Roo.each(this.files, function(file){
31620             if(!file.target){
31621                 return;
31622             }
31623             
31624             file.target.remove();
31625
31626         }, this);
31627         
31628         this.files = [];
31629         
31630         this.refresh();
31631     },
31632     
31633     onClick : function(e, el, o)
31634     {
31635         e.preventDefault();
31636         
31637         this.fireEvent('click', this, o);
31638         
31639     },
31640     
31641     closable : function(closable)
31642     {
31643         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31644             
31645             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31646             
31647             if(closable){
31648                 el.show();
31649                 return;
31650             }
31651             
31652             el.hide();
31653             
31654         }, this);
31655     },
31656     
31657     xhrOnLoad : function(xhr)
31658     {
31659         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31660             el.remove();
31661         }, this);
31662         
31663         if (xhr.readyState !== 4) {
31664             this.arrange();
31665             this.fireEvent('exception', this, xhr);
31666             return;
31667         }
31668
31669         var response = Roo.decode(xhr.responseText);
31670         
31671         if(!response.success){
31672             this.arrange();
31673             this.fireEvent('exception', this, xhr);
31674             return;
31675         }
31676         
31677         var file = this.renderPreview(response.data);
31678         
31679         this.files.push(file);
31680         
31681         this.arrange();
31682         
31683         this.fireEvent('afterupload', this, xhr);
31684         
31685     },
31686     
31687     xhrOnError : function(xhr)
31688     {
31689         Roo.log('xhr on error');
31690         
31691         var response = Roo.decode(xhr.responseText);
31692           
31693         Roo.log(response);
31694         
31695         this.arrange();
31696     },
31697     
31698     process : function(file)
31699     {
31700         if(this.fireEvent('process', this, file) !== false){
31701             if(this.editable && file.type.indexOf('image') != -1){
31702                 this.fireEvent('edit', this, file);
31703                 return;
31704             }
31705
31706             this.uploadStart(file, false);
31707
31708             return;
31709         }
31710         
31711     },
31712     
31713     uploadStart : function(file, crop)
31714     {
31715         this.xhr = new XMLHttpRequest();
31716         
31717         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31718             this.arrange();
31719             return;
31720         }
31721         
31722         file.xhr = this.xhr;
31723             
31724         this.managerEl.createChild({
31725             tag : 'div',
31726             cls : 'roo-document-manager-loading',
31727             cn : [
31728                 {
31729                     tag : 'div',
31730                     tooltip : file.name,
31731                     cls : 'roo-document-manager-thumb',
31732                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31733                 }
31734             ]
31735
31736         });
31737
31738         this.xhr.open(this.method, this.url, true);
31739         
31740         var headers = {
31741             "Accept": "application/json",
31742             "Cache-Control": "no-cache",
31743             "X-Requested-With": "XMLHttpRequest"
31744         };
31745         
31746         for (var headerName in headers) {
31747             var headerValue = headers[headerName];
31748             if (headerValue) {
31749                 this.xhr.setRequestHeader(headerName, headerValue);
31750             }
31751         }
31752         
31753         var _this = this;
31754         
31755         this.xhr.onload = function()
31756         {
31757             _this.xhrOnLoad(_this.xhr);
31758         }
31759         
31760         this.xhr.onerror = function()
31761         {
31762             _this.xhrOnError(_this.xhr);
31763         }
31764         
31765         var formData = new FormData();
31766
31767         formData.append('returnHTML', 'NO');
31768         
31769         if(crop){
31770             formData.append('crop', crop);
31771         }
31772         
31773         formData.append(this.paramName, file, file.name);
31774         
31775         var options = {
31776             file : file, 
31777             manually : false
31778         };
31779         
31780         if(this.fireEvent('prepare', this, formData, options) != false){
31781             
31782             if(options.manually){
31783                 return;
31784             }
31785             
31786             this.xhr.send(formData);
31787             return;
31788         };
31789         
31790         this.uploadCancel();
31791     },
31792     
31793     uploadCancel : function()
31794     {
31795         if (this.xhr) {
31796             this.xhr.abort();
31797         }
31798         
31799         this.delegates = [];
31800         
31801         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31802             el.remove();
31803         }, this);
31804         
31805         this.arrange();
31806     },
31807     
31808     renderPreview : function(file)
31809     {
31810         if(typeof(file.target) != 'undefined' && file.target){
31811             return file;
31812         }
31813         
31814         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31815         
31816         var previewEl = this.managerEl.createChild({
31817             tag : 'div',
31818             cls : 'roo-document-manager-preview',
31819             cn : [
31820                 {
31821                     tag : 'div',
31822                     tooltip : file[this.toolTipName],
31823                     cls : 'roo-document-manager-thumb',
31824                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31825                 },
31826                 {
31827                     tag : 'button',
31828                     cls : 'close',
31829                     html : '<i class="fa fa-times-circle"></i>'
31830                 }
31831             ]
31832         });
31833
31834         var close = previewEl.select('button.close', true).first();
31835
31836         close.on('click', this.onRemove, this, file);
31837
31838         file.target = previewEl;
31839
31840         var image = previewEl.select('img', true).first();
31841         
31842         var _this = this;
31843         
31844         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31845         
31846         image.on('click', this.onClick, this, file);
31847         
31848         this.fireEvent('previewrendered', this, file);
31849         
31850         return file;
31851         
31852     },
31853     
31854     onPreviewLoad : function(file, image)
31855     {
31856         if(typeof(file.target) == 'undefined' || !file.target){
31857             return;
31858         }
31859         
31860         var width = image.dom.naturalWidth || image.dom.width;
31861         var height = image.dom.naturalHeight || image.dom.height;
31862         
31863         if(!this.previewResize) {
31864             return;
31865         }
31866         
31867         if(width > height){
31868             file.target.addClass('wide');
31869             return;
31870         }
31871         
31872         file.target.addClass('tall');
31873         return;
31874         
31875     },
31876     
31877     uploadFromSource : function(file, crop)
31878     {
31879         this.xhr = new XMLHttpRequest();
31880         
31881         this.managerEl.createChild({
31882             tag : 'div',
31883             cls : 'roo-document-manager-loading',
31884             cn : [
31885                 {
31886                     tag : 'div',
31887                     tooltip : file.name,
31888                     cls : 'roo-document-manager-thumb',
31889                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31890                 }
31891             ]
31892
31893         });
31894
31895         this.xhr.open(this.method, this.url, true);
31896         
31897         var headers = {
31898             "Accept": "application/json",
31899             "Cache-Control": "no-cache",
31900             "X-Requested-With": "XMLHttpRequest"
31901         };
31902         
31903         for (var headerName in headers) {
31904             var headerValue = headers[headerName];
31905             if (headerValue) {
31906                 this.xhr.setRequestHeader(headerName, headerValue);
31907             }
31908         }
31909         
31910         var _this = this;
31911         
31912         this.xhr.onload = function()
31913         {
31914             _this.xhrOnLoad(_this.xhr);
31915         }
31916         
31917         this.xhr.onerror = function()
31918         {
31919             _this.xhrOnError(_this.xhr);
31920         }
31921         
31922         var formData = new FormData();
31923
31924         formData.append('returnHTML', 'NO');
31925         
31926         formData.append('crop', crop);
31927         
31928         if(typeof(file.filename) != 'undefined'){
31929             formData.append('filename', file.filename);
31930         }
31931         
31932         if(typeof(file.mimetype) != 'undefined'){
31933             formData.append('mimetype', file.mimetype);
31934         }
31935         
31936         Roo.log(formData);
31937         
31938         if(this.fireEvent('prepare', this, formData) != false){
31939             this.xhr.send(formData);
31940         };
31941     }
31942 });
31943
31944 /*
31945 * Licence: LGPL
31946 */
31947
31948 /**
31949  * @class Roo.bootstrap.DocumentViewer
31950  * @extends Roo.bootstrap.Component
31951  * Bootstrap DocumentViewer class
31952  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31953  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31954  * 
31955  * @constructor
31956  * Create a new DocumentViewer
31957  * @param {Object} config The config object
31958  */
31959
31960 Roo.bootstrap.DocumentViewer = function(config){
31961     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31962     
31963     this.addEvents({
31964         /**
31965          * @event initial
31966          * Fire after initEvent
31967          * @param {Roo.bootstrap.DocumentViewer} this
31968          */
31969         "initial" : true,
31970         /**
31971          * @event click
31972          * Fire after click
31973          * @param {Roo.bootstrap.DocumentViewer} this
31974          */
31975         "click" : true,
31976         /**
31977          * @event download
31978          * Fire after download button
31979          * @param {Roo.bootstrap.DocumentViewer} this
31980          */
31981         "download" : true,
31982         /**
31983          * @event trash
31984          * Fire after trash button
31985          * @param {Roo.bootstrap.DocumentViewer} this
31986          */
31987         "trash" : true
31988         
31989     });
31990 };
31991
31992 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31993     
31994     showDownload : true,
31995     
31996     showTrash : true,
31997     
31998     getAutoCreate : function()
31999     {
32000         var cfg = {
32001             tag : 'div',
32002             cls : 'roo-document-viewer',
32003             cn : [
32004                 {
32005                     tag : 'div',
32006                     cls : 'roo-document-viewer-body',
32007                     cn : [
32008                         {
32009                             tag : 'div',
32010                             cls : 'roo-document-viewer-thumb',
32011                             cn : [
32012                                 {
32013                                     tag : 'img',
32014                                     cls : 'roo-document-viewer-image'
32015                                 }
32016                             ]
32017                         }
32018                     ]
32019                 },
32020                 {
32021                     tag : 'div',
32022                     cls : 'roo-document-viewer-footer',
32023                     cn : {
32024                         tag : 'div',
32025                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32026                         cn : [
32027                             {
32028                                 tag : 'div',
32029                                 cls : 'btn-group roo-document-viewer-download',
32030                                 cn : [
32031                                     {
32032                                         tag : 'button',
32033                                         cls : 'btn btn-default',
32034                                         html : '<i class="fa fa-download"></i>'
32035                                     }
32036                                 ]
32037                             },
32038                             {
32039                                 tag : 'div',
32040                                 cls : 'btn-group roo-document-viewer-trash',
32041                                 cn : [
32042                                     {
32043                                         tag : 'button',
32044                                         cls : 'btn btn-default',
32045                                         html : '<i class="fa fa-trash"></i>'
32046                                     }
32047                                 ]
32048                             }
32049                         ]
32050                     }
32051                 }
32052             ]
32053         };
32054         
32055         return cfg;
32056     },
32057     
32058     initEvents : function()
32059     {
32060         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32061         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32062         
32063         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32064         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32065         
32066         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32067         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32068         
32069         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32070         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32071         
32072         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32073         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32074         
32075         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32076         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32077         
32078         this.bodyEl.on('click', this.onClick, this);
32079         this.downloadBtn.on('click', this.onDownload, this);
32080         this.trashBtn.on('click', this.onTrash, this);
32081         
32082         this.downloadBtn.hide();
32083         this.trashBtn.hide();
32084         
32085         if(this.showDownload){
32086             this.downloadBtn.show();
32087         }
32088         
32089         if(this.showTrash){
32090             this.trashBtn.show();
32091         }
32092         
32093         if(!this.showDownload && !this.showTrash) {
32094             this.footerEl.hide();
32095         }
32096         
32097     },
32098     
32099     initial : function()
32100     {
32101         this.fireEvent('initial', this);
32102         
32103     },
32104     
32105     onClick : function(e)
32106     {
32107         e.preventDefault();
32108         
32109         this.fireEvent('click', this);
32110     },
32111     
32112     onDownload : function(e)
32113     {
32114         e.preventDefault();
32115         
32116         this.fireEvent('download', this);
32117     },
32118     
32119     onTrash : function(e)
32120     {
32121         e.preventDefault();
32122         
32123         this.fireEvent('trash', this);
32124     }
32125     
32126 });
32127 /*
32128  * - LGPL
32129  *
32130  * nav progress bar
32131  * 
32132  */
32133
32134 /**
32135  * @class Roo.bootstrap.NavProgressBar
32136  * @extends Roo.bootstrap.Component
32137  * Bootstrap NavProgressBar class
32138  * 
32139  * @constructor
32140  * Create a new nav progress bar
32141  * @param {Object} config The config object
32142  */
32143
32144 Roo.bootstrap.NavProgressBar = function(config){
32145     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32146
32147     this.bullets = this.bullets || [];
32148    
32149 //    Roo.bootstrap.NavProgressBar.register(this);
32150      this.addEvents({
32151         /**
32152              * @event changed
32153              * Fires when the active item changes
32154              * @param {Roo.bootstrap.NavProgressBar} this
32155              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32156              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32157          */
32158         'changed': true
32159      });
32160     
32161 };
32162
32163 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32164     
32165     bullets : [],
32166     barItems : [],
32167     
32168     getAutoCreate : function()
32169     {
32170         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32171         
32172         cfg = {
32173             tag : 'div',
32174             cls : 'roo-navigation-bar-group',
32175             cn : [
32176                 {
32177                     tag : 'div',
32178                     cls : 'roo-navigation-top-bar'
32179                 },
32180                 {
32181                     tag : 'div',
32182                     cls : 'roo-navigation-bullets-bar',
32183                     cn : [
32184                         {
32185                             tag : 'ul',
32186                             cls : 'roo-navigation-bar'
32187                         }
32188                     ]
32189                 },
32190                 
32191                 {
32192                     tag : 'div',
32193                     cls : 'roo-navigation-bottom-bar'
32194                 }
32195             ]
32196             
32197         };
32198         
32199         return cfg;
32200         
32201     },
32202     
32203     initEvents: function() 
32204     {
32205         
32206     },
32207     
32208     onRender : function(ct, position) 
32209     {
32210         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32211         
32212         if(this.bullets.length){
32213             Roo.each(this.bullets, function(b){
32214                this.addItem(b);
32215             }, this);
32216         }
32217         
32218         this.format();
32219         
32220     },
32221     
32222     addItem : function(cfg)
32223     {
32224         var item = new Roo.bootstrap.NavProgressItem(cfg);
32225         
32226         item.parentId = this.id;
32227         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32228         
32229         if(cfg.html){
32230             var top = new Roo.bootstrap.Element({
32231                 tag : 'div',
32232                 cls : 'roo-navigation-bar-text'
32233             });
32234             
32235             var bottom = new Roo.bootstrap.Element({
32236                 tag : 'div',
32237                 cls : 'roo-navigation-bar-text'
32238             });
32239             
32240             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32241             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32242             
32243             var topText = new Roo.bootstrap.Element({
32244                 tag : 'span',
32245                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32246             });
32247             
32248             var bottomText = new Roo.bootstrap.Element({
32249                 tag : 'span',
32250                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32251             });
32252             
32253             topText.onRender(top.el, null);
32254             bottomText.onRender(bottom.el, null);
32255             
32256             item.topEl = top;
32257             item.bottomEl = bottom;
32258         }
32259         
32260         this.barItems.push(item);
32261         
32262         return item;
32263     },
32264     
32265     getActive : function()
32266     {
32267         var active = false;
32268         
32269         Roo.each(this.barItems, function(v){
32270             
32271             if (!v.isActive()) {
32272                 return;
32273             }
32274             
32275             active = v;
32276             return false;
32277             
32278         });
32279         
32280         return active;
32281     },
32282     
32283     setActiveItem : function(item)
32284     {
32285         var prev = false;
32286         
32287         Roo.each(this.barItems, function(v){
32288             if (v.rid == item.rid) {
32289                 return ;
32290             }
32291             
32292             if (v.isActive()) {
32293                 v.setActive(false);
32294                 prev = v;
32295             }
32296         });
32297
32298         item.setActive(true);
32299         
32300         this.fireEvent('changed', this, item, prev);
32301     },
32302     
32303     getBarItem: function(rid)
32304     {
32305         var ret = false;
32306         
32307         Roo.each(this.barItems, function(e) {
32308             if (e.rid != rid) {
32309                 return;
32310             }
32311             
32312             ret =  e;
32313             return false;
32314         });
32315         
32316         return ret;
32317     },
32318     
32319     indexOfItem : function(item)
32320     {
32321         var index = false;
32322         
32323         Roo.each(this.barItems, function(v, i){
32324             
32325             if (v.rid != item.rid) {
32326                 return;
32327             }
32328             
32329             index = i;
32330             return false
32331         });
32332         
32333         return index;
32334     },
32335     
32336     setActiveNext : function()
32337     {
32338         var i = this.indexOfItem(this.getActive());
32339         
32340         if (i > this.barItems.length) {
32341             return;
32342         }
32343         
32344         this.setActiveItem(this.barItems[i+1]);
32345     },
32346     
32347     setActivePrev : function()
32348     {
32349         var i = this.indexOfItem(this.getActive());
32350         
32351         if (i  < 1) {
32352             return;
32353         }
32354         
32355         this.setActiveItem(this.barItems[i-1]);
32356     },
32357     
32358     format : function()
32359     {
32360         if(!this.barItems.length){
32361             return;
32362         }
32363      
32364         var width = 100 / this.barItems.length;
32365         
32366         Roo.each(this.barItems, function(i){
32367             i.el.setStyle('width', width + '%');
32368             i.topEl.el.setStyle('width', width + '%');
32369             i.bottomEl.el.setStyle('width', width + '%');
32370         }, this);
32371         
32372     }
32373     
32374 });
32375 /*
32376  * - LGPL
32377  *
32378  * Nav Progress Item
32379  * 
32380  */
32381
32382 /**
32383  * @class Roo.bootstrap.NavProgressItem
32384  * @extends Roo.bootstrap.Component
32385  * Bootstrap NavProgressItem class
32386  * @cfg {String} rid the reference id
32387  * @cfg {Boolean} active (true|false) Is item active default false
32388  * @cfg {Boolean} disabled (true|false) Is item active default false
32389  * @cfg {String} html
32390  * @cfg {String} position (top|bottom) text position default bottom
32391  * @cfg {String} icon show icon instead of number
32392  * 
32393  * @constructor
32394  * Create a new NavProgressItem
32395  * @param {Object} config The config object
32396  */
32397 Roo.bootstrap.NavProgressItem = function(config){
32398     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32399     this.addEvents({
32400         // raw events
32401         /**
32402          * @event click
32403          * The raw click event for the entire grid.
32404          * @param {Roo.bootstrap.NavProgressItem} this
32405          * @param {Roo.EventObject} e
32406          */
32407         "click" : true
32408     });
32409    
32410 };
32411
32412 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32413     
32414     rid : '',
32415     active : false,
32416     disabled : false,
32417     html : '',
32418     position : 'bottom',
32419     icon : false,
32420     
32421     getAutoCreate : function()
32422     {
32423         var iconCls = 'roo-navigation-bar-item-icon';
32424         
32425         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32426         
32427         var cfg = {
32428             tag: 'li',
32429             cls: 'roo-navigation-bar-item',
32430             cn : [
32431                 {
32432                     tag : 'i',
32433                     cls : iconCls
32434                 }
32435             ]
32436         };
32437         
32438         if(this.active){
32439             cfg.cls += ' active';
32440         }
32441         if(this.disabled){
32442             cfg.cls += ' disabled';
32443         }
32444         
32445         return cfg;
32446     },
32447     
32448     disable : function()
32449     {
32450         this.setDisabled(true);
32451     },
32452     
32453     enable : function()
32454     {
32455         this.setDisabled(false);
32456     },
32457     
32458     initEvents: function() 
32459     {
32460         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32461         
32462         this.iconEl.on('click', this.onClick, this);
32463     },
32464     
32465     onClick : function(e)
32466     {
32467         e.preventDefault();
32468         
32469         if(this.disabled){
32470             return;
32471         }
32472         
32473         if(this.fireEvent('click', this, e) === false){
32474             return;
32475         };
32476         
32477         this.parent().setActiveItem(this);
32478     },
32479     
32480     isActive: function () 
32481     {
32482         return this.active;
32483     },
32484     
32485     setActive : function(state)
32486     {
32487         if(this.active == state){
32488             return;
32489         }
32490         
32491         this.active = state;
32492         
32493         if (state) {
32494             this.el.addClass('active');
32495             return;
32496         }
32497         
32498         this.el.removeClass('active');
32499         
32500         return;
32501     },
32502     
32503     setDisabled : function(state)
32504     {
32505         if(this.disabled == state){
32506             return;
32507         }
32508         
32509         this.disabled = state;
32510         
32511         if (state) {
32512             this.el.addClass('disabled');
32513             return;
32514         }
32515         
32516         this.el.removeClass('disabled');
32517     },
32518     
32519     tooltipEl : function()
32520     {
32521         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32522     }
32523 });
32524  
32525
32526  /*
32527  * - LGPL
32528  *
32529  * FieldLabel
32530  * 
32531  */
32532
32533 /**
32534  * @class Roo.bootstrap.FieldLabel
32535  * @extends Roo.bootstrap.Component
32536  * Bootstrap FieldLabel class
32537  * @cfg {String} html contents of the element
32538  * @cfg {String} tag tag of the element default label
32539  * @cfg {String} cls class of the element
32540  * @cfg {String} target label target 
32541  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32542  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32543  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32544  * @cfg {String} iconTooltip default "This field is required"
32545  * @cfg {String} indicatorpos (left|right) default left
32546  * 
32547  * @constructor
32548  * Create a new FieldLabel
32549  * @param {Object} config The config object
32550  */
32551
32552 Roo.bootstrap.FieldLabel = function(config){
32553     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32554     
32555     this.addEvents({
32556             /**
32557              * @event invalid
32558              * Fires after the field has been marked as invalid.
32559              * @param {Roo.form.FieldLabel} this
32560              * @param {String} msg The validation message
32561              */
32562             invalid : true,
32563             /**
32564              * @event valid
32565              * Fires after the field has been validated with no errors.
32566              * @param {Roo.form.FieldLabel} this
32567              */
32568             valid : true
32569         });
32570 };
32571
32572 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32573     
32574     tag: 'label',
32575     cls: '',
32576     html: '',
32577     target: '',
32578     allowBlank : true,
32579     invalidClass : 'has-warning',
32580     validClass : 'has-success',
32581     iconTooltip : 'This field is required',
32582     indicatorpos : 'left',
32583     
32584     getAutoCreate : function(){
32585         
32586         var cls = "";
32587         if (!this.allowBlank) {
32588             cls  = "visible";
32589         }
32590         
32591         var cfg = {
32592             tag : this.tag,
32593             cls : 'roo-bootstrap-field-label ' + this.cls,
32594             for : this.target,
32595             cn : [
32596                 {
32597                     tag : 'i',
32598                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32599                     tooltip : this.iconTooltip
32600                 },
32601                 {
32602                     tag : 'span',
32603                     html : this.html
32604                 }
32605             ] 
32606         };
32607         
32608         if(this.indicatorpos == 'right'){
32609             var cfg = {
32610                 tag : this.tag,
32611                 cls : 'roo-bootstrap-field-label ' + this.cls,
32612                 for : this.target,
32613                 cn : [
32614                     {
32615                         tag : 'span',
32616                         html : this.html
32617                     },
32618                     {
32619                         tag : 'i',
32620                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32621                         tooltip : this.iconTooltip
32622                     }
32623                 ] 
32624             };
32625         }
32626         
32627         return cfg;
32628     },
32629     
32630     initEvents: function() 
32631     {
32632         Roo.bootstrap.Element.superclass.initEvents.call(this);
32633         
32634         this.indicator = this.indicatorEl();
32635         
32636         if(this.indicator){
32637             this.indicator.removeClass('visible');
32638             this.indicator.addClass('invisible');
32639         }
32640         
32641         Roo.bootstrap.FieldLabel.register(this);
32642     },
32643     
32644     indicatorEl : function()
32645     {
32646         var indicator = this.el.select('i.roo-required-indicator',true).first();
32647         
32648         if(!indicator){
32649             return false;
32650         }
32651         
32652         return indicator;
32653         
32654     },
32655     
32656     /**
32657      * Mark this field as valid
32658      */
32659     markValid : function()
32660     {
32661         if(this.indicator){
32662             this.indicator.removeClass('visible');
32663             this.indicator.addClass('invisible');
32664         }
32665         if (Roo.bootstrap.version == 3) {
32666             this.el.removeClass(this.invalidClass);
32667             this.el.addClass(this.validClass);
32668         } else {
32669             this.el.removeClass('is-invalid');
32670             this.el.addClass('is-valid');
32671         }
32672         
32673         
32674         this.fireEvent('valid', this);
32675     },
32676     
32677     /**
32678      * Mark this field as invalid
32679      * @param {String} msg The validation message
32680      */
32681     markInvalid : function(msg)
32682     {
32683         if(this.indicator){
32684             this.indicator.removeClass('invisible');
32685             this.indicator.addClass('visible');
32686         }
32687           if (Roo.bootstrap.version == 3) {
32688             this.el.removeClass(this.validClass);
32689             this.el.addClass(this.invalidClass);
32690         } else {
32691             this.el.removeClass('is-valid');
32692             this.el.addClass('is-invalid');
32693         }
32694         
32695         
32696         this.fireEvent('invalid', this, msg);
32697     }
32698     
32699    
32700 });
32701
32702 Roo.apply(Roo.bootstrap.FieldLabel, {
32703     
32704     groups: {},
32705     
32706      /**
32707     * register a FieldLabel Group
32708     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32709     */
32710     register : function(label)
32711     {
32712         if(this.groups.hasOwnProperty(label.target)){
32713             return;
32714         }
32715      
32716         this.groups[label.target] = label;
32717         
32718     },
32719     /**
32720     * fetch a FieldLabel Group based on the target
32721     * @param {string} target
32722     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32723     */
32724     get: function(target) {
32725         if (typeof(this.groups[target]) == 'undefined') {
32726             return false;
32727         }
32728         
32729         return this.groups[target] ;
32730     }
32731 });
32732
32733  
32734
32735  /*
32736  * - LGPL
32737  *
32738  * page DateSplitField.
32739  * 
32740  */
32741
32742
32743 /**
32744  * @class Roo.bootstrap.DateSplitField
32745  * @extends Roo.bootstrap.Component
32746  * Bootstrap DateSplitField class
32747  * @cfg {string} fieldLabel - the label associated
32748  * @cfg {Number} labelWidth set the width of label (0-12)
32749  * @cfg {String} labelAlign (top|left)
32750  * @cfg {Boolean} dayAllowBlank (true|false) default false
32751  * @cfg {Boolean} monthAllowBlank (true|false) default false
32752  * @cfg {Boolean} yearAllowBlank (true|false) default false
32753  * @cfg {string} dayPlaceholder 
32754  * @cfg {string} monthPlaceholder
32755  * @cfg {string} yearPlaceholder
32756  * @cfg {string} dayFormat default 'd'
32757  * @cfg {string} monthFormat default 'm'
32758  * @cfg {string} yearFormat default 'Y'
32759  * @cfg {Number} labellg set the width of label (1-12)
32760  * @cfg {Number} labelmd set the width of label (1-12)
32761  * @cfg {Number} labelsm set the width of label (1-12)
32762  * @cfg {Number} labelxs set the width of label (1-12)
32763
32764  *     
32765  * @constructor
32766  * Create a new DateSplitField
32767  * @param {Object} config The config object
32768  */
32769
32770 Roo.bootstrap.DateSplitField = function(config){
32771     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32772     
32773     this.addEvents({
32774         // raw events
32775          /**
32776          * @event years
32777          * getting the data of years
32778          * @param {Roo.bootstrap.DateSplitField} this
32779          * @param {Object} years
32780          */
32781         "years" : true,
32782         /**
32783          * @event days
32784          * getting the data of days
32785          * @param {Roo.bootstrap.DateSplitField} this
32786          * @param {Object} days
32787          */
32788         "days" : true,
32789         /**
32790          * @event invalid
32791          * Fires after the field has been marked as invalid.
32792          * @param {Roo.form.Field} this
32793          * @param {String} msg The validation message
32794          */
32795         invalid : true,
32796        /**
32797          * @event valid
32798          * Fires after the field has been validated with no errors.
32799          * @param {Roo.form.Field} this
32800          */
32801         valid : true
32802     });
32803 };
32804
32805 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32806     
32807     fieldLabel : '',
32808     labelAlign : 'top',
32809     labelWidth : 3,
32810     dayAllowBlank : false,
32811     monthAllowBlank : false,
32812     yearAllowBlank : false,
32813     dayPlaceholder : '',
32814     monthPlaceholder : '',
32815     yearPlaceholder : '',
32816     dayFormat : 'd',
32817     monthFormat : 'm',
32818     yearFormat : 'Y',
32819     isFormField : true,
32820     labellg : 0,
32821     labelmd : 0,
32822     labelsm : 0,
32823     labelxs : 0,
32824     
32825     getAutoCreate : function()
32826     {
32827         var cfg = {
32828             tag : 'div',
32829             cls : 'row roo-date-split-field-group',
32830             cn : [
32831                 {
32832                     tag : 'input',
32833                     type : 'hidden',
32834                     cls : 'form-hidden-field roo-date-split-field-group-value',
32835                     name : this.name
32836                 }
32837             ]
32838         };
32839         
32840         var labelCls = 'col-md-12';
32841         var contentCls = 'col-md-4';
32842         
32843         if(this.fieldLabel){
32844             
32845             var label = {
32846                 tag : 'div',
32847                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32848                 cn : [
32849                     {
32850                         tag : 'label',
32851                         html : this.fieldLabel
32852                     }
32853                 ]
32854             };
32855             
32856             if(this.labelAlign == 'left'){
32857             
32858                 if(this.labelWidth > 12){
32859                     label.style = "width: " + this.labelWidth + 'px';
32860                 }
32861
32862                 if(this.labelWidth < 13 && this.labelmd == 0){
32863                     this.labelmd = this.labelWidth;
32864                 }
32865
32866                 if(this.labellg > 0){
32867                     labelCls = ' col-lg-' + this.labellg;
32868                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32869                 }
32870
32871                 if(this.labelmd > 0){
32872                     labelCls = ' col-md-' + this.labelmd;
32873                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32874                 }
32875
32876                 if(this.labelsm > 0){
32877                     labelCls = ' col-sm-' + this.labelsm;
32878                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32879                 }
32880
32881                 if(this.labelxs > 0){
32882                     labelCls = ' col-xs-' + this.labelxs;
32883                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32884                 }
32885             }
32886             
32887             label.cls += ' ' + labelCls;
32888             
32889             cfg.cn.push(label);
32890         }
32891         
32892         Roo.each(['day', 'month', 'year'], function(t){
32893             cfg.cn.push({
32894                 tag : 'div',
32895                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32896             });
32897         }, this);
32898         
32899         return cfg;
32900     },
32901     
32902     inputEl: function ()
32903     {
32904         return this.el.select('.roo-date-split-field-group-value', true).first();
32905     },
32906     
32907     onRender : function(ct, position) 
32908     {
32909         var _this = this;
32910         
32911         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32912         
32913         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32914         
32915         this.dayField = new Roo.bootstrap.ComboBox({
32916             allowBlank : this.dayAllowBlank,
32917             alwaysQuery : true,
32918             displayField : 'value',
32919             editable : false,
32920             fieldLabel : '',
32921             forceSelection : true,
32922             mode : 'local',
32923             placeholder : this.dayPlaceholder,
32924             selectOnFocus : true,
32925             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32926             triggerAction : 'all',
32927             typeAhead : true,
32928             valueField : 'value',
32929             store : new Roo.data.SimpleStore({
32930                 data : (function() {    
32931                     var days = [];
32932                     _this.fireEvent('days', _this, days);
32933                     return days;
32934                 })(),
32935                 fields : [ 'value' ]
32936             }),
32937             listeners : {
32938                 select : function (_self, record, index)
32939                 {
32940                     _this.setValue(_this.getValue());
32941                 }
32942             }
32943         });
32944
32945         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32946         
32947         this.monthField = new Roo.bootstrap.MonthField({
32948             after : '<i class=\"fa fa-calendar\"></i>',
32949             allowBlank : this.monthAllowBlank,
32950             placeholder : this.monthPlaceholder,
32951             readOnly : true,
32952             listeners : {
32953                 render : function (_self)
32954                 {
32955                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32956                         e.preventDefault();
32957                         _self.focus();
32958                     });
32959                 },
32960                 select : function (_self, oldvalue, newvalue)
32961                 {
32962                     _this.setValue(_this.getValue());
32963                 }
32964             }
32965         });
32966         
32967         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32968         
32969         this.yearField = new Roo.bootstrap.ComboBox({
32970             allowBlank : this.yearAllowBlank,
32971             alwaysQuery : true,
32972             displayField : 'value',
32973             editable : false,
32974             fieldLabel : '',
32975             forceSelection : true,
32976             mode : 'local',
32977             placeholder : this.yearPlaceholder,
32978             selectOnFocus : true,
32979             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32980             triggerAction : 'all',
32981             typeAhead : true,
32982             valueField : 'value',
32983             store : new Roo.data.SimpleStore({
32984                 data : (function() {
32985                     var years = [];
32986                     _this.fireEvent('years', _this, years);
32987                     return years;
32988                 })(),
32989                 fields : [ 'value' ]
32990             }),
32991             listeners : {
32992                 select : function (_self, record, index)
32993                 {
32994                     _this.setValue(_this.getValue());
32995                 }
32996             }
32997         });
32998
32999         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33000     },
33001     
33002     setValue : function(v, format)
33003     {
33004         this.inputEl.dom.value = v;
33005         
33006         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33007         
33008         var d = Date.parseDate(v, f);
33009         
33010         if(!d){
33011             this.validate();
33012             return;
33013         }
33014         
33015         this.setDay(d.format(this.dayFormat));
33016         this.setMonth(d.format(this.monthFormat));
33017         this.setYear(d.format(this.yearFormat));
33018         
33019         this.validate();
33020         
33021         return;
33022     },
33023     
33024     setDay : function(v)
33025     {
33026         this.dayField.setValue(v);
33027         this.inputEl.dom.value = this.getValue();
33028         this.validate();
33029         return;
33030     },
33031     
33032     setMonth : function(v)
33033     {
33034         this.monthField.setValue(v, true);
33035         this.inputEl.dom.value = this.getValue();
33036         this.validate();
33037         return;
33038     },
33039     
33040     setYear : function(v)
33041     {
33042         this.yearField.setValue(v);
33043         this.inputEl.dom.value = this.getValue();
33044         this.validate();
33045         return;
33046     },
33047     
33048     getDay : function()
33049     {
33050         return this.dayField.getValue();
33051     },
33052     
33053     getMonth : function()
33054     {
33055         return this.monthField.getValue();
33056     },
33057     
33058     getYear : function()
33059     {
33060         return this.yearField.getValue();
33061     },
33062     
33063     getValue : function()
33064     {
33065         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33066         
33067         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33068         
33069         return date;
33070     },
33071     
33072     reset : function()
33073     {
33074         this.setDay('');
33075         this.setMonth('');
33076         this.setYear('');
33077         this.inputEl.dom.value = '';
33078         this.validate();
33079         return;
33080     },
33081     
33082     validate : function()
33083     {
33084         var d = this.dayField.validate();
33085         var m = this.monthField.validate();
33086         var y = this.yearField.validate();
33087         
33088         var valid = true;
33089         
33090         if(
33091                 (!this.dayAllowBlank && !d) ||
33092                 (!this.monthAllowBlank && !m) ||
33093                 (!this.yearAllowBlank && !y)
33094         ){
33095             valid = false;
33096         }
33097         
33098         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33099             return valid;
33100         }
33101         
33102         if(valid){
33103             this.markValid();
33104             return valid;
33105         }
33106         
33107         this.markInvalid();
33108         
33109         return valid;
33110     },
33111     
33112     markValid : function()
33113     {
33114         
33115         var label = this.el.select('label', true).first();
33116         var icon = this.el.select('i.fa-star', true).first();
33117
33118         if(label && icon){
33119             icon.remove();
33120         }
33121         
33122         this.fireEvent('valid', this);
33123     },
33124     
33125      /**
33126      * Mark this field as invalid
33127      * @param {String} msg The validation message
33128      */
33129     markInvalid : function(msg)
33130     {
33131         
33132         var label = this.el.select('label', true).first();
33133         var icon = this.el.select('i.fa-star', true).first();
33134
33135         if(label && !icon){
33136             this.el.select('.roo-date-split-field-label', true).createChild({
33137                 tag : 'i',
33138                 cls : 'text-danger fa fa-lg fa-star',
33139                 tooltip : 'This field is required',
33140                 style : 'margin-right:5px;'
33141             }, label, true);
33142         }
33143         
33144         this.fireEvent('invalid', this, msg);
33145     },
33146     
33147     clearInvalid : function()
33148     {
33149         var label = this.el.select('label', true).first();
33150         var icon = this.el.select('i.fa-star', true).first();
33151
33152         if(label && icon){
33153             icon.remove();
33154         }
33155         
33156         this.fireEvent('valid', this);
33157     },
33158     
33159     getName: function()
33160     {
33161         return this.name;
33162     }
33163     
33164 });
33165
33166  /**
33167  *
33168  * This is based on 
33169  * http://masonry.desandro.com
33170  *
33171  * The idea is to render all the bricks based on vertical width...
33172  *
33173  * The original code extends 'outlayer' - we might need to use that....
33174  * 
33175  */
33176
33177
33178 /**
33179  * @class Roo.bootstrap.LayoutMasonry
33180  * @extends Roo.bootstrap.Component
33181  * Bootstrap Layout Masonry class
33182  * 
33183  * @constructor
33184  * Create a new Element
33185  * @param {Object} config The config object
33186  */
33187
33188 Roo.bootstrap.LayoutMasonry = function(config){
33189     
33190     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33191     
33192     this.bricks = [];
33193     
33194     Roo.bootstrap.LayoutMasonry.register(this);
33195     
33196     this.addEvents({
33197         // raw events
33198         /**
33199          * @event layout
33200          * Fire after layout the items
33201          * @param {Roo.bootstrap.LayoutMasonry} this
33202          * @param {Roo.EventObject} e
33203          */
33204         "layout" : true
33205     });
33206     
33207 };
33208
33209 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33210     
33211     /**
33212      * @cfg {Boolean} isLayoutInstant = no animation?
33213      */   
33214     isLayoutInstant : false, // needed?
33215    
33216     /**
33217      * @cfg {Number} boxWidth  width of the columns
33218      */   
33219     boxWidth : 450,
33220     
33221       /**
33222      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33223      */   
33224     boxHeight : 0,
33225     
33226     /**
33227      * @cfg {Number} padWidth padding below box..
33228      */   
33229     padWidth : 10, 
33230     
33231     /**
33232      * @cfg {Number} gutter gutter width..
33233      */   
33234     gutter : 10,
33235     
33236      /**
33237      * @cfg {Number} maxCols maximum number of columns
33238      */   
33239     
33240     maxCols: 0,
33241     
33242     /**
33243      * @cfg {Boolean} isAutoInitial defalut true
33244      */   
33245     isAutoInitial : true, 
33246     
33247     containerWidth: 0,
33248     
33249     /**
33250      * @cfg {Boolean} isHorizontal defalut false
33251      */   
33252     isHorizontal : false, 
33253
33254     currentSize : null,
33255     
33256     tag: 'div',
33257     
33258     cls: '',
33259     
33260     bricks: null, //CompositeElement
33261     
33262     cols : 1,
33263     
33264     _isLayoutInited : false,
33265     
33266 //    isAlternative : false, // only use for vertical layout...
33267     
33268     /**
33269      * @cfg {Number} alternativePadWidth padding below box..
33270      */   
33271     alternativePadWidth : 50,
33272     
33273     selectedBrick : [],
33274     
33275     getAutoCreate : function(){
33276         
33277         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33278         
33279         var cfg = {
33280             tag: this.tag,
33281             cls: 'blog-masonary-wrapper ' + this.cls,
33282             cn : {
33283                 cls : 'mas-boxes masonary'
33284             }
33285         };
33286         
33287         return cfg;
33288     },
33289     
33290     getChildContainer: function( )
33291     {
33292         if (this.boxesEl) {
33293             return this.boxesEl;
33294         }
33295         
33296         this.boxesEl = this.el.select('.mas-boxes').first();
33297         
33298         return this.boxesEl;
33299     },
33300     
33301     
33302     initEvents : function()
33303     {
33304         var _this = this;
33305         
33306         if(this.isAutoInitial){
33307             Roo.log('hook children rendered');
33308             this.on('childrenrendered', function() {
33309                 Roo.log('children rendered');
33310                 _this.initial();
33311             } ,this);
33312         }
33313     },
33314     
33315     initial : function()
33316     {
33317         this.selectedBrick = [];
33318         
33319         this.currentSize = this.el.getBox(true);
33320         
33321         Roo.EventManager.onWindowResize(this.resize, this); 
33322
33323         if(!this.isAutoInitial){
33324             this.layout();
33325             return;
33326         }
33327         
33328         this.layout();
33329         
33330         return;
33331         //this.layout.defer(500,this);
33332         
33333     },
33334     
33335     resize : function()
33336     {
33337         var cs = this.el.getBox(true);
33338         
33339         if (
33340                 this.currentSize.width == cs.width && 
33341                 this.currentSize.x == cs.x && 
33342                 this.currentSize.height == cs.height && 
33343                 this.currentSize.y == cs.y 
33344         ) {
33345             Roo.log("no change in with or X or Y");
33346             return;
33347         }
33348         
33349         this.currentSize = cs;
33350         
33351         this.layout();
33352         
33353     },
33354     
33355     layout : function()
33356     {   
33357         this._resetLayout();
33358         
33359         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33360         
33361         this.layoutItems( isInstant );
33362       
33363         this._isLayoutInited = true;
33364         
33365         this.fireEvent('layout', this);
33366         
33367     },
33368     
33369     _resetLayout : function()
33370     {
33371         if(this.isHorizontal){
33372             this.horizontalMeasureColumns();
33373             return;
33374         }
33375         
33376         this.verticalMeasureColumns();
33377         
33378     },
33379     
33380     verticalMeasureColumns : function()
33381     {
33382         this.getContainerWidth();
33383         
33384 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33385 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33386 //            return;
33387 //        }
33388         
33389         var boxWidth = this.boxWidth + this.padWidth;
33390         
33391         if(this.containerWidth < this.boxWidth){
33392             boxWidth = this.containerWidth
33393         }
33394         
33395         var containerWidth = this.containerWidth;
33396         
33397         var cols = Math.floor(containerWidth / boxWidth);
33398         
33399         this.cols = Math.max( cols, 1 );
33400         
33401         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33402         
33403         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33404         
33405         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33406         
33407         this.colWidth = boxWidth + avail - this.padWidth;
33408         
33409         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33410         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33411     },
33412     
33413     horizontalMeasureColumns : function()
33414     {
33415         this.getContainerWidth();
33416         
33417         var boxWidth = this.boxWidth;
33418         
33419         if(this.containerWidth < boxWidth){
33420             boxWidth = this.containerWidth;
33421         }
33422         
33423         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33424         
33425         this.el.setHeight(boxWidth);
33426         
33427     },
33428     
33429     getContainerWidth : function()
33430     {
33431         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33432     },
33433     
33434     layoutItems : function( isInstant )
33435     {
33436         Roo.log(this.bricks);
33437         
33438         var items = Roo.apply([], this.bricks);
33439         
33440         if(this.isHorizontal){
33441             this._horizontalLayoutItems( items , isInstant );
33442             return;
33443         }
33444         
33445 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33446 //            this._verticalAlternativeLayoutItems( items , isInstant );
33447 //            return;
33448 //        }
33449         
33450         this._verticalLayoutItems( items , isInstant );
33451         
33452     },
33453     
33454     _verticalLayoutItems : function ( items , isInstant)
33455     {
33456         if ( !items || !items.length ) {
33457             return;
33458         }
33459         
33460         var standard = [
33461             ['xs', 'xs', 'xs', 'tall'],
33462             ['xs', 'xs', 'tall'],
33463             ['xs', 'xs', 'sm'],
33464             ['xs', 'xs', 'xs'],
33465             ['xs', 'tall'],
33466             ['xs', 'sm'],
33467             ['xs', 'xs'],
33468             ['xs'],
33469             
33470             ['sm', 'xs', 'xs'],
33471             ['sm', 'xs'],
33472             ['sm'],
33473             
33474             ['tall', 'xs', 'xs', 'xs'],
33475             ['tall', 'xs', 'xs'],
33476             ['tall', 'xs'],
33477             ['tall']
33478             
33479         ];
33480         
33481         var queue = [];
33482         
33483         var boxes = [];
33484         
33485         var box = [];
33486         
33487         Roo.each(items, function(item, k){
33488             
33489             switch (item.size) {
33490                 // these layouts take up a full box,
33491                 case 'md' :
33492                 case 'md-left' :
33493                 case 'md-right' :
33494                 case 'wide' :
33495                     
33496                     if(box.length){
33497                         boxes.push(box);
33498                         box = [];
33499                     }
33500                     
33501                     boxes.push([item]);
33502                     
33503                     break;
33504                     
33505                 case 'xs' :
33506                 case 'sm' :
33507                 case 'tall' :
33508                     
33509                     box.push(item);
33510                     
33511                     break;
33512                 default :
33513                     break;
33514                     
33515             }
33516             
33517         }, this);
33518         
33519         if(box.length){
33520             boxes.push(box);
33521             box = [];
33522         }
33523         
33524         var filterPattern = function(box, length)
33525         {
33526             if(!box.length){
33527                 return;
33528             }
33529             
33530             var match = false;
33531             
33532             var pattern = box.slice(0, length);
33533             
33534             var format = [];
33535             
33536             Roo.each(pattern, function(i){
33537                 format.push(i.size);
33538             }, this);
33539             
33540             Roo.each(standard, function(s){
33541                 
33542                 if(String(s) != String(format)){
33543                     return;
33544                 }
33545                 
33546                 match = true;
33547                 return false;
33548                 
33549             }, this);
33550             
33551             if(!match && length == 1){
33552                 return;
33553             }
33554             
33555             if(!match){
33556                 filterPattern(box, length - 1);
33557                 return;
33558             }
33559                 
33560             queue.push(pattern);
33561
33562             box = box.slice(length, box.length);
33563
33564             filterPattern(box, 4);
33565
33566             return;
33567             
33568         }
33569         
33570         Roo.each(boxes, function(box, k){
33571             
33572             if(!box.length){
33573                 return;
33574             }
33575             
33576             if(box.length == 1){
33577                 queue.push(box);
33578                 return;
33579             }
33580             
33581             filterPattern(box, 4);
33582             
33583         }, this);
33584         
33585         this._processVerticalLayoutQueue( queue, isInstant );
33586         
33587     },
33588     
33589 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33590 //    {
33591 //        if ( !items || !items.length ) {
33592 //            return;
33593 //        }
33594 //
33595 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33596 //        
33597 //    },
33598     
33599     _horizontalLayoutItems : function ( items , isInstant)
33600     {
33601         if ( !items || !items.length || items.length < 3) {
33602             return;
33603         }
33604         
33605         items.reverse();
33606         
33607         var eItems = items.slice(0, 3);
33608         
33609         items = items.slice(3, items.length);
33610         
33611         var standard = [
33612             ['xs', 'xs', 'xs', 'wide'],
33613             ['xs', 'xs', 'wide'],
33614             ['xs', 'xs', 'sm'],
33615             ['xs', 'xs', 'xs'],
33616             ['xs', 'wide'],
33617             ['xs', 'sm'],
33618             ['xs', 'xs'],
33619             ['xs'],
33620             
33621             ['sm', 'xs', 'xs'],
33622             ['sm', 'xs'],
33623             ['sm'],
33624             
33625             ['wide', 'xs', 'xs', 'xs'],
33626             ['wide', 'xs', 'xs'],
33627             ['wide', 'xs'],
33628             ['wide'],
33629             
33630             ['wide-thin']
33631         ];
33632         
33633         var queue = [];
33634         
33635         var boxes = [];
33636         
33637         var box = [];
33638         
33639         Roo.each(items, function(item, k){
33640             
33641             switch (item.size) {
33642                 case 'md' :
33643                 case 'md-left' :
33644                 case 'md-right' :
33645                 case 'tall' :
33646                     
33647                     if(box.length){
33648                         boxes.push(box);
33649                         box = [];
33650                     }
33651                     
33652                     boxes.push([item]);
33653                     
33654                     break;
33655                     
33656                 case 'xs' :
33657                 case 'sm' :
33658                 case 'wide' :
33659                 case 'wide-thin' :
33660                     
33661                     box.push(item);
33662                     
33663                     break;
33664                 default :
33665                     break;
33666                     
33667             }
33668             
33669         }, this);
33670         
33671         if(box.length){
33672             boxes.push(box);
33673             box = [];
33674         }
33675         
33676         var filterPattern = function(box, length)
33677         {
33678             if(!box.length){
33679                 return;
33680             }
33681             
33682             var match = false;
33683             
33684             var pattern = box.slice(0, length);
33685             
33686             var format = [];
33687             
33688             Roo.each(pattern, function(i){
33689                 format.push(i.size);
33690             }, this);
33691             
33692             Roo.each(standard, function(s){
33693                 
33694                 if(String(s) != String(format)){
33695                     return;
33696                 }
33697                 
33698                 match = true;
33699                 return false;
33700                 
33701             }, this);
33702             
33703             if(!match && length == 1){
33704                 return;
33705             }
33706             
33707             if(!match){
33708                 filterPattern(box, length - 1);
33709                 return;
33710             }
33711                 
33712             queue.push(pattern);
33713
33714             box = box.slice(length, box.length);
33715
33716             filterPattern(box, 4);
33717
33718             return;
33719             
33720         }
33721         
33722         Roo.each(boxes, function(box, k){
33723             
33724             if(!box.length){
33725                 return;
33726             }
33727             
33728             if(box.length == 1){
33729                 queue.push(box);
33730                 return;
33731             }
33732             
33733             filterPattern(box, 4);
33734             
33735         }, this);
33736         
33737         
33738         var prune = [];
33739         
33740         var pos = this.el.getBox(true);
33741         
33742         var minX = pos.x;
33743         
33744         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33745         
33746         var hit_end = false;
33747         
33748         Roo.each(queue, function(box){
33749             
33750             if(hit_end){
33751                 
33752                 Roo.each(box, function(b){
33753                 
33754                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33755                     b.el.hide();
33756
33757                 }, this);
33758
33759                 return;
33760             }
33761             
33762             var mx = 0;
33763             
33764             Roo.each(box, function(b){
33765                 
33766                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33767                 b.el.show();
33768
33769                 mx = Math.max(mx, b.x);
33770                 
33771             }, this);
33772             
33773             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33774             
33775             if(maxX < minX){
33776                 
33777                 Roo.each(box, function(b){
33778                 
33779                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33780                     b.el.hide();
33781                     
33782                 }, this);
33783                 
33784                 hit_end = true;
33785                 
33786                 return;
33787             }
33788             
33789             prune.push(box);
33790             
33791         }, this);
33792         
33793         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33794     },
33795     
33796     /** Sets position of item in DOM
33797     * @param {Element} item
33798     * @param {Number} x - horizontal position
33799     * @param {Number} y - vertical position
33800     * @param {Boolean} isInstant - disables transitions
33801     */
33802     _processVerticalLayoutQueue : function( queue, isInstant )
33803     {
33804         var pos = this.el.getBox(true);
33805         var x = pos.x;
33806         var y = pos.y;
33807         var maxY = [];
33808         
33809         for (var i = 0; i < this.cols; i++){
33810             maxY[i] = pos.y;
33811         }
33812         
33813         Roo.each(queue, function(box, k){
33814             
33815             var col = k % this.cols;
33816             
33817             Roo.each(box, function(b,kk){
33818                 
33819                 b.el.position('absolute');
33820                 
33821                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33822                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33823                 
33824                 if(b.size == 'md-left' || b.size == 'md-right'){
33825                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33826                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33827                 }
33828                 
33829                 b.el.setWidth(width);
33830                 b.el.setHeight(height);
33831                 // iframe?
33832                 b.el.select('iframe',true).setSize(width,height);
33833                 
33834             }, this);
33835             
33836             for (var i = 0; i < this.cols; i++){
33837                 
33838                 if(maxY[i] < maxY[col]){
33839                     col = i;
33840                     continue;
33841                 }
33842                 
33843                 col = Math.min(col, i);
33844                 
33845             }
33846             
33847             x = pos.x + col * (this.colWidth + this.padWidth);
33848             
33849             y = maxY[col];
33850             
33851             var positions = [];
33852             
33853             switch (box.length){
33854                 case 1 :
33855                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33856                     break;
33857                 case 2 :
33858                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33859                     break;
33860                 case 3 :
33861                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33862                     break;
33863                 case 4 :
33864                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33865                     break;
33866                 default :
33867                     break;
33868             }
33869             
33870             Roo.each(box, function(b,kk){
33871                 
33872                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33873                 
33874                 var sz = b.el.getSize();
33875                 
33876                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33877                 
33878             }, this);
33879             
33880         }, this);
33881         
33882         var mY = 0;
33883         
33884         for (var i = 0; i < this.cols; i++){
33885             mY = Math.max(mY, maxY[i]);
33886         }
33887         
33888         this.el.setHeight(mY - pos.y);
33889         
33890     },
33891     
33892 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33893 //    {
33894 //        var pos = this.el.getBox(true);
33895 //        var x = pos.x;
33896 //        var y = pos.y;
33897 //        var maxX = pos.right;
33898 //        
33899 //        var maxHeight = 0;
33900 //        
33901 //        Roo.each(items, function(item, k){
33902 //            
33903 //            var c = k % 2;
33904 //            
33905 //            item.el.position('absolute');
33906 //                
33907 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33908 //
33909 //            item.el.setWidth(width);
33910 //
33911 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33912 //
33913 //            item.el.setHeight(height);
33914 //            
33915 //            if(c == 0){
33916 //                item.el.setXY([x, y], isInstant ? false : true);
33917 //            } else {
33918 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33919 //            }
33920 //            
33921 //            y = y + height + this.alternativePadWidth;
33922 //            
33923 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33924 //            
33925 //        }, this);
33926 //        
33927 //        this.el.setHeight(maxHeight);
33928 //        
33929 //    },
33930     
33931     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33932     {
33933         var pos = this.el.getBox(true);
33934         
33935         var minX = pos.x;
33936         var minY = pos.y;
33937         
33938         var maxX = pos.right;
33939         
33940         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33941         
33942         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33943         
33944         Roo.each(queue, function(box, k){
33945             
33946             Roo.each(box, function(b, kk){
33947                 
33948                 b.el.position('absolute');
33949                 
33950                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33951                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33952                 
33953                 if(b.size == 'md-left' || b.size == 'md-right'){
33954                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33955                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33956                 }
33957                 
33958                 b.el.setWidth(width);
33959                 b.el.setHeight(height);
33960                 
33961             }, this);
33962             
33963             if(!box.length){
33964                 return;
33965             }
33966             
33967             var positions = [];
33968             
33969             switch (box.length){
33970                 case 1 :
33971                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33972                     break;
33973                 case 2 :
33974                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33975                     break;
33976                 case 3 :
33977                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33978                     break;
33979                 case 4 :
33980                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33981                     break;
33982                 default :
33983                     break;
33984             }
33985             
33986             Roo.each(box, function(b,kk){
33987                 
33988                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33989                 
33990                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33991                 
33992             }, this);
33993             
33994         }, this);
33995         
33996     },
33997     
33998     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33999     {
34000         Roo.each(eItems, function(b,k){
34001             
34002             b.size = (k == 0) ? 'sm' : 'xs';
34003             b.x = (k == 0) ? 2 : 1;
34004             b.y = (k == 0) ? 2 : 1;
34005             
34006             b.el.position('absolute');
34007             
34008             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34009                 
34010             b.el.setWidth(width);
34011             
34012             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34013             
34014             b.el.setHeight(height);
34015             
34016         }, this);
34017
34018         var positions = [];
34019         
34020         positions.push({
34021             x : maxX - this.unitWidth * 2 - this.gutter,
34022             y : minY
34023         });
34024         
34025         positions.push({
34026             x : maxX - this.unitWidth,
34027             y : minY + (this.unitWidth + this.gutter) * 2
34028         });
34029         
34030         positions.push({
34031             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34032             y : minY
34033         });
34034         
34035         Roo.each(eItems, function(b,k){
34036             
34037             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34038
34039         }, this);
34040         
34041     },
34042     
34043     getVerticalOneBoxColPositions : function(x, y, box)
34044     {
34045         var pos = [];
34046         
34047         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34048         
34049         if(box[0].size == 'md-left'){
34050             rand = 0;
34051         }
34052         
34053         if(box[0].size == 'md-right'){
34054             rand = 1;
34055         }
34056         
34057         pos.push({
34058             x : x + (this.unitWidth + this.gutter) * rand,
34059             y : y
34060         });
34061         
34062         return pos;
34063     },
34064     
34065     getVerticalTwoBoxColPositions : function(x, y, box)
34066     {
34067         var pos = [];
34068         
34069         if(box[0].size == 'xs'){
34070             
34071             pos.push({
34072                 x : x,
34073                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34074             });
34075
34076             pos.push({
34077                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34078                 y : y
34079             });
34080             
34081             return pos;
34082             
34083         }
34084         
34085         pos.push({
34086             x : x,
34087             y : y
34088         });
34089
34090         pos.push({
34091             x : x + (this.unitWidth + this.gutter) * 2,
34092             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34093         });
34094         
34095         return pos;
34096         
34097     },
34098     
34099     getVerticalThreeBoxColPositions : function(x, y, box)
34100     {
34101         var pos = [];
34102         
34103         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34104             
34105             pos.push({
34106                 x : x,
34107                 y : y
34108             });
34109
34110             pos.push({
34111                 x : x + (this.unitWidth + this.gutter) * 1,
34112                 y : y
34113             });
34114             
34115             pos.push({
34116                 x : x + (this.unitWidth + this.gutter) * 2,
34117                 y : y
34118             });
34119             
34120             return pos;
34121             
34122         }
34123         
34124         if(box[0].size == 'xs' && box[1].size == 'xs'){
34125             
34126             pos.push({
34127                 x : x,
34128                 y : y
34129             });
34130
34131             pos.push({
34132                 x : x,
34133                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34134             });
34135             
34136             pos.push({
34137                 x : x + (this.unitWidth + this.gutter) * 1,
34138                 y : y
34139             });
34140             
34141             return pos;
34142             
34143         }
34144         
34145         pos.push({
34146             x : x,
34147             y : y
34148         });
34149
34150         pos.push({
34151             x : x + (this.unitWidth + this.gutter) * 2,
34152             y : y
34153         });
34154
34155         pos.push({
34156             x : x + (this.unitWidth + this.gutter) * 2,
34157             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34158         });
34159             
34160         return pos;
34161         
34162     },
34163     
34164     getVerticalFourBoxColPositions : function(x, y, box)
34165     {
34166         var pos = [];
34167         
34168         if(box[0].size == 'xs'){
34169             
34170             pos.push({
34171                 x : x,
34172                 y : y
34173             });
34174
34175             pos.push({
34176                 x : x,
34177                 y : y + (this.unitHeight + this.gutter) * 1
34178             });
34179             
34180             pos.push({
34181                 x : x,
34182                 y : y + (this.unitHeight + this.gutter) * 2
34183             });
34184             
34185             pos.push({
34186                 x : x + (this.unitWidth + this.gutter) * 1,
34187                 y : y
34188             });
34189             
34190             return pos;
34191             
34192         }
34193         
34194         pos.push({
34195             x : x,
34196             y : y
34197         });
34198
34199         pos.push({
34200             x : x + (this.unitWidth + this.gutter) * 2,
34201             y : y
34202         });
34203
34204         pos.push({
34205             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34206             y : y + (this.unitHeight + this.gutter) * 1
34207         });
34208
34209         pos.push({
34210             x : x + (this.unitWidth + this.gutter) * 2,
34211             y : y + (this.unitWidth + this.gutter) * 2
34212         });
34213
34214         return pos;
34215         
34216     },
34217     
34218     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34219     {
34220         var pos = [];
34221         
34222         if(box[0].size == 'md-left'){
34223             pos.push({
34224                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34225                 y : minY
34226             });
34227             
34228             return pos;
34229         }
34230         
34231         if(box[0].size == 'md-right'){
34232             pos.push({
34233                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34234                 y : minY + (this.unitWidth + this.gutter) * 1
34235             });
34236             
34237             return pos;
34238         }
34239         
34240         var rand = Math.floor(Math.random() * (4 - box[0].y));
34241         
34242         pos.push({
34243             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34244             y : minY + (this.unitWidth + this.gutter) * rand
34245         });
34246         
34247         return pos;
34248         
34249     },
34250     
34251     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34252     {
34253         var pos = [];
34254         
34255         if(box[0].size == 'xs'){
34256             
34257             pos.push({
34258                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34259                 y : minY
34260             });
34261
34262             pos.push({
34263                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34264                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34265             });
34266             
34267             return pos;
34268             
34269         }
34270         
34271         pos.push({
34272             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34273             y : minY
34274         });
34275
34276         pos.push({
34277             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34278             y : minY + (this.unitWidth + this.gutter) * 2
34279         });
34280         
34281         return pos;
34282         
34283     },
34284     
34285     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34286     {
34287         var pos = [];
34288         
34289         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34290             
34291             pos.push({
34292                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34293                 y : minY
34294             });
34295
34296             pos.push({
34297                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34298                 y : minY + (this.unitWidth + this.gutter) * 1
34299             });
34300             
34301             pos.push({
34302                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34303                 y : minY + (this.unitWidth + this.gutter) * 2
34304             });
34305             
34306             return pos;
34307             
34308         }
34309         
34310         if(box[0].size == 'xs' && box[1].size == 'xs'){
34311             
34312             pos.push({
34313                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34314                 y : minY
34315             });
34316
34317             pos.push({
34318                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34319                 y : minY
34320             });
34321             
34322             pos.push({
34323                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34324                 y : minY + (this.unitWidth + this.gutter) * 1
34325             });
34326             
34327             return pos;
34328             
34329         }
34330         
34331         pos.push({
34332             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34333             y : minY
34334         });
34335
34336         pos.push({
34337             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34338             y : minY + (this.unitWidth + this.gutter) * 2
34339         });
34340
34341         pos.push({
34342             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34343             y : minY + (this.unitWidth + this.gutter) * 2
34344         });
34345             
34346         return pos;
34347         
34348     },
34349     
34350     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34351     {
34352         var pos = [];
34353         
34354         if(box[0].size == 'xs'){
34355             
34356             pos.push({
34357                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34358                 y : minY
34359             });
34360
34361             pos.push({
34362                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34363                 y : minY
34364             });
34365             
34366             pos.push({
34367                 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),
34368                 y : minY
34369             });
34370             
34371             pos.push({
34372                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34373                 y : minY + (this.unitWidth + this.gutter) * 1
34374             });
34375             
34376             return pos;
34377             
34378         }
34379         
34380         pos.push({
34381             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34382             y : minY
34383         });
34384         
34385         pos.push({
34386             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34387             y : minY + (this.unitWidth + this.gutter) * 2
34388         });
34389         
34390         pos.push({
34391             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34392             y : minY + (this.unitWidth + this.gutter) * 2
34393         });
34394         
34395         pos.push({
34396             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),
34397             y : minY + (this.unitWidth + this.gutter) * 2
34398         });
34399
34400         return pos;
34401         
34402     },
34403     
34404     /**
34405     * remove a Masonry Brick
34406     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34407     */
34408     removeBrick : function(brick_id)
34409     {
34410         if (!brick_id) {
34411             return;
34412         }
34413         
34414         for (var i = 0; i<this.bricks.length; i++) {
34415             if (this.bricks[i].id == brick_id) {
34416                 this.bricks.splice(i,1);
34417                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34418                 this.initial();
34419             }
34420         }
34421     },
34422     
34423     /**
34424     * adds a Masonry Brick
34425     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34426     */
34427     addBrick : function(cfg)
34428     {
34429         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34430         //this.register(cn);
34431         cn.parentId = this.id;
34432         cn.render(this.el);
34433         return cn;
34434     },
34435     
34436     /**
34437     * register a Masonry Brick
34438     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34439     */
34440     
34441     register : function(brick)
34442     {
34443         this.bricks.push(brick);
34444         brick.masonryId = this.id;
34445     },
34446     
34447     /**
34448     * clear all the Masonry Brick
34449     */
34450     clearAll : function()
34451     {
34452         this.bricks = [];
34453         //this.getChildContainer().dom.innerHTML = "";
34454         this.el.dom.innerHTML = '';
34455     },
34456     
34457     getSelected : function()
34458     {
34459         if (!this.selectedBrick) {
34460             return false;
34461         }
34462         
34463         return this.selectedBrick;
34464     }
34465 });
34466
34467 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34468     
34469     groups: {},
34470      /**
34471     * register a Masonry Layout
34472     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34473     */
34474     
34475     register : function(layout)
34476     {
34477         this.groups[layout.id] = layout;
34478     },
34479     /**
34480     * fetch a  Masonry Layout based on the masonry layout ID
34481     * @param {string} the masonry layout to add
34482     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34483     */
34484     
34485     get: function(layout_id) {
34486         if (typeof(this.groups[layout_id]) == 'undefined') {
34487             return false;
34488         }
34489         return this.groups[layout_id] ;
34490     }
34491     
34492     
34493     
34494 });
34495
34496  
34497
34498  /**
34499  *
34500  * This is based on 
34501  * http://masonry.desandro.com
34502  *
34503  * The idea is to render all the bricks based on vertical width...
34504  *
34505  * The original code extends 'outlayer' - we might need to use that....
34506  * 
34507  */
34508
34509
34510 /**
34511  * @class Roo.bootstrap.LayoutMasonryAuto
34512  * @extends Roo.bootstrap.Component
34513  * Bootstrap Layout Masonry class
34514  * 
34515  * @constructor
34516  * Create a new Element
34517  * @param {Object} config The config object
34518  */
34519
34520 Roo.bootstrap.LayoutMasonryAuto = function(config){
34521     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34522 };
34523
34524 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34525     
34526       /**
34527      * @cfg {Boolean} isFitWidth  - resize the width..
34528      */   
34529     isFitWidth : false,  // options..
34530     /**
34531      * @cfg {Boolean} isOriginLeft = left align?
34532      */   
34533     isOriginLeft : true,
34534     /**
34535      * @cfg {Boolean} isOriginTop = top align?
34536      */   
34537     isOriginTop : false,
34538     /**
34539      * @cfg {Boolean} isLayoutInstant = no animation?
34540      */   
34541     isLayoutInstant : false, // needed?
34542     /**
34543      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34544      */   
34545     isResizingContainer : true,
34546     /**
34547      * @cfg {Number} columnWidth  width of the columns 
34548      */   
34549     
34550     columnWidth : 0,
34551     
34552     /**
34553      * @cfg {Number} maxCols maximum number of columns
34554      */   
34555     
34556     maxCols: 0,
34557     /**
34558      * @cfg {Number} padHeight padding below box..
34559      */   
34560     
34561     padHeight : 10, 
34562     
34563     /**
34564      * @cfg {Boolean} isAutoInitial defalut true
34565      */   
34566     
34567     isAutoInitial : true, 
34568     
34569     // private?
34570     gutter : 0,
34571     
34572     containerWidth: 0,
34573     initialColumnWidth : 0,
34574     currentSize : null,
34575     
34576     colYs : null, // array.
34577     maxY : 0,
34578     padWidth: 10,
34579     
34580     
34581     tag: 'div',
34582     cls: '',
34583     bricks: null, //CompositeElement
34584     cols : 0, // array?
34585     // element : null, // wrapped now this.el
34586     _isLayoutInited : null, 
34587     
34588     
34589     getAutoCreate : function(){
34590         
34591         var cfg = {
34592             tag: this.tag,
34593             cls: 'blog-masonary-wrapper ' + this.cls,
34594             cn : {
34595                 cls : 'mas-boxes masonary'
34596             }
34597         };
34598         
34599         return cfg;
34600     },
34601     
34602     getChildContainer: function( )
34603     {
34604         if (this.boxesEl) {
34605             return this.boxesEl;
34606         }
34607         
34608         this.boxesEl = this.el.select('.mas-boxes').first();
34609         
34610         return this.boxesEl;
34611     },
34612     
34613     
34614     initEvents : function()
34615     {
34616         var _this = this;
34617         
34618         if(this.isAutoInitial){
34619             Roo.log('hook children rendered');
34620             this.on('childrenrendered', function() {
34621                 Roo.log('children rendered');
34622                 _this.initial();
34623             } ,this);
34624         }
34625         
34626     },
34627     
34628     initial : function()
34629     {
34630         this.reloadItems();
34631
34632         this.currentSize = this.el.getBox(true);
34633
34634         /// was window resize... - let's see if this works..
34635         Roo.EventManager.onWindowResize(this.resize, this); 
34636
34637         if(!this.isAutoInitial){
34638             this.layout();
34639             return;
34640         }
34641         
34642         this.layout.defer(500,this);
34643     },
34644     
34645     reloadItems: function()
34646     {
34647         this.bricks = this.el.select('.masonry-brick', true);
34648         
34649         this.bricks.each(function(b) {
34650             //Roo.log(b.getSize());
34651             if (!b.attr('originalwidth')) {
34652                 b.attr('originalwidth',  b.getSize().width);
34653             }
34654             
34655         });
34656         
34657         Roo.log(this.bricks.elements.length);
34658     },
34659     
34660     resize : function()
34661     {
34662         Roo.log('resize');
34663         var cs = this.el.getBox(true);
34664         
34665         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34666             Roo.log("no change in with or X");
34667             return;
34668         }
34669         this.currentSize = cs;
34670         this.layout();
34671     },
34672     
34673     layout : function()
34674     {
34675          Roo.log('layout');
34676         this._resetLayout();
34677         //this._manageStamps();
34678       
34679         // don't animate first layout
34680         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34681         this.layoutItems( isInstant );
34682       
34683         // flag for initalized
34684         this._isLayoutInited = true;
34685     },
34686     
34687     layoutItems : function( isInstant )
34688     {
34689         //var items = this._getItemsForLayout( this.items );
34690         // original code supports filtering layout items.. we just ignore it..
34691         
34692         this._layoutItems( this.bricks , isInstant );
34693       
34694         this._postLayout();
34695     },
34696     _layoutItems : function ( items , isInstant)
34697     {
34698        //this.fireEvent( 'layout', this, items );
34699     
34700
34701         if ( !items || !items.elements.length ) {
34702           // no items, emit event with empty array
34703             return;
34704         }
34705
34706         var queue = [];
34707         items.each(function(item) {
34708             Roo.log("layout item");
34709             Roo.log(item);
34710             // get x/y object from method
34711             var position = this._getItemLayoutPosition( item );
34712             // enqueue
34713             position.item = item;
34714             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34715             queue.push( position );
34716         }, this);
34717       
34718         this._processLayoutQueue( queue );
34719     },
34720     /** Sets position of item in DOM
34721     * @param {Element} item
34722     * @param {Number} x - horizontal position
34723     * @param {Number} y - vertical position
34724     * @param {Boolean} isInstant - disables transitions
34725     */
34726     _processLayoutQueue : function( queue )
34727     {
34728         for ( var i=0, len = queue.length; i < len; i++ ) {
34729             var obj = queue[i];
34730             obj.item.position('absolute');
34731             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34732         }
34733     },
34734       
34735     
34736     /**
34737     * Any logic you want to do after each layout,
34738     * i.e. size the container
34739     */
34740     _postLayout : function()
34741     {
34742         this.resizeContainer();
34743     },
34744     
34745     resizeContainer : function()
34746     {
34747         if ( !this.isResizingContainer ) {
34748             return;
34749         }
34750         var size = this._getContainerSize();
34751         if ( size ) {
34752             this.el.setSize(size.width,size.height);
34753             this.boxesEl.setSize(size.width,size.height);
34754         }
34755     },
34756     
34757     
34758     
34759     _resetLayout : function()
34760     {
34761         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34762         this.colWidth = this.el.getWidth();
34763         //this.gutter = this.el.getWidth(); 
34764         
34765         this.measureColumns();
34766
34767         // reset column Y
34768         var i = this.cols;
34769         this.colYs = [];
34770         while (i--) {
34771             this.colYs.push( 0 );
34772         }
34773     
34774         this.maxY = 0;
34775     },
34776
34777     measureColumns : function()
34778     {
34779         this.getContainerWidth();
34780       // if columnWidth is 0, default to outerWidth of first item
34781         if ( !this.columnWidth ) {
34782             var firstItem = this.bricks.first();
34783             Roo.log(firstItem);
34784             this.columnWidth  = this.containerWidth;
34785             if (firstItem && firstItem.attr('originalwidth') ) {
34786                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34787             }
34788             // columnWidth fall back to item of first element
34789             Roo.log("set column width?");
34790                         this.initialColumnWidth = this.columnWidth  ;
34791
34792             // if first elem has no width, default to size of container
34793             
34794         }
34795         
34796         
34797         if (this.initialColumnWidth) {
34798             this.columnWidth = this.initialColumnWidth;
34799         }
34800         
34801         
34802             
34803         // column width is fixed at the top - however if container width get's smaller we should
34804         // reduce it...
34805         
34806         // this bit calcs how man columns..
34807             
34808         var columnWidth = this.columnWidth += this.gutter;
34809       
34810         // calculate columns
34811         var containerWidth = this.containerWidth + this.gutter;
34812         
34813         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34814         // fix rounding errors, typically with gutters
34815         var excess = columnWidth - containerWidth % columnWidth;
34816         
34817         
34818         // if overshoot is less than a pixel, round up, otherwise floor it
34819         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34820         cols = Math[ mathMethod ]( cols );
34821         this.cols = Math.max( cols, 1 );
34822         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34823         
34824          // padding positioning..
34825         var totalColWidth = this.cols * this.columnWidth;
34826         var padavail = this.containerWidth - totalColWidth;
34827         // so for 2 columns - we need 3 'pads'
34828         
34829         var padNeeded = (1+this.cols) * this.padWidth;
34830         
34831         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34832         
34833         this.columnWidth += padExtra
34834         //this.padWidth = Math.floor(padavail /  ( this.cols));
34835         
34836         // adjust colum width so that padding is fixed??
34837         
34838         // we have 3 columns ... total = width * 3
34839         // we have X left over... that should be used by 
34840         
34841         //if (this.expandC) {
34842             
34843         //}
34844         
34845         
34846         
34847     },
34848     
34849     getContainerWidth : function()
34850     {
34851        /* // container is parent if fit width
34852         var container = this.isFitWidth ? this.element.parentNode : this.element;
34853         // check that this.size and size are there
34854         // IE8 triggers resize on body size change, so they might not be
34855         
34856         var size = getSize( container );  //FIXME
34857         this.containerWidth = size && size.innerWidth; //FIXME
34858         */
34859          
34860         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34861         
34862     },
34863     
34864     _getItemLayoutPosition : function( item )  // what is item?
34865     {
34866         // we resize the item to our columnWidth..
34867       
34868         item.setWidth(this.columnWidth);
34869         item.autoBoxAdjust  = false;
34870         
34871         var sz = item.getSize();
34872  
34873         // how many columns does this brick span
34874         var remainder = this.containerWidth % this.columnWidth;
34875         
34876         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34877         // round if off by 1 pixel, otherwise use ceil
34878         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34879         colSpan = Math.min( colSpan, this.cols );
34880         
34881         // normally this should be '1' as we dont' currently allow multi width columns..
34882         
34883         var colGroup = this._getColGroup( colSpan );
34884         // get the minimum Y value from the columns
34885         var minimumY = Math.min.apply( Math, colGroup );
34886         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34887         
34888         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34889          
34890         // position the brick
34891         var position = {
34892             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34893             y: this.currentSize.y + minimumY + this.padHeight
34894         };
34895         
34896         Roo.log(position);
34897         // apply setHeight to necessary columns
34898         var setHeight = minimumY + sz.height + this.padHeight;
34899         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34900         
34901         var setSpan = this.cols + 1 - colGroup.length;
34902         for ( var i = 0; i < setSpan; i++ ) {
34903           this.colYs[ shortColIndex + i ] = setHeight ;
34904         }
34905       
34906         return position;
34907     },
34908     
34909     /**
34910      * @param {Number} colSpan - number of columns the element spans
34911      * @returns {Array} colGroup
34912      */
34913     _getColGroup : function( colSpan )
34914     {
34915         if ( colSpan < 2 ) {
34916           // if brick spans only one column, use all the column Ys
34917           return this.colYs;
34918         }
34919       
34920         var colGroup = [];
34921         // how many different places could this brick fit horizontally
34922         var groupCount = this.cols + 1 - colSpan;
34923         // for each group potential horizontal position
34924         for ( var i = 0; i < groupCount; i++ ) {
34925           // make an array of colY values for that one group
34926           var groupColYs = this.colYs.slice( i, i + colSpan );
34927           // and get the max value of the array
34928           colGroup[i] = Math.max.apply( Math, groupColYs );
34929         }
34930         return colGroup;
34931     },
34932     /*
34933     _manageStamp : function( stamp )
34934     {
34935         var stampSize =  stamp.getSize();
34936         var offset = stamp.getBox();
34937         // get the columns that this stamp affects
34938         var firstX = this.isOriginLeft ? offset.x : offset.right;
34939         var lastX = firstX + stampSize.width;
34940         var firstCol = Math.floor( firstX / this.columnWidth );
34941         firstCol = Math.max( 0, firstCol );
34942         
34943         var lastCol = Math.floor( lastX / this.columnWidth );
34944         // lastCol should not go over if multiple of columnWidth #425
34945         lastCol -= lastX % this.columnWidth ? 0 : 1;
34946         lastCol = Math.min( this.cols - 1, lastCol );
34947         
34948         // set colYs to bottom of the stamp
34949         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34950             stampSize.height;
34951             
34952         for ( var i = firstCol; i <= lastCol; i++ ) {
34953           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34954         }
34955     },
34956     */
34957     
34958     _getContainerSize : function()
34959     {
34960         this.maxY = Math.max.apply( Math, this.colYs );
34961         var size = {
34962             height: this.maxY
34963         };
34964       
34965         if ( this.isFitWidth ) {
34966             size.width = this._getContainerFitWidth();
34967         }
34968       
34969         return size;
34970     },
34971     
34972     _getContainerFitWidth : function()
34973     {
34974         var unusedCols = 0;
34975         // count unused columns
34976         var i = this.cols;
34977         while ( --i ) {
34978           if ( this.colYs[i] !== 0 ) {
34979             break;
34980           }
34981           unusedCols++;
34982         }
34983         // fit container to columns that have been used
34984         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34985     },
34986     
34987     needsResizeLayout : function()
34988     {
34989         var previousWidth = this.containerWidth;
34990         this.getContainerWidth();
34991         return previousWidth !== this.containerWidth;
34992     }
34993  
34994 });
34995
34996  
34997
34998  /*
34999  * - LGPL
35000  *
35001  * element
35002  * 
35003  */
35004
35005 /**
35006  * @class Roo.bootstrap.MasonryBrick
35007  * @extends Roo.bootstrap.Component
35008  * Bootstrap MasonryBrick class
35009  * 
35010  * @constructor
35011  * Create a new MasonryBrick
35012  * @param {Object} config The config object
35013  */
35014
35015 Roo.bootstrap.MasonryBrick = function(config){
35016     
35017     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35018     
35019     Roo.bootstrap.MasonryBrick.register(this);
35020     
35021     this.addEvents({
35022         // raw events
35023         /**
35024          * @event click
35025          * When a MasonryBrick is clcik
35026          * @param {Roo.bootstrap.MasonryBrick} this
35027          * @param {Roo.EventObject} e
35028          */
35029         "click" : true
35030     });
35031 };
35032
35033 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35034     
35035     /**
35036      * @cfg {String} title
35037      */   
35038     title : '',
35039     /**
35040      * @cfg {String} html
35041      */   
35042     html : '',
35043     /**
35044      * @cfg {String} bgimage
35045      */   
35046     bgimage : '',
35047     /**
35048      * @cfg {String} videourl
35049      */   
35050     videourl : '',
35051     /**
35052      * @cfg {String} cls
35053      */   
35054     cls : '',
35055     /**
35056      * @cfg {String} href
35057      */   
35058     href : '',
35059     /**
35060      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35061      */   
35062     size : 'xs',
35063     
35064     /**
35065      * @cfg {String} placetitle (center|bottom)
35066      */   
35067     placetitle : '',
35068     
35069     /**
35070      * @cfg {Boolean} isFitContainer defalut true
35071      */   
35072     isFitContainer : true, 
35073     
35074     /**
35075      * @cfg {Boolean} preventDefault defalut false
35076      */   
35077     preventDefault : false, 
35078     
35079     /**
35080      * @cfg {Boolean} inverse defalut false
35081      */   
35082     maskInverse : false, 
35083     
35084     getAutoCreate : function()
35085     {
35086         if(!this.isFitContainer){
35087             return this.getSplitAutoCreate();
35088         }
35089         
35090         var cls = 'masonry-brick masonry-brick-full';
35091         
35092         if(this.href.length){
35093             cls += ' masonry-brick-link';
35094         }
35095         
35096         if(this.bgimage.length){
35097             cls += ' masonry-brick-image';
35098         }
35099         
35100         if(this.maskInverse){
35101             cls += ' mask-inverse';
35102         }
35103         
35104         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35105             cls += ' enable-mask';
35106         }
35107         
35108         if(this.size){
35109             cls += ' masonry-' + this.size + '-brick';
35110         }
35111         
35112         if(this.placetitle.length){
35113             
35114             switch (this.placetitle) {
35115                 case 'center' :
35116                     cls += ' masonry-center-title';
35117                     break;
35118                 case 'bottom' :
35119                     cls += ' masonry-bottom-title';
35120                     break;
35121                 default:
35122                     break;
35123             }
35124             
35125         } else {
35126             if(!this.html.length && !this.bgimage.length){
35127                 cls += ' masonry-center-title';
35128             }
35129
35130             if(!this.html.length && this.bgimage.length){
35131                 cls += ' masonry-bottom-title';
35132             }
35133         }
35134         
35135         if(this.cls){
35136             cls += ' ' + this.cls;
35137         }
35138         
35139         var cfg = {
35140             tag: (this.href.length) ? 'a' : 'div',
35141             cls: cls,
35142             cn: [
35143                 {
35144                     tag: 'div',
35145                     cls: 'masonry-brick-mask'
35146                 },
35147                 {
35148                     tag: 'div',
35149                     cls: 'masonry-brick-paragraph',
35150                     cn: []
35151                 }
35152             ]
35153         };
35154         
35155         if(this.href.length){
35156             cfg.href = this.href;
35157         }
35158         
35159         var cn = cfg.cn[1].cn;
35160         
35161         if(this.title.length){
35162             cn.push({
35163                 tag: 'h4',
35164                 cls: 'masonry-brick-title',
35165                 html: this.title
35166             });
35167         }
35168         
35169         if(this.html.length){
35170             cn.push({
35171                 tag: 'p',
35172                 cls: 'masonry-brick-text',
35173                 html: this.html
35174             });
35175         }
35176         
35177         if (!this.title.length && !this.html.length) {
35178             cfg.cn[1].cls += ' hide';
35179         }
35180         
35181         if(this.bgimage.length){
35182             cfg.cn.push({
35183                 tag: 'img',
35184                 cls: 'masonry-brick-image-view',
35185                 src: this.bgimage
35186             });
35187         }
35188         
35189         if(this.videourl.length){
35190             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35191             // youtube support only?
35192             cfg.cn.push({
35193                 tag: 'iframe',
35194                 cls: 'masonry-brick-image-view',
35195                 src: vurl,
35196                 frameborder : 0,
35197                 allowfullscreen : true
35198             });
35199         }
35200         
35201         return cfg;
35202         
35203     },
35204     
35205     getSplitAutoCreate : function()
35206     {
35207         var cls = 'masonry-brick masonry-brick-split';
35208         
35209         if(this.href.length){
35210             cls += ' masonry-brick-link';
35211         }
35212         
35213         if(this.bgimage.length){
35214             cls += ' masonry-brick-image';
35215         }
35216         
35217         if(this.size){
35218             cls += ' masonry-' + this.size + '-brick';
35219         }
35220         
35221         switch (this.placetitle) {
35222             case 'center' :
35223                 cls += ' masonry-center-title';
35224                 break;
35225             case 'bottom' :
35226                 cls += ' masonry-bottom-title';
35227                 break;
35228             default:
35229                 if(!this.bgimage.length){
35230                     cls += ' masonry-center-title';
35231                 }
35232
35233                 if(this.bgimage.length){
35234                     cls += ' masonry-bottom-title';
35235                 }
35236                 break;
35237         }
35238         
35239         if(this.cls){
35240             cls += ' ' + this.cls;
35241         }
35242         
35243         var cfg = {
35244             tag: (this.href.length) ? 'a' : 'div',
35245             cls: cls,
35246             cn: [
35247                 {
35248                     tag: 'div',
35249                     cls: 'masonry-brick-split-head',
35250                     cn: [
35251                         {
35252                             tag: 'div',
35253                             cls: 'masonry-brick-paragraph',
35254                             cn: []
35255                         }
35256                     ]
35257                 },
35258                 {
35259                     tag: 'div',
35260                     cls: 'masonry-brick-split-body',
35261                     cn: []
35262                 }
35263             ]
35264         };
35265         
35266         if(this.href.length){
35267             cfg.href = this.href;
35268         }
35269         
35270         if(this.title.length){
35271             cfg.cn[0].cn[0].cn.push({
35272                 tag: 'h4',
35273                 cls: 'masonry-brick-title',
35274                 html: this.title
35275             });
35276         }
35277         
35278         if(this.html.length){
35279             cfg.cn[1].cn.push({
35280                 tag: 'p',
35281                 cls: 'masonry-brick-text',
35282                 html: this.html
35283             });
35284         }
35285
35286         if(this.bgimage.length){
35287             cfg.cn[0].cn.push({
35288                 tag: 'img',
35289                 cls: 'masonry-brick-image-view',
35290                 src: this.bgimage
35291             });
35292         }
35293         
35294         if(this.videourl.length){
35295             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35296             // youtube support only?
35297             cfg.cn[0].cn.cn.push({
35298                 tag: 'iframe',
35299                 cls: 'masonry-brick-image-view',
35300                 src: vurl,
35301                 frameborder : 0,
35302                 allowfullscreen : true
35303             });
35304         }
35305         
35306         return cfg;
35307     },
35308     
35309     initEvents: function() 
35310     {
35311         switch (this.size) {
35312             case 'xs' :
35313                 this.x = 1;
35314                 this.y = 1;
35315                 break;
35316             case 'sm' :
35317                 this.x = 2;
35318                 this.y = 2;
35319                 break;
35320             case 'md' :
35321             case 'md-left' :
35322             case 'md-right' :
35323                 this.x = 3;
35324                 this.y = 3;
35325                 break;
35326             case 'tall' :
35327                 this.x = 2;
35328                 this.y = 3;
35329                 break;
35330             case 'wide' :
35331                 this.x = 3;
35332                 this.y = 2;
35333                 break;
35334             case 'wide-thin' :
35335                 this.x = 3;
35336                 this.y = 1;
35337                 break;
35338                         
35339             default :
35340                 break;
35341         }
35342         
35343         if(Roo.isTouch){
35344             this.el.on('touchstart', this.onTouchStart, this);
35345             this.el.on('touchmove', this.onTouchMove, this);
35346             this.el.on('touchend', this.onTouchEnd, this);
35347             this.el.on('contextmenu', this.onContextMenu, this);
35348         } else {
35349             this.el.on('mouseenter'  ,this.enter, this);
35350             this.el.on('mouseleave', this.leave, this);
35351             this.el.on('click', this.onClick, this);
35352         }
35353         
35354         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35355             this.parent().bricks.push(this);   
35356         }
35357         
35358     },
35359     
35360     onClick: function(e, el)
35361     {
35362         var time = this.endTimer - this.startTimer;
35363         // Roo.log(e.preventDefault());
35364         if(Roo.isTouch){
35365             if(time > 1000){
35366                 e.preventDefault();
35367                 return;
35368             }
35369         }
35370         
35371         if(!this.preventDefault){
35372             return;
35373         }
35374         
35375         e.preventDefault();
35376         
35377         if (this.activeClass != '') {
35378             this.selectBrick();
35379         }
35380         
35381         this.fireEvent('click', this, e);
35382     },
35383     
35384     enter: function(e, el)
35385     {
35386         e.preventDefault();
35387         
35388         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35389             return;
35390         }
35391         
35392         if(this.bgimage.length && this.html.length){
35393             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35394         }
35395     },
35396     
35397     leave: function(e, el)
35398     {
35399         e.preventDefault();
35400         
35401         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35402             return;
35403         }
35404         
35405         if(this.bgimage.length && this.html.length){
35406             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35407         }
35408     },
35409     
35410     onTouchStart: function(e, el)
35411     {
35412 //        e.preventDefault();
35413         
35414         this.touchmoved = false;
35415         
35416         if(!this.isFitContainer){
35417             return;
35418         }
35419         
35420         if(!this.bgimage.length || !this.html.length){
35421             return;
35422         }
35423         
35424         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35425         
35426         this.timer = new Date().getTime();
35427         
35428     },
35429     
35430     onTouchMove: function(e, el)
35431     {
35432         this.touchmoved = true;
35433     },
35434     
35435     onContextMenu : function(e,el)
35436     {
35437         e.preventDefault();
35438         e.stopPropagation();
35439         return false;
35440     },
35441     
35442     onTouchEnd: function(e, el)
35443     {
35444 //        e.preventDefault();
35445         
35446         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35447         
35448             this.leave(e,el);
35449             
35450             return;
35451         }
35452         
35453         if(!this.bgimage.length || !this.html.length){
35454             
35455             if(this.href.length){
35456                 window.location.href = this.href;
35457             }
35458             
35459             return;
35460         }
35461         
35462         if(!this.isFitContainer){
35463             return;
35464         }
35465         
35466         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35467         
35468         window.location.href = this.href;
35469     },
35470     
35471     //selection on single brick only
35472     selectBrick : function() {
35473         
35474         if (!this.parentId) {
35475             return;
35476         }
35477         
35478         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35479         var index = m.selectedBrick.indexOf(this.id);
35480         
35481         if ( index > -1) {
35482             m.selectedBrick.splice(index,1);
35483             this.el.removeClass(this.activeClass);
35484             return;
35485         }
35486         
35487         for(var i = 0; i < m.selectedBrick.length; i++) {
35488             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35489             b.el.removeClass(b.activeClass);
35490         }
35491         
35492         m.selectedBrick = [];
35493         
35494         m.selectedBrick.push(this.id);
35495         this.el.addClass(this.activeClass);
35496         return;
35497     },
35498     
35499     isSelected : function(){
35500         return this.el.hasClass(this.activeClass);
35501         
35502     }
35503 });
35504
35505 Roo.apply(Roo.bootstrap.MasonryBrick, {
35506     
35507     //groups: {},
35508     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35509      /**
35510     * register a Masonry Brick
35511     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35512     */
35513     
35514     register : function(brick)
35515     {
35516         //this.groups[brick.id] = brick;
35517         this.groups.add(brick.id, brick);
35518     },
35519     /**
35520     * fetch a  masonry brick based on the masonry brick ID
35521     * @param {string} the masonry brick to add
35522     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35523     */
35524     
35525     get: function(brick_id) 
35526     {
35527         // if (typeof(this.groups[brick_id]) == 'undefined') {
35528         //     return false;
35529         // }
35530         // return this.groups[brick_id] ;
35531         
35532         if(this.groups.key(brick_id)) {
35533             return this.groups.key(brick_id);
35534         }
35535         
35536         return false;
35537     }
35538     
35539     
35540     
35541 });
35542
35543  /*
35544  * - LGPL
35545  *
35546  * element
35547  * 
35548  */
35549
35550 /**
35551  * @class Roo.bootstrap.Brick
35552  * @extends Roo.bootstrap.Component
35553  * Bootstrap Brick class
35554  * 
35555  * @constructor
35556  * Create a new Brick
35557  * @param {Object} config The config object
35558  */
35559
35560 Roo.bootstrap.Brick = function(config){
35561     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35562     
35563     this.addEvents({
35564         // raw events
35565         /**
35566          * @event click
35567          * When a Brick is click
35568          * @param {Roo.bootstrap.Brick} this
35569          * @param {Roo.EventObject} e
35570          */
35571         "click" : true
35572     });
35573 };
35574
35575 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35576     
35577     /**
35578      * @cfg {String} title
35579      */   
35580     title : '',
35581     /**
35582      * @cfg {String} html
35583      */   
35584     html : '',
35585     /**
35586      * @cfg {String} bgimage
35587      */   
35588     bgimage : '',
35589     /**
35590      * @cfg {String} cls
35591      */   
35592     cls : '',
35593     /**
35594      * @cfg {String} href
35595      */   
35596     href : '',
35597     /**
35598      * @cfg {String} video
35599      */   
35600     video : '',
35601     /**
35602      * @cfg {Boolean} square
35603      */   
35604     square : true,
35605     
35606     getAutoCreate : function()
35607     {
35608         var cls = 'roo-brick';
35609         
35610         if(this.href.length){
35611             cls += ' roo-brick-link';
35612         }
35613         
35614         if(this.bgimage.length){
35615             cls += ' roo-brick-image';
35616         }
35617         
35618         if(!this.html.length && !this.bgimage.length){
35619             cls += ' roo-brick-center-title';
35620         }
35621         
35622         if(!this.html.length && this.bgimage.length){
35623             cls += ' roo-brick-bottom-title';
35624         }
35625         
35626         if(this.cls){
35627             cls += ' ' + this.cls;
35628         }
35629         
35630         var cfg = {
35631             tag: (this.href.length) ? 'a' : 'div',
35632             cls: cls,
35633             cn: [
35634                 {
35635                     tag: 'div',
35636                     cls: 'roo-brick-paragraph',
35637                     cn: []
35638                 }
35639             ]
35640         };
35641         
35642         if(this.href.length){
35643             cfg.href = this.href;
35644         }
35645         
35646         var cn = cfg.cn[0].cn;
35647         
35648         if(this.title.length){
35649             cn.push({
35650                 tag: 'h4',
35651                 cls: 'roo-brick-title',
35652                 html: this.title
35653             });
35654         }
35655         
35656         if(this.html.length){
35657             cn.push({
35658                 tag: 'p',
35659                 cls: 'roo-brick-text',
35660                 html: this.html
35661             });
35662         } else {
35663             cn.cls += ' hide';
35664         }
35665         
35666         if(this.bgimage.length){
35667             cfg.cn.push({
35668                 tag: 'img',
35669                 cls: 'roo-brick-image-view',
35670                 src: this.bgimage
35671             });
35672         }
35673         
35674         return cfg;
35675     },
35676     
35677     initEvents: function() 
35678     {
35679         if(this.title.length || this.html.length){
35680             this.el.on('mouseenter'  ,this.enter, this);
35681             this.el.on('mouseleave', this.leave, this);
35682         }
35683         
35684         Roo.EventManager.onWindowResize(this.resize, this); 
35685         
35686         if(this.bgimage.length){
35687             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35688             this.imageEl.on('load', this.onImageLoad, this);
35689             return;
35690         }
35691         
35692         this.resize();
35693     },
35694     
35695     onImageLoad : function()
35696     {
35697         this.resize();
35698     },
35699     
35700     resize : function()
35701     {
35702         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35703         
35704         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35705         
35706         if(this.bgimage.length){
35707             var image = this.el.select('.roo-brick-image-view', true).first();
35708             
35709             image.setWidth(paragraph.getWidth());
35710             
35711             if(this.square){
35712                 image.setHeight(paragraph.getWidth());
35713             }
35714             
35715             this.el.setHeight(image.getHeight());
35716             paragraph.setHeight(image.getHeight());
35717             
35718         }
35719         
35720     },
35721     
35722     enter: function(e, el)
35723     {
35724         e.preventDefault();
35725         
35726         if(this.bgimage.length){
35727             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35728             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35729         }
35730     },
35731     
35732     leave: function(e, el)
35733     {
35734         e.preventDefault();
35735         
35736         if(this.bgimage.length){
35737             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35738             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35739         }
35740     }
35741     
35742 });
35743
35744  
35745
35746  /*
35747  * - LGPL
35748  *
35749  * Number field 
35750  */
35751
35752 /**
35753  * @class Roo.bootstrap.NumberField
35754  * @extends Roo.bootstrap.Input
35755  * Bootstrap NumberField class
35756  * 
35757  * 
35758  * 
35759  * 
35760  * @constructor
35761  * Create a new NumberField
35762  * @param {Object} config The config object
35763  */
35764
35765 Roo.bootstrap.NumberField = function(config){
35766     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35767 };
35768
35769 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35770     
35771     /**
35772      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35773      */
35774     allowDecimals : true,
35775     /**
35776      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35777      */
35778     decimalSeparator : ".",
35779     /**
35780      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35781      */
35782     decimalPrecision : 2,
35783     /**
35784      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35785      */
35786     allowNegative : true,
35787     
35788     /**
35789      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35790      */
35791     allowZero: true,
35792     /**
35793      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35794      */
35795     minValue : Number.NEGATIVE_INFINITY,
35796     /**
35797      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35798      */
35799     maxValue : Number.MAX_VALUE,
35800     /**
35801      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35802      */
35803     minText : "The minimum value for this field is {0}",
35804     /**
35805      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35806      */
35807     maxText : "The maximum value for this field is {0}",
35808     /**
35809      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35810      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35811      */
35812     nanText : "{0} is not a valid number",
35813     /**
35814      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35815      */
35816     thousandsDelimiter : false,
35817     /**
35818      * @cfg {String} valueAlign alignment of value
35819      */
35820     valueAlign : "left",
35821
35822     getAutoCreate : function()
35823     {
35824         var hiddenInput = {
35825             tag: 'input',
35826             type: 'hidden',
35827             id: Roo.id(),
35828             cls: 'hidden-number-input'
35829         };
35830         
35831         if (this.name) {
35832             hiddenInput.name = this.name;
35833         }
35834         
35835         this.name = '';
35836         
35837         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35838         
35839         this.name = hiddenInput.name;
35840         
35841         if(cfg.cn.length > 0) {
35842             cfg.cn.push(hiddenInput);
35843         }
35844         
35845         return cfg;
35846     },
35847
35848     // private
35849     initEvents : function()
35850     {   
35851         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35852         
35853         var allowed = "0123456789";
35854         
35855         if(this.allowDecimals){
35856             allowed += this.decimalSeparator;
35857         }
35858         
35859         if(this.allowNegative){
35860             allowed += "-";
35861         }
35862         
35863         if(this.thousandsDelimiter) {
35864             allowed += ",";
35865         }
35866         
35867         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35868         
35869         var keyPress = function(e){
35870             
35871             var k = e.getKey();
35872             
35873             var c = e.getCharCode();
35874             
35875             if(
35876                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35877                     allowed.indexOf(String.fromCharCode(c)) === -1
35878             ){
35879                 e.stopEvent();
35880                 return;
35881             }
35882             
35883             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35884                 return;
35885             }
35886             
35887             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35888                 e.stopEvent();
35889             }
35890         };
35891         
35892         this.el.on("keypress", keyPress, this);
35893     },
35894     
35895     validateValue : function(value)
35896     {
35897         
35898         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35899             return false;
35900         }
35901         
35902         var num = this.parseValue(value);
35903         
35904         if(isNaN(num)){
35905             this.markInvalid(String.format(this.nanText, value));
35906             return false;
35907         }
35908         
35909         if(num < this.minValue){
35910             this.markInvalid(String.format(this.minText, this.minValue));
35911             return false;
35912         }
35913         
35914         if(num > this.maxValue){
35915             this.markInvalid(String.format(this.maxText, this.maxValue));
35916             return false;
35917         }
35918         
35919         return true;
35920     },
35921
35922     getValue : function()
35923     {
35924         var v = this.hiddenEl().getValue();
35925         
35926         return this.fixPrecision(this.parseValue(v));
35927     },
35928
35929     parseValue : function(value)
35930     {
35931         if(this.thousandsDelimiter) {
35932             value += "";
35933             r = new RegExp(",", "g");
35934             value = value.replace(r, "");
35935         }
35936         
35937         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35938         return isNaN(value) ? '' : value;
35939     },
35940
35941     fixPrecision : function(value)
35942     {
35943         if(this.thousandsDelimiter) {
35944             value += "";
35945             r = new RegExp(",", "g");
35946             value = value.replace(r, "");
35947         }
35948         
35949         var nan = isNaN(value);
35950         
35951         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35952             return nan ? '' : value;
35953         }
35954         return parseFloat(value).toFixed(this.decimalPrecision);
35955     },
35956
35957     setValue : function(v)
35958     {
35959         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35960         
35961         this.value = v;
35962         
35963         if(this.rendered){
35964             
35965             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35966             
35967             this.inputEl().dom.value = (v == '') ? '' :
35968                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35969             
35970             if(!this.allowZero && v === '0') {
35971                 this.hiddenEl().dom.value = '';
35972                 this.inputEl().dom.value = '';
35973             }
35974             
35975             this.validate();
35976         }
35977     },
35978
35979     decimalPrecisionFcn : function(v)
35980     {
35981         return Math.floor(v);
35982     },
35983
35984     beforeBlur : function()
35985     {
35986         var v = this.parseValue(this.getRawValue());
35987         
35988         if(v || v === 0 || v === ''){
35989             this.setValue(v);
35990         }
35991     },
35992     
35993     hiddenEl : function()
35994     {
35995         return this.el.select('input.hidden-number-input',true).first();
35996     }
35997     
35998 });
35999
36000  
36001
36002 /*
36003 * Licence: LGPL
36004 */
36005
36006 /**
36007  * @class Roo.bootstrap.DocumentSlider
36008  * @extends Roo.bootstrap.Component
36009  * Bootstrap DocumentSlider class
36010  * 
36011  * @constructor
36012  * Create a new DocumentViewer
36013  * @param {Object} config The config object
36014  */
36015
36016 Roo.bootstrap.DocumentSlider = function(config){
36017     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36018     
36019     this.files = [];
36020     
36021     this.addEvents({
36022         /**
36023          * @event initial
36024          * Fire after initEvent
36025          * @param {Roo.bootstrap.DocumentSlider} this
36026          */
36027         "initial" : true,
36028         /**
36029          * @event update
36030          * Fire after update
36031          * @param {Roo.bootstrap.DocumentSlider} this
36032          */
36033         "update" : true,
36034         /**
36035          * @event click
36036          * Fire after click
36037          * @param {Roo.bootstrap.DocumentSlider} this
36038          */
36039         "click" : true
36040     });
36041 };
36042
36043 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36044     
36045     files : false,
36046     
36047     indicator : 0,
36048     
36049     getAutoCreate : function()
36050     {
36051         var cfg = {
36052             tag : 'div',
36053             cls : 'roo-document-slider',
36054             cn : [
36055                 {
36056                     tag : 'div',
36057                     cls : 'roo-document-slider-header',
36058                     cn : [
36059                         {
36060                             tag : 'div',
36061                             cls : 'roo-document-slider-header-title'
36062                         }
36063                     ]
36064                 },
36065                 {
36066                     tag : 'div',
36067                     cls : 'roo-document-slider-body',
36068                     cn : [
36069                         {
36070                             tag : 'div',
36071                             cls : 'roo-document-slider-prev',
36072                             cn : [
36073                                 {
36074                                     tag : 'i',
36075                                     cls : 'fa fa-chevron-left'
36076                                 }
36077                             ]
36078                         },
36079                         {
36080                             tag : 'div',
36081                             cls : 'roo-document-slider-thumb',
36082                             cn : [
36083                                 {
36084                                     tag : 'img',
36085                                     cls : 'roo-document-slider-image'
36086                                 }
36087                             ]
36088                         },
36089                         {
36090                             tag : 'div',
36091                             cls : 'roo-document-slider-next',
36092                             cn : [
36093                                 {
36094                                     tag : 'i',
36095                                     cls : 'fa fa-chevron-right'
36096                                 }
36097                             ]
36098                         }
36099                     ]
36100                 }
36101             ]
36102         };
36103         
36104         return cfg;
36105     },
36106     
36107     initEvents : function()
36108     {
36109         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36110         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36111         
36112         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36113         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36114         
36115         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36116         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36117         
36118         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36119         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36120         
36121         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36122         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36123         
36124         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36125         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36126         
36127         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36128         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36129         
36130         this.thumbEl.on('click', this.onClick, this);
36131         
36132         this.prevIndicator.on('click', this.prev, this);
36133         
36134         this.nextIndicator.on('click', this.next, this);
36135         
36136     },
36137     
36138     initial : function()
36139     {
36140         if(this.files.length){
36141             this.indicator = 1;
36142             this.update()
36143         }
36144         
36145         this.fireEvent('initial', this);
36146     },
36147     
36148     update : function()
36149     {
36150         this.imageEl.attr('src', this.files[this.indicator - 1]);
36151         
36152         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36153         
36154         this.prevIndicator.show();
36155         
36156         if(this.indicator == 1){
36157             this.prevIndicator.hide();
36158         }
36159         
36160         this.nextIndicator.show();
36161         
36162         if(this.indicator == this.files.length){
36163             this.nextIndicator.hide();
36164         }
36165         
36166         this.thumbEl.scrollTo('top');
36167         
36168         this.fireEvent('update', this);
36169     },
36170     
36171     onClick : function(e)
36172     {
36173         e.preventDefault();
36174         
36175         this.fireEvent('click', this);
36176     },
36177     
36178     prev : function(e)
36179     {
36180         e.preventDefault();
36181         
36182         this.indicator = Math.max(1, this.indicator - 1);
36183         
36184         this.update();
36185     },
36186     
36187     next : function(e)
36188     {
36189         e.preventDefault();
36190         
36191         this.indicator = Math.min(this.files.length, this.indicator + 1);
36192         
36193         this.update();
36194     }
36195 });
36196 /*
36197  * - LGPL
36198  *
36199  * RadioSet
36200  *
36201  *
36202  */
36203
36204 /**
36205  * @class Roo.bootstrap.RadioSet
36206  * @extends Roo.bootstrap.Input
36207  * Bootstrap RadioSet class
36208  * @cfg {String} indicatorpos (left|right) default left
36209  * @cfg {Boolean} inline (true|false) inline the element (default true)
36210  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36211  * @constructor
36212  * Create a new RadioSet
36213  * @param {Object} config The config object
36214  */
36215
36216 Roo.bootstrap.RadioSet = function(config){
36217     
36218     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36219     
36220     this.radioes = [];
36221     
36222     Roo.bootstrap.RadioSet.register(this);
36223     
36224     this.addEvents({
36225         /**
36226         * @event check
36227         * Fires when the element is checked or unchecked.
36228         * @param {Roo.bootstrap.RadioSet} this This radio
36229         * @param {Roo.bootstrap.Radio} item The checked item
36230         */
36231        check : true,
36232        /**
36233         * @event click
36234         * Fires when the element is click.
36235         * @param {Roo.bootstrap.RadioSet} this This radio set
36236         * @param {Roo.bootstrap.Radio} item The checked item
36237         * @param {Roo.EventObject} e The event object
36238         */
36239        click : true
36240     });
36241     
36242 };
36243
36244 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36245
36246     radioes : false,
36247     
36248     inline : true,
36249     
36250     weight : '',
36251     
36252     indicatorpos : 'left',
36253     
36254     getAutoCreate : function()
36255     {
36256         var label = {
36257             tag : 'label',
36258             cls : 'roo-radio-set-label',
36259             cn : [
36260                 {
36261                     tag : 'span',
36262                     html : this.fieldLabel
36263                 }
36264             ]
36265         };
36266         if (Roo.bootstrap.version == 3) {
36267             
36268             
36269             if(this.indicatorpos == 'left'){
36270                 label.cn.unshift({
36271                     tag : 'i',
36272                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36273                     tooltip : 'This field is required'
36274                 });
36275             } else {
36276                 label.cn.push({
36277                     tag : 'i',
36278                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36279                     tooltip : 'This field is required'
36280                 });
36281             }
36282         }
36283         var items = {
36284             tag : 'div',
36285             cls : 'roo-radio-set-items'
36286         };
36287         
36288         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36289         
36290         if (align === 'left' && this.fieldLabel.length) {
36291             
36292             items = {
36293                 cls : "roo-radio-set-right", 
36294                 cn: [
36295                     items
36296                 ]
36297             };
36298             
36299             if(this.labelWidth > 12){
36300                 label.style = "width: " + this.labelWidth + 'px';
36301             }
36302             
36303             if(this.labelWidth < 13 && this.labelmd == 0){
36304                 this.labelmd = this.labelWidth;
36305             }
36306             
36307             if(this.labellg > 0){
36308                 label.cls += ' col-lg-' + this.labellg;
36309                 items.cls += ' col-lg-' + (12 - this.labellg);
36310             }
36311             
36312             if(this.labelmd > 0){
36313                 label.cls += ' col-md-' + this.labelmd;
36314                 items.cls += ' col-md-' + (12 - this.labelmd);
36315             }
36316             
36317             if(this.labelsm > 0){
36318                 label.cls += ' col-sm-' + this.labelsm;
36319                 items.cls += ' col-sm-' + (12 - this.labelsm);
36320             }
36321             
36322             if(this.labelxs > 0){
36323                 label.cls += ' col-xs-' + this.labelxs;
36324                 items.cls += ' col-xs-' + (12 - this.labelxs);
36325             }
36326         }
36327         
36328         var cfg = {
36329             tag : 'div',
36330             cls : 'roo-radio-set',
36331             cn : [
36332                 {
36333                     tag : 'input',
36334                     cls : 'roo-radio-set-input',
36335                     type : 'hidden',
36336                     name : this.name,
36337                     value : this.value ? this.value :  ''
36338                 },
36339                 label,
36340                 items
36341             ]
36342         };
36343         
36344         if(this.weight.length){
36345             cfg.cls += ' roo-radio-' + this.weight;
36346         }
36347         
36348         if(this.inline) {
36349             cfg.cls += ' roo-radio-set-inline';
36350         }
36351         
36352         var settings=this;
36353         ['xs','sm','md','lg'].map(function(size){
36354             if (settings[size]) {
36355                 cfg.cls += ' col-' + size + '-' + settings[size];
36356             }
36357         });
36358         
36359         return cfg;
36360         
36361     },
36362
36363     initEvents : function()
36364     {
36365         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36366         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36367         
36368         if(!this.fieldLabel.length){
36369             this.labelEl.hide();
36370         }
36371         
36372         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36373         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36374         
36375         this.indicator = this.indicatorEl();
36376         
36377         if(this.indicator){
36378             this.indicator.addClass('invisible');
36379         }
36380         
36381         this.originalValue = this.getValue();
36382         
36383     },
36384     
36385     inputEl: function ()
36386     {
36387         return this.el.select('.roo-radio-set-input', true).first();
36388     },
36389     
36390     getChildContainer : function()
36391     {
36392         return this.itemsEl;
36393     },
36394     
36395     register : function(item)
36396     {
36397         this.radioes.push(item);
36398         
36399     },
36400     
36401     validate : function()
36402     {   
36403         if(this.getVisibilityEl().hasClass('hidden')){
36404             return true;
36405         }
36406         
36407         var valid = false;
36408         
36409         Roo.each(this.radioes, function(i){
36410             if(!i.checked){
36411                 return;
36412             }
36413             
36414             valid = true;
36415             return false;
36416         });
36417         
36418         if(this.allowBlank) {
36419             return true;
36420         }
36421         
36422         if(this.disabled || valid){
36423             this.markValid();
36424             return true;
36425         }
36426         
36427         this.markInvalid();
36428         return false;
36429         
36430     },
36431     
36432     markValid : function()
36433     {
36434         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36435             this.indicatorEl().removeClass('visible');
36436             this.indicatorEl().addClass('invisible');
36437         }
36438         
36439         
36440         if (Roo.bootstrap.version == 3) {
36441             this.el.removeClass([this.invalidClass, this.validClass]);
36442             this.el.addClass(this.validClass);
36443         } else {
36444             this.el.removeClass(['is-invalid','is-valid']);
36445             this.el.addClass(['is-valid']);
36446         }
36447         this.fireEvent('valid', this);
36448     },
36449     
36450     markInvalid : function(msg)
36451     {
36452         if(this.allowBlank || this.disabled){
36453             return;
36454         }
36455         
36456         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36457             this.indicatorEl().removeClass('invisible');
36458             this.indicatorEl().addClass('visible');
36459         }
36460         if (Roo.bootstrap.version == 3) {
36461             this.el.removeClass([this.invalidClass, this.validClass]);
36462             this.el.addClass(this.invalidClass);
36463         } else {
36464             this.el.removeClass(['is-invalid','is-valid']);
36465             this.el.addClass(['is-invalid']);
36466         }
36467         
36468         this.fireEvent('invalid', this, msg);
36469         
36470     },
36471     
36472     setValue : function(v, suppressEvent)
36473     {   
36474         if(this.value === v){
36475             return;
36476         }
36477         
36478         this.value = v;
36479         
36480         if(this.rendered){
36481             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36482         }
36483         
36484         Roo.each(this.radioes, function(i){
36485             i.checked = false;
36486             i.el.removeClass('checked');
36487         });
36488         
36489         Roo.each(this.radioes, function(i){
36490             
36491             if(i.value === v || i.value.toString() === v.toString()){
36492                 i.checked = true;
36493                 i.el.addClass('checked');
36494                 
36495                 if(suppressEvent !== true){
36496                     this.fireEvent('check', this, i);
36497                 }
36498                 
36499                 return false;
36500             }
36501             
36502         }, this);
36503         
36504         this.validate();
36505     },
36506     
36507     clearInvalid : function(){
36508         
36509         if(!this.el || this.preventMark){
36510             return;
36511         }
36512         
36513         this.el.removeClass([this.invalidClass]);
36514         
36515         this.fireEvent('valid', this);
36516     }
36517     
36518 });
36519
36520 Roo.apply(Roo.bootstrap.RadioSet, {
36521     
36522     groups: {},
36523     
36524     register : function(set)
36525     {
36526         this.groups[set.name] = set;
36527     },
36528     
36529     get: function(name) 
36530     {
36531         if (typeof(this.groups[name]) == 'undefined') {
36532             return false;
36533         }
36534         
36535         return this.groups[name] ;
36536     }
36537     
36538 });
36539 /*
36540  * Based on:
36541  * Ext JS Library 1.1.1
36542  * Copyright(c) 2006-2007, Ext JS, LLC.
36543  *
36544  * Originally Released Under LGPL - original licence link has changed is not relivant.
36545  *
36546  * Fork - LGPL
36547  * <script type="text/javascript">
36548  */
36549
36550
36551 /**
36552  * @class Roo.bootstrap.SplitBar
36553  * @extends Roo.util.Observable
36554  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36555  * <br><br>
36556  * Usage:
36557  * <pre><code>
36558 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36559                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36560 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36561 split.minSize = 100;
36562 split.maxSize = 600;
36563 split.animate = true;
36564 split.on('moved', splitterMoved);
36565 </code></pre>
36566  * @constructor
36567  * Create a new SplitBar
36568  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36569  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36570  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36571  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36572                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36573                         position of the SplitBar).
36574  */
36575 Roo.bootstrap.SplitBar = function(cfg){
36576     
36577     /** @private */
36578     
36579     //{
36580     //  dragElement : elm
36581     //  resizingElement: el,
36582         // optional..
36583     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36584     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36585         // existingProxy ???
36586     //}
36587     
36588     this.el = Roo.get(cfg.dragElement, true);
36589     this.el.dom.unselectable = "on";
36590     /** @private */
36591     this.resizingEl = Roo.get(cfg.resizingElement, true);
36592
36593     /**
36594      * @private
36595      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36596      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36597      * @type Number
36598      */
36599     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36600     
36601     /**
36602      * The minimum size of the resizing element. (Defaults to 0)
36603      * @type Number
36604      */
36605     this.minSize = 0;
36606     
36607     /**
36608      * The maximum size of the resizing element. (Defaults to 2000)
36609      * @type Number
36610      */
36611     this.maxSize = 2000;
36612     
36613     /**
36614      * Whether to animate the transition to the new size
36615      * @type Boolean
36616      */
36617     this.animate = false;
36618     
36619     /**
36620      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36621      * @type Boolean
36622      */
36623     this.useShim = false;
36624     
36625     /** @private */
36626     this.shim = null;
36627     
36628     if(!cfg.existingProxy){
36629         /** @private */
36630         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36631     }else{
36632         this.proxy = Roo.get(cfg.existingProxy).dom;
36633     }
36634     /** @private */
36635     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36636     
36637     /** @private */
36638     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36639     
36640     /** @private */
36641     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36642     
36643     /** @private */
36644     this.dragSpecs = {};
36645     
36646     /**
36647      * @private The adapter to use to positon and resize elements
36648      */
36649     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36650     this.adapter.init(this);
36651     
36652     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36653         /** @private */
36654         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36655         this.el.addClass("roo-splitbar-h");
36656     }else{
36657         /** @private */
36658         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36659         this.el.addClass("roo-splitbar-v");
36660     }
36661     
36662     this.addEvents({
36663         /**
36664          * @event resize
36665          * Fires when the splitter is moved (alias for {@link #event-moved})
36666          * @param {Roo.bootstrap.SplitBar} this
36667          * @param {Number} newSize the new width or height
36668          */
36669         "resize" : true,
36670         /**
36671          * @event moved
36672          * Fires when the splitter is moved
36673          * @param {Roo.bootstrap.SplitBar} this
36674          * @param {Number} newSize the new width or height
36675          */
36676         "moved" : true,
36677         /**
36678          * @event beforeresize
36679          * Fires before the splitter is dragged
36680          * @param {Roo.bootstrap.SplitBar} this
36681          */
36682         "beforeresize" : true,
36683
36684         "beforeapply" : true
36685     });
36686
36687     Roo.util.Observable.call(this);
36688 };
36689
36690 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36691     onStartProxyDrag : function(x, y){
36692         this.fireEvent("beforeresize", this);
36693         if(!this.overlay){
36694             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36695             o.unselectable();
36696             o.enableDisplayMode("block");
36697             // all splitbars share the same overlay
36698             Roo.bootstrap.SplitBar.prototype.overlay = o;
36699         }
36700         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36701         this.overlay.show();
36702         Roo.get(this.proxy).setDisplayed("block");
36703         var size = this.adapter.getElementSize(this);
36704         this.activeMinSize = this.getMinimumSize();;
36705         this.activeMaxSize = this.getMaximumSize();;
36706         var c1 = size - this.activeMinSize;
36707         var c2 = Math.max(this.activeMaxSize - size, 0);
36708         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36709             this.dd.resetConstraints();
36710             this.dd.setXConstraint(
36711                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36712                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36713             );
36714             this.dd.setYConstraint(0, 0);
36715         }else{
36716             this.dd.resetConstraints();
36717             this.dd.setXConstraint(0, 0);
36718             this.dd.setYConstraint(
36719                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36720                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36721             );
36722          }
36723         this.dragSpecs.startSize = size;
36724         this.dragSpecs.startPoint = [x, y];
36725         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36726     },
36727     
36728     /** 
36729      * @private Called after the drag operation by the DDProxy
36730      */
36731     onEndProxyDrag : function(e){
36732         Roo.get(this.proxy).setDisplayed(false);
36733         var endPoint = Roo.lib.Event.getXY(e);
36734         if(this.overlay){
36735             this.overlay.hide();
36736         }
36737         var newSize;
36738         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36739             newSize = this.dragSpecs.startSize + 
36740                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36741                     endPoint[0] - this.dragSpecs.startPoint[0] :
36742                     this.dragSpecs.startPoint[0] - endPoint[0]
36743                 );
36744         }else{
36745             newSize = this.dragSpecs.startSize + 
36746                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36747                     endPoint[1] - this.dragSpecs.startPoint[1] :
36748                     this.dragSpecs.startPoint[1] - endPoint[1]
36749                 );
36750         }
36751         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36752         if(newSize != this.dragSpecs.startSize){
36753             if(this.fireEvent('beforeapply', this, newSize) !== false){
36754                 this.adapter.setElementSize(this, newSize);
36755                 this.fireEvent("moved", this, newSize);
36756                 this.fireEvent("resize", this, newSize);
36757             }
36758         }
36759     },
36760     
36761     /**
36762      * Get the adapter this SplitBar uses
36763      * @return The adapter object
36764      */
36765     getAdapter : function(){
36766         return this.adapter;
36767     },
36768     
36769     /**
36770      * Set the adapter this SplitBar uses
36771      * @param {Object} adapter A SplitBar adapter object
36772      */
36773     setAdapter : function(adapter){
36774         this.adapter = adapter;
36775         this.adapter.init(this);
36776     },
36777     
36778     /**
36779      * Gets the minimum size for the resizing element
36780      * @return {Number} The minimum size
36781      */
36782     getMinimumSize : function(){
36783         return this.minSize;
36784     },
36785     
36786     /**
36787      * Sets the minimum size for the resizing element
36788      * @param {Number} minSize The minimum size
36789      */
36790     setMinimumSize : function(minSize){
36791         this.minSize = minSize;
36792     },
36793     
36794     /**
36795      * Gets the maximum size for the resizing element
36796      * @return {Number} The maximum size
36797      */
36798     getMaximumSize : function(){
36799         return this.maxSize;
36800     },
36801     
36802     /**
36803      * Sets the maximum size for the resizing element
36804      * @param {Number} maxSize The maximum size
36805      */
36806     setMaximumSize : function(maxSize){
36807         this.maxSize = maxSize;
36808     },
36809     
36810     /**
36811      * Sets the initialize size for the resizing element
36812      * @param {Number} size The initial size
36813      */
36814     setCurrentSize : function(size){
36815         var oldAnimate = this.animate;
36816         this.animate = false;
36817         this.adapter.setElementSize(this, size);
36818         this.animate = oldAnimate;
36819     },
36820     
36821     /**
36822      * Destroy this splitbar. 
36823      * @param {Boolean} removeEl True to remove the element
36824      */
36825     destroy : function(removeEl){
36826         if(this.shim){
36827             this.shim.remove();
36828         }
36829         this.dd.unreg();
36830         this.proxy.parentNode.removeChild(this.proxy);
36831         if(removeEl){
36832             this.el.remove();
36833         }
36834     }
36835 });
36836
36837 /**
36838  * @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.
36839  */
36840 Roo.bootstrap.SplitBar.createProxy = function(dir){
36841     var proxy = new Roo.Element(document.createElement("div"));
36842     proxy.unselectable();
36843     var cls = 'roo-splitbar-proxy';
36844     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36845     document.body.appendChild(proxy.dom);
36846     return proxy.dom;
36847 };
36848
36849 /** 
36850  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36851  * Default Adapter. It assumes the splitter and resizing element are not positioned
36852  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36853  */
36854 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36855 };
36856
36857 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36858     // do nothing for now
36859     init : function(s){
36860     
36861     },
36862     /**
36863      * Called before drag operations to get the current size of the resizing element. 
36864      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36865      */
36866      getElementSize : function(s){
36867         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36868             return s.resizingEl.getWidth();
36869         }else{
36870             return s.resizingEl.getHeight();
36871         }
36872     },
36873     
36874     /**
36875      * Called after drag operations to set the size of the resizing element.
36876      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36877      * @param {Number} newSize The new size to set
36878      * @param {Function} onComplete A function to be invoked when resizing is complete
36879      */
36880     setElementSize : function(s, newSize, onComplete){
36881         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36882             if(!s.animate){
36883                 s.resizingEl.setWidth(newSize);
36884                 if(onComplete){
36885                     onComplete(s, newSize);
36886                 }
36887             }else{
36888                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36889             }
36890         }else{
36891             
36892             if(!s.animate){
36893                 s.resizingEl.setHeight(newSize);
36894                 if(onComplete){
36895                     onComplete(s, newSize);
36896                 }
36897             }else{
36898                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36899             }
36900         }
36901     }
36902 };
36903
36904 /** 
36905  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36906  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36907  * Adapter that  moves the splitter element to align with the resized sizing element. 
36908  * Used with an absolute positioned SplitBar.
36909  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36910  * document.body, make sure you assign an id to the body element.
36911  */
36912 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36913     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36914     this.container = Roo.get(container);
36915 };
36916
36917 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36918     init : function(s){
36919         this.basic.init(s);
36920     },
36921     
36922     getElementSize : function(s){
36923         return this.basic.getElementSize(s);
36924     },
36925     
36926     setElementSize : function(s, newSize, onComplete){
36927         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36928     },
36929     
36930     moveSplitter : function(s){
36931         var yes = Roo.bootstrap.SplitBar;
36932         switch(s.placement){
36933             case yes.LEFT:
36934                 s.el.setX(s.resizingEl.getRight());
36935                 break;
36936             case yes.RIGHT:
36937                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36938                 break;
36939             case yes.TOP:
36940                 s.el.setY(s.resizingEl.getBottom());
36941                 break;
36942             case yes.BOTTOM:
36943                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36944                 break;
36945         }
36946     }
36947 };
36948
36949 /**
36950  * Orientation constant - Create a vertical SplitBar
36951  * @static
36952  * @type Number
36953  */
36954 Roo.bootstrap.SplitBar.VERTICAL = 1;
36955
36956 /**
36957  * Orientation constant - Create a horizontal SplitBar
36958  * @static
36959  * @type Number
36960  */
36961 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36962
36963 /**
36964  * Placement constant - The resizing element is to the left of the splitter element
36965  * @static
36966  * @type Number
36967  */
36968 Roo.bootstrap.SplitBar.LEFT = 1;
36969
36970 /**
36971  * Placement constant - The resizing element is to the right of the splitter element
36972  * @static
36973  * @type Number
36974  */
36975 Roo.bootstrap.SplitBar.RIGHT = 2;
36976
36977 /**
36978  * Placement constant - The resizing element is positioned above the splitter element
36979  * @static
36980  * @type Number
36981  */
36982 Roo.bootstrap.SplitBar.TOP = 3;
36983
36984 /**
36985  * Placement constant - The resizing element is positioned under splitter element
36986  * @static
36987  * @type Number
36988  */
36989 Roo.bootstrap.SplitBar.BOTTOM = 4;
36990 Roo.namespace("Roo.bootstrap.layout");/*
36991  * Based on:
36992  * Ext JS Library 1.1.1
36993  * Copyright(c) 2006-2007, Ext JS, LLC.
36994  *
36995  * Originally Released Under LGPL - original licence link has changed is not relivant.
36996  *
36997  * Fork - LGPL
36998  * <script type="text/javascript">
36999  */
37000
37001 /**
37002  * @class Roo.bootstrap.layout.Manager
37003  * @extends Roo.bootstrap.Component
37004  * Base class for layout managers.
37005  */
37006 Roo.bootstrap.layout.Manager = function(config)
37007 {
37008     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37009
37010
37011
37012
37013
37014     /** false to disable window resize monitoring @type Boolean */
37015     this.monitorWindowResize = true;
37016     this.regions = {};
37017     this.addEvents({
37018         /**
37019          * @event layout
37020          * Fires when a layout is performed.
37021          * @param {Roo.LayoutManager} this
37022          */
37023         "layout" : true,
37024         /**
37025          * @event regionresized
37026          * Fires when the user resizes a region.
37027          * @param {Roo.LayoutRegion} region The resized region
37028          * @param {Number} newSize The new size (width for east/west, height for north/south)
37029          */
37030         "regionresized" : true,
37031         /**
37032          * @event regioncollapsed
37033          * Fires when a region is collapsed.
37034          * @param {Roo.LayoutRegion} region The collapsed region
37035          */
37036         "regioncollapsed" : true,
37037         /**
37038          * @event regionexpanded
37039          * Fires when a region is expanded.
37040          * @param {Roo.LayoutRegion} region The expanded region
37041          */
37042         "regionexpanded" : true
37043     });
37044     this.updating = false;
37045
37046     if (config.el) {
37047         this.el = Roo.get(config.el);
37048         this.initEvents();
37049     }
37050
37051 };
37052
37053 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37054
37055
37056     regions : null,
37057
37058     monitorWindowResize : true,
37059
37060
37061     updating : false,
37062
37063
37064     onRender : function(ct, position)
37065     {
37066         if(!this.el){
37067             this.el = Roo.get(ct);
37068             this.initEvents();
37069         }
37070         //this.fireEvent('render',this);
37071     },
37072
37073
37074     initEvents: function()
37075     {
37076
37077
37078         // ie scrollbar fix
37079         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37080             document.body.scroll = "no";
37081         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37082             this.el.position('relative');
37083         }
37084         this.id = this.el.id;
37085         this.el.addClass("roo-layout-container");
37086         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37087         if(this.el.dom != document.body ) {
37088             this.el.on('resize', this.layout,this);
37089             this.el.on('show', this.layout,this);
37090         }
37091
37092     },
37093
37094     /**
37095      * Returns true if this layout is currently being updated
37096      * @return {Boolean}
37097      */
37098     isUpdating : function(){
37099         return this.updating;
37100     },
37101
37102     /**
37103      * Suspend the LayoutManager from doing auto-layouts while
37104      * making multiple add or remove calls
37105      */
37106     beginUpdate : function(){
37107         this.updating = true;
37108     },
37109
37110     /**
37111      * Restore auto-layouts and optionally disable the manager from performing a layout
37112      * @param {Boolean} noLayout true to disable a layout update
37113      */
37114     endUpdate : function(noLayout){
37115         this.updating = false;
37116         if(!noLayout){
37117             this.layout();
37118         }
37119     },
37120
37121     layout: function(){
37122         // abstract...
37123     },
37124
37125     onRegionResized : function(region, newSize){
37126         this.fireEvent("regionresized", region, newSize);
37127         this.layout();
37128     },
37129
37130     onRegionCollapsed : function(region){
37131         this.fireEvent("regioncollapsed", region);
37132     },
37133
37134     onRegionExpanded : function(region){
37135         this.fireEvent("regionexpanded", region);
37136     },
37137
37138     /**
37139      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37140      * performs box-model adjustments.
37141      * @return {Object} The size as an object {width: (the width), height: (the height)}
37142      */
37143     getViewSize : function()
37144     {
37145         var size;
37146         if(this.el.dom != document.body){
37147             size = this.el.getSize();
37148         }else{
37149             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37150         }
37151         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37152         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37153         return size;
37154     },
37155
37156     /**
37157      * Returns the Element this layout is bound to.
37158      * @return {Roo.Element}
37159      */
37160     getEl : function(){
37161         return this.el;
37162     },
37163
37164     /**
37165      * Returns the specified region.
37166      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37167      * @return {Roo.LayoutRegion}
37168      */
37169     getRegion : function(target){
37170         return this.regions[target.toLowerCase()];
37171     },
37172
37173     onWindowResize : function(){
37174         if(this.monitorWindowResize){
37175             this.layout();
37176         }
37177     }
37178 });
37179 /*
37180  * Based on:
37181  * Ext JS Library 1.1.1
37182  * Copyright(c) 2006-2007, Ext JS, LLC.
37183  *
37184  * Originally Released Under LGPL - original licence link has changed is not relivant.
37185  *
37186  * Fork - LGPL
37187  * <script type="text/javascript">
37188  */
37189 /**
37190  * @class Roo.bootstrap.layout.Border
37191  * @extends Roo.bootstrap.layout.Manager
37192  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37193  * please see: examples/bootstrap/nested.html<br><br>
37194  
37195 <b>The container the layout is rendered into can be either the body element or any other element.
37196 If it is not the body element, the container needs to either be an absolute positioned element,
37197 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37198 the container size if it is not the body element.</b>
37199
37200 * @constructor
37201 * Create a new Border
37202 * @param {Object} config Configuration options
37203  */
37204 Roo.bootstrap.layout.Border = function(config){
37205     config = config || {};
37206     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37207     
37208     
37209     
37210     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37211         if(config[region]){
37212             config[region].region = region;
37213             this.addRegion(config[region]);
37214         }
37215     },this);
37216     
37217 };
37218
37219 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37220
37221 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37222     
37223     parent : false, // this might point to a 'nest' or a ???
37224     
37225     /**
37226      * Creates and adds a new region if it doesn't already exist.
37227      * @param {String} target The target region key (north, south, east, west or center).
37228      * @param {Object} config The regions config object
37229      * @return {BorderLayoutRegion} The new region
37230      */
37231     addRegion : function(config)
37232     {
37233         if(!this.regions[config.region]){
37234             var r = this.factory(config);
37235             this.bindRegion(r);
37236         }
37237         return this.regions[config.region];
37238     },
37239
37240     // private (kinda)
37241     bindRegion : function(r){
37242         this.regions[r.config.region] = r;
37243         
37244         r.on("visibilitychange",    this.layout, this);
37245         r.on("paneladded",          this.layout, this);
37246         r.on("panelremoved",        this.layout, this);
37247         r.on("invalidated",         this.layout, this);
37248         r.on("resized",             this.onRegionResized, this);
37249         r.on("collapsed",           this.onRegionCollapsed, this);
37250         r.on("expanded",            this.onRegionExpanded, this);
37251     },
37252
37253     /**
37254      * Performs a layout update.
37255      */
37256     layout : function()
37257     {
37258         if(this.updating) {
37259             return;
37260         }
37261         
37262         // render all the rebions if they have not been done alreayd?
37263         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37264             if(this.regions[region] && !this.regions[region].bodyEl){
37265                 this.regions[region].onRender(this.el)
37266             }
37267         },this);
37268         
37269         var size = this.getViewSize();
37270         var w = size.width;
37271         var h = size.height;
37272         var centerW = w;
37273         var centerH = h;
37274         var centerY = 0;
37275         var centerX = 0;
37276         //var x = 0, y = 0;
37277
37278         var rs = this.regions;
37279         var north = rs["north"];
37280         var south = rs["south"]; 
37281         var west = rs["west"];
37282         var east = rs["east"];
37283         var center = rs["center"];
37284         //if(this.hideOnLayout){ // not supported anymore
37285             //c.el.setStyle("display", "none");
37286         //}
37287         if(north && north.isVisible()){
37288             var b = north.getBox();
37289             var m = north.getMargins();
37290             b.width = w - (m.left+m.right);
37291             b.x = m.left;
37292             b.y = m.top;
37293             centerY = b.height + b.y + m.bottom;
37294             centerH -= centerY;
37295             north.updateBox(this.safeBox(b));
37296         }
37297         if(south && south.isVisible()){
37298             var b = south.getBox();
37299             var m = south.getMargins();
37300             b.width = w - (m.left+m.right);
37301             b.x = m.left;
37302             var totalHeight = (b.height + m.top + m.bottom);
37303             b.y = h - totalHeight + m.top;
37304             centerH -= totalHeight;
37305             south.updateBox(this.safeBox(b));
37306         }
37307         if(west && west.isVisible()){
37308             var b = west.getBox();
37309             var m = west.getMargins();
37310             b.height = centerH - (m.top+m.bottom);
37311             b.x = m.left;
37312             b.y = centerY + m.top;
37313             var totalWidth = (b.width + m.left + m.right);
37314             centerX += totalWidth;
37315             centerW -= totalWidth;
37316             west.updateBox(this.safeBox(b));
37317         }
37318         if(east && east.isVisible()){
37319             var b = east.getBox();
37320             var m = east.getMargins();
37321             b.height = centerH - (m.top+m.bottom);
37322             var totalWidth = (b.width + m.left + m.right);
37323             b.x = w - totalWidth + m.left;
37324             b.y = centerY + m.top;
37325             centerW -= totalWidth;
37326             east.updateBox(this.safeBox(b));
37327         }
37328         if(center){
37329             var m = center.getMargins();
37330             var centerBox = {
37331                 x: centerX + m.left,
37332                 y: centerY + m.top,
37333                 width: centerW - (m.left+m.right),
37334                 height: centerH - (m.top+m.bottom)
37335             };
37336             //if(this.hideOnLayout){
37337                 //center.el.setStyle("display", "block");
37338             //}
37339             center.updateBox(this.safeBox(centerBox));
37340         }
37341         this.el.repaint();
37342         this.fireEvent("layout", this);
37343     },
37344
37345     // private
37346     safeBox : function(box){
37347         box.width = Math.max(0, box.width);
37348         box.height = Math.max(0, box.height);
37349         return box;
37350     },
37351
37352     /**
37353      * Adds a ContentPanel (or subclass) to this layout.
37354      * @param {String} target The target region key (north, south, east, west or center).
37355      * @param {Roo.ContentPanel} panel The panel to add
37356      * @return {Roo.ContentPanel} The added panel
37357      */
37358     add : function(target, panel){
37359          
37360         target = target.toLowerCase();
37361         return this.regions[target].add(panel);
37362     },
37363
37364     /**
37365      * Remove a ContentPanel (or subclass) to this layout.
37366      * @param {String} target The target region key (north, south, east, west or center).
37367      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37368      * @return {Roo.ContentPanel} The removed panel
37369      */
37370     remove : function(target, panel){
37371         target = target.toLowerCase();
37372         return this.regions[target].remove(panel);
37373     },
37374
37375     /**
37376      * Searches all regions for a panel with the specified id
37377      * @param {String} panelId
37378      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37379      */
37380     findPanel : function(panelId){
37381         var rs = this.regions;
37382         for(var target in rs){
37383             if(typeof rs[target] != "function"){
37384                 var p = rs[target].getPanel(panelId);
37385                 if(p){
37386                     return p;
37387                 }
37388             }
37389         }
37390         return null;
37391     },
37392
37393     /**
37394      * Searches all regions for a panel with the specified id and activates (shows) it.
37395      * @param {String/ContentPanel} panelId The panels id or the panel itself
37396      * @return {Roo.ContentPanel} The shown panel or null
37397      */
37398     showPanel : function(panelId) {
37399       var rs = this.regions;
37400       for(var target in rs){
37401          var r = rs[target];
37402          if(typeof r != "function"){
37403             if(r.hasPanel(panelId)){
37404                return r.showPanel(panelId);
37405             }
37406          }
37407       }
37408       return null;
37409    },
37410
37411    /**
37412      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37413      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37414      */
37415    /*
37416     restoreState : function(provider){
37417         if(!provider){
37418             provider = Roo.state.Manager;
37419         }
37420         var sm = new Roo.LayoutStateManager();
37421         sm.init(this, provider);
37422     },
37423 */
37424  
37425  
37426     /**
37427      * Adds a xtype elements to the layout.
37428      * <pre><code>
37429
37430 layout.addxtype({
37431        xtype : 'ContentPanel',
37432        region: 'west',
37433        items: [ .... ]
37434    }
37435 );
37436
37437 layout.addxtype({
37438         xtype : 'NestedLayoutPanel',
37439         region: 'west',
37440         layout: {
37441            center: { },
37442            west: { }   
37443         },
37444         items : [ ... list of content panels or nested layout panels.. ]
37445    }
37446 );
37447 </code></pre>
37448      * @param {Object} cfg Xtype definition of item to add.
37449      */
37450     addxtype : function(cfg)
37451     {
37452         // basically accepts a pannel...
37453         // can accept a layout region..!?!?
37454         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37455         
37456         
37457         // theory?  children can only be panels??
37458         
37459         //if (!cfg.xtype.match(/Panel$/)) {
37460         //    return false;
37461         //}
37462         var ret = false;
37463         
37464         if (typeof(cfg.region) == 'undefined') {
37465             Roo.log("Failed to add Panel, region was not set");
37466             Roo.log(cfg);
37467             return false;
37468         }
37469         var region = cfg.region;
37470         delete cfg.region;
37471         
37472           
37473         var xitems = [];
37474         if (cfg.items) {
37475             xitems = cfg.items;
37476             delete cfg.items;
37477         }
37478         var nb = false;
37479         
37480         if ( region == 'center') {
37481             Roo.log("Center: " + cfg.title);
37482         }
37483         
37484         
37485         switch(cfg.xtype) 
37486         {
37487             case 'Content':  // ContentPanel (el, cfg)
37488             case 'Scroll':  // ContentPanel (el, cfg)
37489             case 'View': 
37490                 cfg.autoCreate = cfg.autoCreate || true;
37491                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37492                 //} else {
37493                 //    var el = this.el.createChild();
37494                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37495                 //}
37496                 
37497                 this.add(region, ret);
37498                 break;
37499             
37500             /*
37501             case 'TreePanel': // our new panel!
37502                 cfg.el = this.el.createChild();
37503                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37504                 this.add(region, ret);
37505                 break;
37506             */
37507             
37508             case 'Nest': 
37509                 // create a new Layout (which is  a Border Layout...
37510                 
37511                 var clayout = cfg.layout;
37512                 clayout.el  = this.el.createChild();
37513                 clayout.items   = clayout.items  || [];
37514                 
37515                 delete cfg.layout;
37516                 
37517                 // replace this exitems with the clayout ones..
37518                 xitems = clayout.items;
37519                  
37520                 // force background off if it's in center...
37521                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37522                     cfg.background = false;
37523                 }
37524                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37525                 
37526                 
37527                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37528                 //console.log('adding nested layout panel '  + cfg.toSource());
37529                 this.add(region, ret);
37530                 nb = {}; /// find first...
37531                 break;
37532             
37533             case 'Grid':
37534                 
37535                 // needs grid and region
37536                 
37537                 //var el = this.getRegion(region).el.createChild();
37538                 /*
37539                  *var el = this.el.createChild();
37540                 // create the grid first...
37541                 cfg.grid.container = el;
37542                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37543                 */
37544                 
37545                 if (region == 'center' && this.active ) {
37546                     cfg.background = false;
37547                 }
37548                 
37549                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37550                 
37551                 this.add(region, ret);
37552                 /*
37553                 if (cfg.background) {
37554                     // render grid on panel activation (if panel background)
37555                     ret.on('activate', function(gp) {
37556                         if (!gp.grid.rendered) {
37557                     //        gp.grid.render(el);
37558                         }
37559                     });
37560                 } else {
37561                   //  cfg.grid.render(el);
37562                 }
37563                 */
37564                 break;
37565            
37566            
37567             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37568                 // it was the old xcomponent building that caused this before.
37569                 // espeically if border is the top element in the tree.
37570                 ret = this;
37571                 break; 
37572                 
37573                     
37574                 
37575                 
37576                 
37577             default:
37578                 /*
37579                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37580                     
37581                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37582                     this.add(region, ret);
37583                 } else {
37584                 */
37585                     Roo.log(cfg);
37586                     throw "Can not add '" + cfg.xtype + "' to Border";
37587                     return null;
37588              
37589                                 
37590              
37591         }
37592         this.beginUpdate();
37593         // add children..
37594         var region = '';
37595         var abn = {};
37596         Roo.each(xitems, function(i)  {
37597             region = nb && i.region ? i.region : false;
37598             
37599             var add = ret.addxtype(i);
37600            
37601             if (region) {
37602                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37603                 if (!i.background) {
37604                     abn[region] = nb[region] ;
37605                 }
37606             }
37607             
37608         });
37609         this.endUpdate();
37610
37611         // make the last non-background panel active..
37612         //if (nb) { Roo.log(abn); }
37613         if (nb) {
37614             
37615             for(var r in abn) {
37616                 region = this.getRegion(r);
37617                 if (region) {
37618                     // tried using nb[r], but it does not work..
37619                      
37620                     region.showPanel(abn[r]);
37621                    
37622                 }
37623             }
37624         }
37625         return ret;
37626         
37627     },
37628     
37629     
37630 // private
37631     factory : function(cfg)
37632     {
37633         
37634         var validRegions = Roo.bootstrap.layout.Border.regions;
37635
37636         var target = cfg.region;
37637         cfg.mgr = this;
37638         
37639         var r = Roo.bootstrap.layout;
37640         Roo.log(target);
37641         switch(target){
37642             case "north":
37643                 return new r.North(cfg);
37644             case "south":
37645                 return new r.South(cfg);
37646             case "east":
37647                 return new r.East(cfg);
37648             case "west":
37649                 return new r.West(cfg);
37650             case "center":
37651                 return new r.Center(cfg);
37652         }
37653         throw 'Layout region "'+target+'" not supported.';
37654     }
37655     
37656     
37657 });
37658  /*
37659  * Based on:
37660  * Ext JS Library 1.1.1
37661  * Copyright(c) 2006-2007, Ext JS, LLC.
37662  *
37663  * Originally Released Under LGPL - original licence link has changed is not relivant.
37664  *
37665  * Fork - LGPL
37666  * <script type="text/javascript">
37667  */
37668  
37669 /**
37670  * @class Roo.bootstrap.layout.Basic
37671  * @extends Roo.util.Observable
37672  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37673  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37674  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37675  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37676  * @cfg {string}   region  the region that it inhabits..
37677  * @cfg {bool}   skipConfig skip config?
37678  * 
37679
37680  */
37681 Roo.bootstrap.layout.Basic = function(config){
37682     
37683     this.mgr = config.mgr;
37684     
37685     this.position = config.region;
37686     
37687     var skipConfig = config.skipConfig;
37688     
37689     this.events = {
37690         /**
37691          * @scope Roo.BasicLayoutRegion
37692          */
37693         
37694         /**
37695          * @event beforeremove
37696          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37697          * @param {Roo.LayoutRegion} this
37698          * @param {Roo.ContentPanel} panel The panel
37699          * @param {Object} e The cancel event object
37700          */
37701         "beforeremove" : true,
37702         /**
37703          * @event invalidated
37704          * Fires when the layout for this region is changed.
37705          * @param {Roo.LayoutRegion} this
37706          */
37707         "invalidated" : true,
37708         /**
37709          * @event visibilitychange
37710          * Fires when this region is shown or hidden 
37711          * @param {Roo.LayoutRegion} this
37712          * @param {Boolean} visibility true or false
37713          */
37714         "visibilitychange" : true,
37715         /**
37716          * @event paneladded
37717          * Fires when a panel is added. 
37718          * @param {Roo.LayoutRegion} this
37719          * @param {Roo.ContentPanel} panel The panel
37720          */
37721         "paneladded" : true,
37722         /**
37723          * @event panelremoved
37724          * Fires when a panel is removed. 
37725          * @param {Roo.LayoutRegion} this
37726          * @param {Roo.ContentPanel} panel The panel
37727          */
37728         "panelremoved" : true,
37729         /**
37730          * @event beforecollapse
37731          * Fires when this region before collapse.
37732          * @param {Roo.LayoutRegion} this
37733          */
37734         "beforecollapse" : true,
37735         /**
37736          * @event collapsed
37737          * Fires when this region is collapsed.
37738          * @param {Roo.LayoutRegion} this
37739          */
37740         "collapsed" : true,
37741         /**
37742          * @event expanded
37743          * Fires when this region is expanded.
37744          * @param {Roo.LayoutRegion} this
37745          */
37746         "expanded" : true,
37747         /**
37748          * @event slideshow
37749          * Fires when this region is slid into view.
37750          * @param {Roo.LayoutRegion} this
37751          */
37752         "slideshow" : true,
37753         /**
37754          * @event slidehide
37755          * Fires when this region slides out of view. 
37756          * @param {Roo.LayoutRegion} this
37757          */
37758         "slidehide" : true,
37759         /**
37760          * @event panelactivated
37761          * Fires when a panel is activated. 
37762          * @param {Roo.LayoutRegion} this
37763          * @param {Roo.ContentPanel} panel The activated panel
37764          */
37765         "panelactivated" : true,
37766         /**
37767          * @event resized
37768          * Fires when the user resizes this region. 
37769          * @param {Roo.LayoutRegion} this
37770          * @param {Number} newSize The new size (width for east/west, height for north/south)
37771          */
37772         "resized" : true
37773     };
37774     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37775     this.panels = new Roo.util.MixedCollection();
37776     this.panels.getKey = this.getPanelId.createDelegate(this);
37777     this.box = null;
37778     this.activePanel = null;
37779     // ensure listeners are added...
37780     
37781     if (config.listeners || config.events) {
37782         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37783             listeners : config.listeners || {},
37784             events : config.events || {}
37785         });
37786     }
37787     
37788     if(skipConfig !== true){
37789         this.applyConfig(config);
37790     }
37791 };
37792
37793 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37794 {
37795     getPanelId : function(p){
37796         return p.getId();
37797     },
37798     
37799     applyConfig : function(config){
37800         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37801         this.config = config;
37802         
37803     },
37804     
37805     /**
37806      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37807      * the width, for horizontal (north, south) the height.
37808      * @param {Number} newSize The new width or height
37809      */
37810     resizeTo : function(newSize){
37811         var el = this.el ? this.el :
37812                  (this.activePanel ? this.activePanel.getEl() : null);
37813         if(el){
37814             switch(this.position){
37815                 case "east":
37816                 case "west":
37817                     el.setWidth(newSize);
37818                     this.fireEvent("resized", this, newSize);
37819                 break;
37820                 case "north":
37821                 case "south":
37822                     el.setHeight(newSize);
37823                     this.fireEvent("resized", this, newSize);
37824                 break;                
37825             }
37826         }
37827     },
37828     
37829     getBox : function(){
37830         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37831     },
37832     
37833     getMargins : function(){
37834         return this.margins;
37835     },
37836     
37837     updateBox : function(box){
37838         this.box = box;
37839         var el = this.activePanel.getEl();
37840         el.dom.style.left = box.x + "px";
37841         el.dom.style.top = box.y + "px";
37842         this.activePanel.setSize(box.width, box.height);
37843     },
37844     
37845     /**
37846      * Returns the container element for this region.
37847      * @return {Roo.Element}
37848      */
37849     getEl : function(){
37850         return this.activePanel;
37851     },
37852     
37853     /**
37854      * Returns true if this region is currently visible.
37855      * @return {Boolean}
37856      */
37857     isVisible : function(){
37858         return this.activePanel ? true : false;
37859     },
37860     
37861     setActivePanel : function(panel){
37862         panel = this.getPanel(panel);
37863         if(this.activePanel && this.activePanel != panel){
37864             this.activePanel.setActiveState(false);
37865             this.activePanel.getEl().setLeftTop(-10000,-10000);
37866         }
37867         this.activePanel = panel;
37868         panel.setActiveState(true);
37869         if(this.box){
37870             panel.setSize(this.box.width, this.box.height);
37871         }
37872         this.fireEvent("panelactivated", this, panel);
37873         this.fireEvent("invalidated");
37874     },
37875     
37876     /**
37877      * Show the specified panel.
37878      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37879      * @return {Roo.ContentPanel} The shown panel or null
37880      */
37881     showPanel : function(panel){
37882         panel = this.getPanel(panel);
37883         if(panel){
37884             this.setActivePanel(panel);
37885         }
37886         return panel;
37887     },
37888     
37889     /**
37890      * Get the active panel for this region.
37891      * @return {Roo.ContentPanel} The active panel or null
37892      */
37893     getActivePanel : function(){
37894         return this.activePanel;
37895     },
37896     
37897     /**
37898      * Add the passed ContentPanel(s)
37899      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37900      * @return {Roo.ContentPanel} The panel added (if only one was added)
37901      */
37902     add : function(panel){
37903         if(arguments.length > 1){
37904             for(var i = 0, len = arguments.length; i < len; i++) {
37905                 this.add(arguments[i]);
37906             }
37907             return null;
37908         }
37909         if(this.hasPanel(panel)){
37910             this.showPanel(panel);
37911             return panel;
37912         }
37913         var el = panel.getEl();
37914         if(el.dom.parentNode != this.mgr.el.dom){
37915             this.mgr.el.dom.appendChild(el.dom);
37916         }
37917         if(panel.setRegion){
37918             panel.setRegion(this);
37919         }
37920         this.panels.add(panel);
37921         el.setStyle("position", "absolute");
37922         if(!panel.background){
37923             this.setActivePanel(panel);
37924             if(this.config.initialSize && this.panels.getCount()==1){
37925                 this.resizeTo(this.config.initialSize);
37926             }
37927         }
37928         this.fireEvent("paneladded", this, panel);
37929         return panel;
37930     },
37931     
37932     /**
37933      * Returns true if the panel is in this region.
37934      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37935      * @return {Boolean}
37936      */
37937     hasPanel : function(panel){
37938         if(typeof panel == "object"){ // must be panel obj
37939             panel = panel.getId();
37940         }
37941         return this.getPanel(panel) ? true : false;
37942     },
37943     
37944     /**
37945      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37946      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37947      * @param {Boolean} preservePanel Overrides the config preservePanel option
37948      * @return {Roo.ContentPanel} The panel that was removed
37949      */
37950     remove : function(panel, preservePanel){
37951         panel = this.getPanel(panel);
37952         if(!panel){
37953             return null;
37954         }
37955         var e = {};
37956         this.fireEvent("beforeremove", this, panel, e);
37957         if(e.cancel === true){
37958             return null;
37959         }
37960         var panelId = panel.getId();
37961         this.panels.removeKey(panelId);
37962         return panel;
37963     },
37964     
37965     /**
37966      * Returns the panel specified or null if it's not in this region.
37967      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37968      * @return {Roo.ContentPanel}
37969      */
37970     getPanel : function(id){
37971         if(typeof id == "object"){ // must be panel obj
37972             return id;
37973         }
37974         return this.panels.get(id);
37975     },
37976     
37977     /**
37978      * Returns this regions position (north/south/east/west/center).
37979      * @return {String} 
37980      */
37981     getPosition: function(){
37982         return this.position;    
37983     }
37984 });/*
37985  * Based on:
37986  * Ext JS Library 1.1.1
37987  * Copyright(c) 2006-2007, Ext JS, LLC.
37988  *
37989  * Originally Released Under LGPL - original licence link has changed is not relivant.
37990  *
37991  * Fork - LGPL
37992  * <script type="text/javascript">
37993  */
37994  
37995 /**
37996  * @class Roo.bootstrap.layout.Region
37997  * @extends Roo.bootstrap.layout.Basic
37998  * This class represents a region in a layout manager.
37999  
38000  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38001  * @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})
38002  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38003  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38004  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38005  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38006  * @cfg {String}    title           The title for the region (overrides panel titles)
38007  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38008  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38009  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38010  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38011  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38012  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38013  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38014  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38015  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38016  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38017
38018  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38019  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38020  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38021  * @cfg {Number}    width           For East/West panels
38022  * @cfg {Number}    height          For North/South panels
38023  * @cfg {Boolean}   split           To show the splitter
38024  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38025  * 
38026  * @cfg {string}   cls             Extra CSS classes to add to region
38027  * 
38028  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38029  * @cfg {string}   region  the region that it inhabits..
38030  *
38031
38032  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38033  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38034
38035  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38036  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38037  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38038  */
38039 Roo.bootstrap.layout.Region = function(config)
38040 {
38041     this.applyConfig(config);
38042
38043     var mgr = config.mgr;
38044     var pos = config.region;
38045     config.skipConfig = true;
38046     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38047     
38048     if (mgr.el) {
38049         this.onRender(mgr.el);   
38050     }
38051      
38052     this.visible = true;
38053     this.collapsed = false;
38054     this.unrendered_panels = [];
38055 };
38056
38057 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38058
38059     position: '', // set by wrapper (eg. north/south etc..)
38060     unrendered_panels : null,  // unrendered panels.
38061     
38062     tabPosition : false,
38063     
38064     mgr: false, // points to 'Border'
38065     
38066     
38067     createBody : function(){
38068         /** This region's body element 
38069         * @type Roo.Element */
38070         this.bodyEl = this.el.createChild({
38071                 tag: "div",
38072                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38073         });
38074     },
38075
38076     onRender: function(ctr, pos)
38077     {
38078         var dh = Roo.DomHelper;
38079         /** This region's container element 
38080         * @type Roo.Element */
38081         this.el = dh.append(ctr.dom, {
38082                 tag: "div",
38083                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38084             }, true);
38085         /** This region's title element 
38086         * @type Roo.Element */
38087     
38088         this.titleEl = dh.append(this.el.dom,  {
38089                 tag: "div",
38090                 unselectable: "on",
38091                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38092                 children:[
38093                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38094                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38095                 ]
38096             }, true);
38097         
38098         this.titleEl.enableDisplayMode();
38099         /** This region's title text element 
38100         * @type HTMLElement */
38101         this.titleTextEl = this.titleEl.dom.firstChild;
38102         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38103         /*
38104         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38105         this.closeBtn.enableDisplayMode();
38106         this.closeBtn.on("click", this.closeClicked, this);
38107         this.closeBtn.hide();
38108     */
38109         this.createBody(this.config);
38110         if(this.config.hideWhenEmpty){
38111             this.hide();
38112             this.on("paneladded", this.validateVisibility, this);
38113             this.on("panelremoved", this.validateVisibility, this);
38114         }
38115         if(this.autoScroll){
38116             this.bodyEl.setStyle("overflow", "auto");
38117         }else{
38118             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38119         }
38120         //if(c.titlebar !== false){
38121             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38122                 this.titleEl.hide();
38123             }else{
38124                 this.titleEl.show();
38125                 if(this.config.title){
38126                     this.titleTextEl.innerHTML = this.config.title;
38127                 }
38128             }
38129         //}
38130         if(this.config.collapsed){
38131             this.collapse(true);
38132         }
38133         if(this.config.hidden){
38134             this.hide();
38135         }
38136         
38137         if (this.unrendered_panels && this.unrendered_panels.length) {
38138             for (var i =0;i< this.unrendered_panels.length; i++) {
38139                 this.add(this.unrendered_panels[i]);
38140             }
38141             this.unrendered_panels = null;
38142             
38143         }
38144         
38145     },
38146     
38147     applyConfig : function(c)
38148     {
38149         /*
38150          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38151             var dh = Roo.DomHelper;
38152             if(c.titlebar !== false){
38153                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38154                 this.collapseBtn.on("click", this.collapse, this);
38155                 this.collapseBtn.enableDisplayMode();
38156                 /*
38157                 if(c.showPin === true || this.showPin){
38158                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38159                     this.stickBtn.enableDisplayMode();
38160                     this.stickBtn.on("click", this.expand, this);
38161                     this.stickBtn.hide();
38162                 }
38163                 
38164             }
38165             */
38166             /** This region's collapsed element
38167             * @type Roo.Element */
38168             /*
38169              *
38170             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38171                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38172             ]}, true);
38173             
38174             if(c.floatable !== false){
38175                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38176                this.collapsedEl.on("click", this.collapseClick, this);
38177             }
38178
38179             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38180                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38181                    id: "message", unselectable: "on", style:{"float":"left"}});
38182                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38183              }
38184             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38185             this.expandBtn.on("click", this.expand, this);
38186             
38187         }
38188         
38189         if(this.collapseBtn){
38190             this.collapseBtn.setVisible(c.collapsible == true);
38191         }
38192         
38193         this.cmargins = c.cmargins || this.cmargins ||
38194                          (this.position == "west" || this.position == "east" ?
38195                              {top: 0, left: 2, right:2, bottom: 0} :
38196                              {top: 2, left: 0, right:0, bottom: 2});
38197         */
38198         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38199         
38200         
38201         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38202         
38203         this.autoScroll = c.autoScroll || false;
38204         
38205         
38206        
38207         
38208         this.duration = c.duration || .30;
38209         this.slideDuration = c.slideDuration || .45;
38210         this.config = c;
38211        
38212     },
38213     /**
38214      * Returns true if this region is currently visible.
38215      * @return {Boolean}
38216      */
38217     isVisible : function(){
38218         return this.visible;
38219     },
38220
38221     /**
38222      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38223      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38224      */
38225     //setCollapsedTitle : function(title){
38226     //    title = title || "&#160;";
38227      //   if(this.collapsedTitleTextEl){
38228       //      this.collapsedTitleTextEl.innerHTML = title;
38229        // }
38230     //},
38231
38232     getBox : function(){
38233         var b;
38234       //  if(!this.collapsed){
38235             b = this.el.getBox(false, true);
38236        // }else{
38237           //  b = this.collapsedEl.getBox(false, true);
38238         //}
38239         return b;
38240     },
38241
38242     getMargins : function(){
38243         return this.margins;
38244         //return this.collapsed ? this.cmargins : this.margins;
38245     },
38246 /*
38247     highlight : function(){
38248         this.el.addClass("x-layout-panel-dragover");
38249     },
38250
38251     unhighlight : function(){
38252         this.el.removeClass("x-layout-panel-dragover");
38253     },
38254 */
38255     updateBox : function(box)
38256     {
38257         if (!this.bodyEl) {
38258             return; // not rendered yet..
38259         }
38260         
38261         this.box = box;
38262         if(!this.collapsed){
38263             this.el.dom.style.left = box.x + "px";
38264             this.el.dom.style.top = box.y + "px";
38265             this.updateBody(box.width, box.height);
38266         }else{
38267             this.collapsedEl.dom.style.left = box.x + "px";
38268             this.collapsedEl.dom.style.top = box.y + "px";
38269             this.collapsedEl.setSize(box.width, box.height);
38270         }
38271         if(this.tabs){
38272             this.tabs.autoSizeTabs();
38273         }
38274     },
38275
38276     updateBody : function(w, h)
38277     {
38278         if(w !== null){
38279             this.el.setWidth(w);
38280             w -= this.el.getBorderWidth("rl");
38281             if(this.config.adjustments){
38282                 w += this.config.adjustments[0];
38283             }
38284         }
38285         if(h !== null && h > 0){
38286             this.el.setHeight(h);
38287             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38288             h -= this.el.getBorderWidth("tb");
38289             if(this.config.adjustments){
38290                 h += this.config.adjustments[1];
38291             }
38292             this.bodyEl.setHeight(h);
38293             if(this.tabs){
38294                 h = this.tabs.syncHeight(h);
38295             }
38296         }
38297         if(this.panelSize){
38298             w = w !== null ? w : this.panelSize.width;
38299             h = h !== null ? h : this.panelSize.height;
38300         }
38301         if(this.activePanel){
38302             var el = this.activePanel.getEl();
38303             w = w !== null ? w : el.getWidth();
38304             h = h !== null ? h : el.getHeight();
38305             this.panelSize = {width: w, height: h};
38306             this.activePanel.setSize(w, h);
38307         }
38308         if(Roo.isIE && this.tabs){
38309             this.tabs.el.repaint();
38310         }
38311     },
38312
38313     /**
38314      * Returns the container element for this region.
38315      * @return {Roo.Element}
38316      */
38317     getEl : function(){
38318         return this.el;
38319     },
38320
38321     /**
38322      * Hides this region.
38323      */
38324     hide : function(){
38325         //if(!this.collapsed){
38326             this.el.dom.style.left = "-2000px";
38327             this.el.hide();
38328         //}else{
38329          //   this.collapsedEl.dom.style.left = "-2000px";
38330          //   this.collapsedEl.hide();
38331        // }
38332         this.visible = false;
38333         this.fireEvent("visibilitychange", this, false);
38334     },
38335
38336     /**
38337      * Shows this region if it was previously hidden.
38338      */
38339     show : function(){
38340         //if(!this.collapsed){
38341             this.el.show();
38342         //}else{
38343         //    this.collapsedEl.show();
38344        // }
38345         this.visible = true;
38346         this.fireEvent("visibilitychange", this, true);
38347     },
38348 /*
38349     closeClicked : function(){
38350         if(this.activePanel){
38351             this.remove(this.activePanel);
38352         }
38353     },
38354
38355     collapseClick : function(e){
38356         if(this.isSlid){
38357            e.stopPropagation();
38358            this.slideIn();
38359         }else{
38360            e.stopPropagation();
38361            this.slideOut();
38362         }
38363     },
38364 */
38365     /**
38366      * Collapses this region.
38367      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38368      */
38369     /*
38370     collapse : function(skipAnim, skipCheck = false){
38371         if(this.collapsed) {
38372             return;
38373         }
38374         
38375         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38376             
38377             this.collapsed = true;
38378             if(this.split){
38379                 this.split.el.hide();
38380             }
38381             if(this.config.animate && skipAnim !== true){
38382                 this.fireEvent("invalidated", this);
38383                 this.animateCollapse();
38384             }else{
38385                 this.el.setLocation(-20000,-20000);
38386                 this.el.hide();
38387                 this.collapsedEl.show();
38388                 this.fireEvent("collapsed", this);
38389                 this.fireEvent("invalidated", this);
38390             }
38391         }
38392         
38393     },
38394 */
38395     animateCollapse : function(){
38396         // overridden
38397     },
38398
38399     /**
38400      * Expands this region if it was previously collapsed.
38401      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38402      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38403      */
38404     /*
38405     expand : function(e, skipAnim){
38406         if(e) {
38407             e.stopPropagation();
38408         }
38409         if(!this.collapsed || this.el.hasActiveFx()) {
38410             return;
38411         }
38412         if(this.isSlid){
38413             this.afterSlideIn();
38414             skipAnim = true;
38415         }
38416         this.collapsed = false;
38417         if(this.config.animate && skipAnim !== true){
38418             this.animateExpand();
38419         }else{
38420             this.el.show();
38421             if(this.split){
38422                 this.split.el.show();
38423             }
38424             this.collapsedEl.setLocation(-2000,-2000);
38425             this.collapsedEl.hide();
38426             this.fireEvent("invalidated", this);
38427             this.fireEvent("expanded", this);
38428         }
38429     },
38430 */
38431     animateExpand : function(){
38432         // overridden
38433     },
38434
38435     initTabs : function()
38436     {
38437         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38438         
38439         var ts = new Roo.bootstrap.panel.Tabs({
38440             el: this.bodyEl.dom,
38441             region : this,
38442             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38443             disableTooltips: this.config.disableTabTips,
38444             toolbar : this.config.toolbar
38445         });
38446         
38447         if(this.config.hideTabs){
38448             ts.stripWrap.setDisplayed(false);
38449         }
38450         this.tabs = ts;
38451         ts.resizeTabs = this.config.resizeTabs === true;
38452         ts.minTabWidth = this.config.minTabWidth || 40;
38453         ts.maxTabWidth = this.config.maxTabWidth || 250;
38454         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38455         ts.monitorResize = false;
38456         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38457         ts.bodyEl.addClass('roo-layout-tabs-body');
38458         this.panels.each(this.initPanelAsTab, this);
38459     },
38460
38461     initPanelAsTab : function(panel){
38462         var ti = this.tabs.addTab(
38463             panel.getEl().id,
38464             panel.getTitle(),
38465             null,
38466             this.config.closeOnTab && panel.isClosable(),
38467             panel.tpl
38468         );
38469         if(panel.tabTip !== undefined){
38470             ti.setTooltip(panel.tabTip);
38471         }
38472         ti.on("activate", function(){
38473               this.setActivePanel(panel);
38474         }, this);
38475         
38476         if(this.config.closeOnTab){
38477             ti.on("beforeclose", function(t, e){
38478                 e.cancel = true;
38479                 this.remove(panel);
38480             }, this);
38481         }
38482         
38483         panel.tabItem = ti;
38484         
38485         return ti;
38486     },
38487
38488     updatePanelTitle : function(panel, title)
38489     {
38490         if(this.activePanel == panel){
38491             this.updateTitle(title);
38492         }
38493         if(this.tabs){
38494             var ti = this.tabs.getTab(panel.getEl().id);
38495             ti.setText(title);
38496             if(panel.tabTip !== undefined){
38497                 ti.setTooltip(panel.tabTip);
38498             }
38499         }
38500     },
38501
38502     updateTitle : function(title){
38503         if(this.titleTextEl && !this.config.title){
38504             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38505         }
38506     },
38507
38508     setActivePanel : function(panel)
38509     {
38510         panel = this.getPanel(panel);
38511         if(this.activePanel && this.activePanel != panel){
38512             if(this.activePanel.setActiveState(false) === false){
38513                 return;
38514             }
38515         }
38516         this.activePanel = panel;
38517         panel.setActiveState(true);
38518         if(this.panelSize){
38519             panel.setSize(this.panelSize.width, this.panelSize.height);
38520         }
38521         if(this.closeBtn){
38522             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38523         }
38524         this.updateTitle(panel.getTitle());
38525         if(this.tabs){
38526             this.fireEvent("invalidated", this);
38527         }
38528         this.fireEvent("panelactivated", this, panel);
38529     },
38530
38531     /**
38532      * Shows the specified panel.
38533      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38534      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38535      */
38536     showPanel : function(panel)
38537     {
38538         panel = this.getPanel(panel);
38539         if(panel){
38540             if(this.tabs){
38541                 var tab = this.tabs.getTab(panel.getEl().id);
38542                 if(tab.isHidden()){
38543                     this.tabs.unhideTab(tab.id);
38544                 }
38545                 tab.activate();
38546             }else{
38547                 this.setActivePanel(panel);
38548             }
38549         }
38550         return panel;
38551     },
38552
38553     /**
38554      * Get the active panel for this region.
38555      * @return {Roo.ContentPanel} The active panel or null
38556      */
38557     getActivePanel : function(){
38558         return this.activePanel;
38559     },
38560
38561     validateVisibility : function(){
38562         if(this.panels.getCount() < 1){
38563             this.updateTitle("&#160;");
38564             this.closeBtn.hide();
38565             this.hide();
38566         }else{
38567             if(!this.isVisible()){
38568                 this.show();
38569             }
38570         }
38571     },
38572
38573     /**
38574      * Adds the passed ContentPanel(s) to this region.
38575      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38576      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38577      */
38578     add : function(panel)
38579     {
38580         if(arguments.length > 1){
38581             for(var i = 0, len = arguments.length; i < len; i++) {
38582                 this.add(arguments[i]);
38583             }
38584             return null;
38585         }
38586         
38587         // if we have not been rendered yet, then we can not really do much of this..
38588         if (!this.bodyEl) {
38589             this.unrendered_panels.push(panel);
38590             return panel;
38591         }
38592         
38593         
38594         
38595         
38596         if(this.hasPanel(panel)){
38597             this.showPanel(panel);
38598             return panel;
38599         }
38600         panel.setRegion(this);
38601         this.panels.add(panel);
38602        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38603             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38604             // and hide them... ???
38605             this.bodyEl.dom.appendChild(panel.getEl().dom);
38606             if(panel.background !== true){
38607                 this.setActivePanel(panel);
38608             }
38609             this.fireEvent("paneladded", this, panel);
38610             return panel;
38611         }
38612         */
38613         if(!this.tabs){
38614             this.initTabs();
38615         }else{
38616             this.initPanelAsTab(panel);
38617         }
38618         
38619         
38620         if(panel.background !== true){
38621             this.tabs.activate(panel.getEl().id);
38622         }
38623         this.fireEvent("paneladded", this, panel);
38624         return panel;
38625     },
38626
38627     /**
38628      * Hides the tab for the specified panel.
38629      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38630      */
38631     hidePanel : function(panel){
38632         if(this.tabs && (panel = this.getPanel(panel))){
38633             this.tabs.hideTab(panel.getEl().id);
38634         }
38635     },
38636
38637     /**
38638      * Unhides the tab for a previously hidden panel.
38639      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38640      */
38641     unhidePanel : function(panel){
38642         if(this.tabs && (panel = this.getPanel(panel))){
38643             this.tabs.unhideTab(panel.getEl().id);
38644         }
38645     },
38646
38647     clearPanels : function(){
38648         while(this.panels.getCount() > 0){
38649              this.remove(this.panels.first());
38650         }
38651     },
38652
38653     /**
38654      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38655      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38656      * @param {Boolean} preservePanel Overrides the config preservePanel option
38657      * @return {Roo.ContentPanel} The panel that was removed
38658      */
38659     remove : function(panel, preservePanel)
38660     {
38661         panel = this.getPanel(panel);
38662         if(!panel){
38663             return null;
38664         }
38665         var e = {};
38666         this.fireEvent("beforeremove", this, panel, e);
38667         if(e.cancel === true){
38668             return null;
38669         }
38670         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38671         var panelId = panel.getId();
38672         this.panels.removeKey(panelId);
38673         if(preservePanel){
38674             document.body.appendChild(panel.getEl().dom);
38675         }
38676         if(this.tabs){
38677             this.tabs.removeTab(panel.getEl().id);
38678         }else if (!preservePanel){
38679             this.bodyEl.dom.removeChild(panel.getEl().dom);
38680         }
38681         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38682             var p = this.panels.first();
38683             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38684             tempEl.appendChild(p.getEl().dom);
38685             this.bodyEl.update("");
38686             this.bodyEl.dom.appendChild(p.getEl().dom);
38687             tempEl = null;
38688             this.updateTitle(p.getTitle());
38689             this.tabs = null;
38690             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38691             this.setActivePanel(p);
38692         }
38693         panel.setRegion(null);
38694         if(this.activePanel == panel){
38695             this.activePanel = null;
38696         }
38697         if(this.config.autoDestroy !== false && preservePanel !== true){
38698             try{panel.destroy();}catch(e){}
38699         }
38700         this.fireEvent("panelremoved", this, panel);
38701         return panel;
38702     },
38703
38704     /**
38705      * Returns the TabPanel component used by this region
38706      * @return {Roo.TabPanel}
38707      */
38708     getTabs : function(){
38709         return this.tabs;
38710     },
38711
38712     createTool : function(parentEl, className){
38713         var btn = Roo.DomHelper.append(parentEl, {
38714             tag: "div",
38715             cls: "x-layout-tools-button",
38716             children: [ {
38717                 tag: "div",
38718                 cls: "roo-layout-tools-button-inner " + className,
38719                 html: "&#160;"
38720             }]
38721         }, true);
38722         btn.addClassOnOver("roo-layout-tools-button-over");
38723         return btn;
38724     }
38725 });/*
38726  * Based on:
38727  * Ext JS Library 1.1.1
38728  * Copyright(c) 2006-2007, Ext JS, LLC.
38729  *
38730  * Originally Released Under LGPL - original licence link has changed is not relivant.
38731  *
38732  * Fork - LGPL
38733  * <script type="text/javascript">
38734  */
38735  
38736
38737
38738 /**
38739  * @class Roo.SplitLayoutRegion
38740  * @extends Roo.LayoutRegion
38741  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38742  */
38743 Roo.bootstrap.layout.Split = function(config){
38744     this.cursor = config.cursor;
38745     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38746 };
38747
38748 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38749 {
38750     splitTip : "Drag to resize.",
38751     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38752     useSplitTips : false,
38753
38754     applyConfig : function(config){
38755         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38756     },
38757     
38758     onRender : function(ctr,pos) {
38759         
38760         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38761         if(!this.config.split){
38762             return;
38763         }
38764         if(!this.split){
38765             
38766             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38767                             tag: "div",
38768                             id: this.el.id + "-split",
38769                             cls: "roo-layout-split roo-layout-split-"+this.position,
38770                             html: "&#160;"
38771             });
38772             /** The SplitBar for this region 
38773             * @type Roo.SplitBar */
38774             // does not exist yet...
38775             Roo.log([this.position, this.orientation]);
38776             
38777             this.split = new Roo.bootstrap.SplitBar({
38778                 dragElement : splitEl,
38779                 resizingElement: this.el,
38780                 orientation : this.orientation
38781             });
38782             
38783             this.split.on("moved", this.onSplitMove, this);
38784             this.split.useShim = this.config.useShim === true;
38785             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38786             if(this.useSplitTips){
38787                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38788             }
38789             //if(config.collapsible){
38790             //    this.split.el.on("dblclick", this.collapse,  this);
38791             //}
38792         }
38793         if(typeof this.config.minSize != "undefined"){
38794             this.split.minSize = this.config.minSize;
38795         }
38796         if(typeof this.config.maxSize != "undefined"){
38797             this.split.maxSize = this.config.maxSize;
38798         }
38799         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38800             this.hideSplitter();
38801         }
38802         
38803     },
38804
38805     getHMaxSize : function(){
38806          var cmax = this.config.maxSize || 10000;
38807          var center = this.mgr.getRegion("center");
38808          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38809     },
38810
38811     getVMaxSize : function(){
38812          var cmax = this.config.maxSize || 10000;
38813          var center = this.mgr.getRegion("center");
38814          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38815     },
38816
38817     onSplitMove : function(split, newSize){
38818         this.fireEvent("resized", this, newSize);
38819     },
38820     
38821     /** 
38822      * Returns the {@link Roo.SplitBar} for this region.
38823      * @return {Roo.SplitBar}
38824      */
38825     getSplitBar : function(){
38826         return this.split;
38827     },
38828     
38829     hide : function(){
38830         this.hideSplitter();
38831         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38832     },
38833
38834     hideSplitter : function(){
38835         if(this.split){
38836             this.split.el.setLocation(-2000,-2000);
38837             this.split.el.hide();
38838         }
38839     },
38840
38841     show : function(){
38842         if(this.split){
38843             this.split.el.show();
38844         }
38845         Roo.bootstrap.layout.Split.superclass.show.call(this);
38846     },
38847     
38848     beforeSlide: function(){
38849         if(Roo.isGecko){// firefox overflow auto bug workaround
38850             this.bodyEl.clip();
38851             if(this.tabs) {
38852                 this.tabs.bodyEl.clip();
38853             }
38854             if(this.activePanel){
38855                 this.activePanel.getEl().clip();
38856                 
38857                 if(this.activePanel.beforeSlide){
38858                     this.activePanel.beforeSlide();
38859                 }
38860             }
38861         }
38862     },
38863     
38864     afterSlide : function(){
38865         if(Roo.isGecko){// firefox overflow auto bug workaround
38866             this.bodyEl.unclip();
38867             if(this.tabs) {
38868                 this.tabs.bodyEl.unclip();
38869             }
38870             if(this.activePanel){
38871                 this.activePanel.getEl().unclip();
38872                 if(this.activePanel.afterSlide){
38873                     this.activePanel.afterSlide();
38874                 }
38875             }
38876         }
38877     },
38878
38879     initAutoHide : function(){
38880         if(this.autoHide !== false){
38881             if(!this.autoHideHd){
38882                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38883                 this.autoHideHd = {
38884                     "mouseout": function(e){
38885                         if(!e.within(this.el, true)){
38886                             st.delay(500);
38887                         }
38888                     },
38889                     "mouseover" : function(e){
38890                         st.cancel();
38891                     },
38892                     scope : this
38893                 };
38894             }
38895             this.el.on(this.autoHideHd);
38896         }
38897     },
38898
38899     clearAutoHide : function(){
38900         if(this.autoHide !== false){
38901             this.el.un("mouseout", this.autoHideHd.mouseout);
38902             this.el.un("mouseover", this.autoHideHd.mouseover);
38903         }
38904     },
38905
38906     clearMonitor : function(){
38907         Roo.get(document).un("click", this.slideInIf, this);
38908     },
38909
38910     // these names are backwards but not changed for compat
38911     slideOut : function(){
38912         if(this.isSlid || this.el.hasActiveFx()){
38913             return;
38914         }
38915         this.isSlid = true;
38916         if(this.collapseBtn){
38917             this.collapseBtn.hide();
38918         }
38919         this.closeBtnState = this.closeBtn.getStyle('display');
38920         this.closeBtn.hide();
38921         if(this.stickBtn){
38922             this.stickBtn.show();
38923         }
38924         this.el.show();
38925         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38926         this.beforeSlide();
38927         this.el.setStyle("z-index", 10001);
38928         this.el.slideIn(this.getSlideAnchor(), {
38929             callback: function(){
38930                 this.afterSlide();
38931                 this.initAutoHide();
38932                 Roo.get(document).on("click", this.slideInIf, this);
38933                 this.fireEvent("slideshow", this);
38934             },
38935             scope: this,
38936             block: true
38937         });
38938     },
38939
38940     afterSlideIn : function(){
38941         this.clearAutoHide();
38942         this.isSlid = false;
38943         this.clearMonitor();
38944         this.el.setStyle("z-index", "");
38945         if(this.collapseBtn){
38946             this.collapseBtn.show();
38947         }
38948         this.closeBtn.setStyle('display', this.closeBtnState);
38949         if(this.stickBtn){
38950             this.stickBtn.hide();
38951         }
38952         this.fireEvent("slidehide", this);
38953     },
38954
38955     slideIn : function(cb){
38956         if(!this.isSlid || this.el.hasActiveFx()){
38957             Roo.callback(cb);
38958             return;
38959         }
38960         this.isSlid = false;
38961         this.beforeSlide();
38962         this.el.slideOut(this.getSlideAnchor(), {
38963             callback: function(){
38964                 this.el.setLeftTop(-10000, -10000);
38965                 this.afterSlide();
38966                 this.afterSlideIn();
38967                 Roo.callback(cb);
38968             },
38969             scope: this,
38970             block: true
38971         });
38972     },
38973     
38974     slideInIf : function(e){
38975         if(!e.within(this.el)){
38976             this.slideIn();
38977         }
38978     },
38979
38980     animateCollapse : function(){
38981         this.beforeSlide();
38982         this.el.setStyle("z-index", 20000);
38983         var anchor = this.getSlideAnchor();
38984         this.el.slideOut(anchor, {
38985             callback : function(){
38986                 this.el.setStyle("z-index", "");
38987                 this.collapsedEl.slideIn(anchor, {duration:.3});
38988                 this.afterSlide();
38989                 this.el.setLocation(-10000,-10000);
38990                 this.el.hide();
38991                 this.fireEvent("collapsed", this);
38992             },
38993             scope: this,
38994             block: true
38995         });
38996     },
38997
38998     animateExpand : function(){
38999         this.beforeSlide();
39000         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39001         this.el.setStyle("z-index", 20000);
39002         this.collapsedEl.hide({
39003             duration:.1
39004         });
39005         this.el.slideIn(this.getSlideAnchor(), {
39006             callback : function(){
39007                 this.el.setStyle("z-index", "");
39008                 this.afterSlide();
39009                 if(this.split){
39010                     this.split.el.show();
39011                 }
39012                 this.fireEvent("invalidated", this);
39013                 this.fireEvent("expanded", this);
39014             },
39015             scope: this,
39016             block: true
39017         });
39018     },
39019
39020     anchors : {
39021         "west" : "left",
39022         "east" : "right",
39023         "north" : "top",
39024         "south" : "bottom"
39025     },
39026
39027     sanchors : {
39028         "west" : "l",
39029         "east" : "r",
39030         "north" : "t",
39031         "south" : "b"
39032     },
39033
39034     canchors : {
39035         "west" : "tl-tr",
39036         "east" : "tr-tl",
39037         "north" : "tl-bl",
39038         "south" : "bl-tl"
39039     },
39040
39041     getAnchor : function(){
39042         return this.anchors[this.position];
39043     },
39044
39045     getCollapseAnchor : function(){
39046         return this.canchors[this.position];
39047     },
39048
39049     getSlideAnchor : function(){
39050         return this.sanchors[this.position];
39051     },
39052
39053     getAlignAdj : function(){
39054         var cm = this.cmargins;
39055         switch(this.position){
39056             case "west":
39057                 return [0, 0];
39058             break;
39059             case "east":
39060                 return [0, 0];
39061             break;
39062             case "north":
39063                 return [0, 0];
39064             break;
39065             case "south":
39066                 return [0, 0];
39067             break;
39068         }
39069     },
39070
39071     getExpandAdj : function(){
39072         var c = this.collapsedEl, cm = this.cmargins;
39073         switch(this.position){
39074             case "west":
39075                 return [-(cm.right+c.getWidth()+cm.left), 0];
39076             break;
39077             case "east":
39078                 return [cm.right+c.getWidth()+cm.left, 0];
39079             break;
39080             case "north":
39081                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39082             break;
39083             case "south":
39084                 return [0, cm.top+cm.bottom+c.getHeight()];
39085             break;
39086         }
39087     }
39088 });/*
39089  * Based on:
39090  * Ext JS Library 1.1.1
39091  * Copyright(c) 2006-2007, Ext JS, LLC.
39092  *
39093  * Originally Released Under LGPL - original licence link has changed is not relivant.
39094  *
39095  * Fork - LGPL
39096  * <script type="text/javascript">
39097  */
39098 /*
39099  * These classes are private internal classes
39100  */
39101 Roo.bootstrap.layout.Center = function(config){
39102     config.region = "center";
39103     Roo.bootstrap.layout.Region.call(this, config);
39104     this.visible = true;
39105     this.minWidth = config.minWidth || 20;
39106     this.minHeight = config.minHeight || 20;
39107 };
39108
39109 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39110     hide : function(){
39111         // center panel can't be hidden
39112     },
39113     
39114     show : function(){
39115         // center panel can't be hidden
39116     },
39117     
39118     getMinWidth: function(){
39119         return this.minWidth;
39120     },
39121     
39122     getMinHeight: function(){
39123         return this.minHeight;
39124     }
39125 });
39126
39127
39128
39129
39130  
39131
39132
39133
39134
39135
39136
39137 Roo.bootstrap.layout.North = function(config)
39138 {
39139     config.region = 'north';
39140     config.cursor = 'n-resize';
39141     
39142     Roo.bootstrap.layout.Split.call(this, config);
39143     
39144     
39145     if(this.split){
39146         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39147         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39148         this.split.el.addClass("roo-layout-split-v");
39149     }
39150     var size = config.initialSize || config.height;
39151     if(typeof size != "undefined"){
39152         this.el.setHeight(size);
39153     }
39154 };
39155 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39156 {
39157     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39158     
39159     
39160     
39161     getBox : function(){
39162         if(this.collapsed){
39163             return this.collapsedEl.getBox();
39164         }
39165         var box = this.el.getBox();
39166         if(this.split){
39167             box.height += this.split.el.getHeight();
39168         }
39169         return box;
39170     },
39171     
39172     updateBox : function(box){
39173         if(this.split && !this.collapsed){
39174             box.height -= this.split.el.getHeight();
39175             this.split.el.setLeft(box.x);
39176             this.split.el.setTop(box.y+box.height);
39177             this.split.el.setWidth(box.width);
39178         }
39179         if(this.collapsed){
39180             this.updateBody(box.width, null);
39181         }
39182         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39183     }
39184 });
39185
39186
39187
39188
39189
39190 Roo.bootstrap.layout.South = function(config){
39191     config.region = 'south';
39192     config.cursor = 's-resize';
39193     Roo.bootstrap.layout.Split.call(this, config);
39194     if(this.split){
39195         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39196         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39197         this.split.el.addClass("roo-layout-split-v");
39198     }
39199     var size = config.initialSize || config.height;
39200     if(typeof size != "undefined"){
39201         this.el.setHeight(size);
39202     }
39203 };
39204
39205 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39206     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39207     getBox : function(){
39208         if(this.collapsed){
39209             return this.collapsedEl.getBox();
39210         }
39211         var box = this.el.getBox();
39212         if(this.split){
39213             var sh = this.split.el.getHeight();
39214             box.height += sh;
39215             box.y -= sh;
39216         }
39217         return box;
39218     },
39219     
39220     updateBox : function(box){
39221         if(this.split && !this.collapsed){
39222             var sh = this.split.el.getHeight();
39223             box.height -= sh;
39224             box.y += sh;
39225             this.split.el.setLeft(box.x);
39226             this.split.el.setTop(box.y-sh);
39227             this.split.el.setWidth(box.width);
39228         }
39229         if(this.collapsed){
39230             this.updateBody(box.width, null);
39231         }
39232         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39233     }
39234 });
39235
39236 Roo.bootstrap.layout.East = function(config){
39237     config.region = "east";
39238     config.cursor = "e-resize";
39239     Roo.bootstrap.layout.Split.call(this, config);
39240     if(this.split){
39241         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39242         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39243         this.split.el.addClass("roo-layout-split-h");
39244     }
39245     var size = config.initialSize || config.width;
39246     if(typeof size != "undefined"){
39247         this.el.setWidth(size);
39248     }
39249 };
39250 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39251     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39252     getBox : function(){
39253         if(this.collapsed){
39254             return this.collapsedEl.getBox();
39255         }
39256         var box = this.el.getBox();
39257         if(this.split){
39258             var sw = this.split.el.getWidth();
39259             box.width += sw;
39260             box.x -= sw;
39261         }
39262         return box;
39263     },
39264
39265     updateBox : function(box){
39266         if(this.split && !this.collapsed){
39267             var sw = this.split.el.getWidth();
39268             box.width -= sw;
39269             this.split.el.setLeft(box.x);
39270             this.split.el.setTop(box.y);
39271             this.split.el.setHeight(box.height);
39272             box.x += sw;
39273         }
39274         if(this.collapsed){
39275             this.updateBody(null, box.height);
39276         }
39277         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39278     }
39279 });
39280
39281 Roo.bootstrap.layout.West = function(config){
39282     config.region = "west";
39283     config.cursor = "w-resize";
39284     
39285     Roo.bootstrap.layout.Split.call(this, config);
39286     if(this.split){
39287         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39288         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39289         this.split.el.addClass("roo-layout-split-h");
39290     }
39291     
39292 };
39293 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39294     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39295     
39296     onRender: function(ctr, pos)
39297     {
39298         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39299         var size = this.config.initialSize || this.config.width;
39300         if(typeof size != "undefined"){
39301             this.el.setWidth(size);
39302         }
39303     },
39304     
39305     getBox : function(){
39306         if(this.collapsed){
39307             return this.collapsedEl.getBox();
39308         }
39309         var box = this.el.getBox();
39310         if(this.split){
39311             box.width += this.split.el.getWidth();
39312         }
39313         return box;
39314     },
39315     
39316     updateBox : function(box){
39317         if(this.split && !this.collapsed){
39318             var sw = this.split.el.getWidth();
39319             box.width -= sw;
39320             this.split.el.setLeft(box.x+box.width);
39321             this.split.el.setTop(box.y);
39322             this.split.el.setHeight(box.height);
39323         }
39324         if(this.collapsed){
39325             this.updateBody(null, box.height);
39326         }
39327         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39328     }
39329 });Roo.namespace("Roo.bootstrap.panel");/*
39330  * Based on:
39331  * Ext JS Library 1.1.1
39332  * Copyright(c) 2006-2007, Ext JS, LLC.
39333  *
39334  * Originally Released Under LGPL - original licence link has changed is not relivant.
39335  *
39336  * Fork - LGPL
39337  * <script type="text/javascript">
39338  */
39339 /**
39340  * @class Roo.ContentPanel
39341  * @extends Roo.util.Observable
39342  * A basic ContentPanel element.
39343  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39344  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39345  * @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
39346  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39347  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39348  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39349  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39350  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39351  * @cfg {String} title          The title for this panel
39352  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39353  * @cfg {String} url            Calls {@link #setUrl} with this value
39354  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39355  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39356  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39357  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39358  * @cfg {Boolean} badges render the badges
39359  * @cfg {String} cls  extra classes to use  
39360  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39361
39362  * @constructor
39363  * Create a new ContentPanel.
39364  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39365  * @param {String/Object} config A string to set only the title or a config object
39366  * @param {String} content (optional) Set the HTML content for this panel
39367  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39368  */
39369 Roo.bootstrap.panel.Content = function( config){
39370     
39371     this.tpl = config.tpl || false;
39372     
39373     var el = config.el;
39374     var content = config.content;
39375
39376     if(config.autoCreate){ // xtype is available if this is called from factory
39377         el = Roo.id();
39378     }
39379     this.el = Roo.get(el);
39380     if(!this.el && config && config.autoCreate){
39381         if(typeof config.autoCreate == "object"){
39382             if(!config.autoCreate.id){
39383                 config.autoCreate.id = config.id||el;
39384             }
39385             this.el = Roo.DomHelper.append(document.body,
39386                         config.autoCreate, true);
39387         }else{
39388             var elcfg =  {
39389                 tag: "div",
39390                 cls: (config.cls || '') +
39391                     (config.background ? ' bg-' + config.background : '') +
39392                     " roo-layout-inactive-content",
39393                 id: config.id||el
39394             };
39395             if (config.html) {
39396                 elcfg.html = config.html;
39397                 
39398             }
39399                         
39400             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39401         }
39402     } 
39403     this.closable = false;
39404     this.loaded = false;
39405     this.active = false;
39406    
39407       
39408     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39409         
39410         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39411         
39412         this.wrapEl = this.el; //this.el.wrap();
39413         var ti = [];
39414         if (config.toolbar.items) {
39415             ti = config.toolbar.items ;
39416             delete config.toolbar.items ;
39417         }
39418         
39419         var nitems = [];
39420         this.toolbar.render(this.wrapEl, 'before');
39421         for(var i =0;i < ti.length;i++) {
39422           //  Roo.log(['add child', items[i]]);
39423             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39424         }
39425         this.toolbar.items = nitems;
39426         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39427         delete config.toolbar;
39428         
39429     }
39430     /*
39431     // xtype created footer. - not sure if will work as we normally have to render first..
39432     if (this.footer && !this.footer.el && this.footer.xtype) {
39433         if (!this.wrapEl) {
39434             this.wrapEl = this.el.wrap();
39435         }
39436     
39437         this.footer.container = this.wrapEl.createChild();
39438          
39439         this.footer = Roo.factory(this.footer, Roo);
39440         
39441     }
39442     */
39443     
39444      if(typeof config == "string"){
39445         this.title = config;
39446     }else{
39447         Roo.apply(this, config);
39448     }
39449     
39450     if(this.resizeEl){
39451         this.resizeEl = Roo.get(this.resizeEl, true);
39452     }else{
39453         this.resizeEl = this.el;
39454     }
39455     // handle view.xtype
39456     
39457  
39458     
39459     
39460     this.addEvents({
39461         /**
39462          * @event activate
39463          * Fires when this panel is activated. 
39464          * @param {Roo.ContentPanel} this
39465          */
39466         "activate" : true,
39467         /**
39468          * @event deactivate
39469          * Fires when this panel is activated. 
39470          * @param {Roo.ContentPanel} this
39471          */
39472         "deactivate" : true,
39473
39474         /**
39475          * @event resize
39476          * Fires when this panel is resized if fitToFrame is true.
39477          * @param {Roo.ContentPanel} this
39478          * @param {Number} width The width after any component adjustments
39479          * @param {Number} height The height after any component adjustments
39480          */
39481         "resize" : true,
39482         
39483          /**
39484          * @event render
39485          * Fires when this tab is created
39486          * @param {Roo.ContentPanel} this
39487          */
39488         "render" : true
39489         
39490         
39491         
39492     });
39493     
39494
39495     
39496     
39497     if(this.autoScroll){
39498         this.resizeEl.setStyle("overflow", "auto");
39499     } else {
39500         // fix randome scrolling
39501         //this.el.on('scroll', function() {
39502         //    Roo.log('fix random scolling');
39503         //    this.scrollTo('top',0); 
39504         //});
39505     }
39506     content = content || this.content;
39507     if(content){
39508         this.setContent(content);
39509     }
39510     if(config && config.url){
39511         this.setUrl(this.url, this.params, this.loadOnce);
39512     }
39513     
39514     
39515     
39516     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39517     
39518     if (this.view && typeof(this.view.xtype) != 'undefined') {
39519         this.view.el = this.el.appendChild(document.createElement("div"));
39520         this.view = Roo.factory(this.view); 
39521         this.view.render  &&  this.view.render(false, '');  
39522     }
39523     
39524     
39525     this.fireEvent('render', this);
39526 };
39527
39528 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39529     
39530     cls : '',
39531     background : '',
39532     
39533     tabTip : '',
39534     
39535     setRegion : function(region){
39536         this.region = region;
39537         this.setActiveClass(region && !this.background);
39538     },
39539     
39540     
39541     setActiveClass: function(state)
39542     {
39543         if(state){
39544            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39545            this.el.setStyle('position','relative');
39546         }else{
39547            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39548            this.el.setStyle('position', 'absolute');
39549         } 
39550     },
39551     
39552     /**
39553      * Returns the toolbar for this Panel if one was configured. 
39554      * @return {Roo.Toolbar} 
39555      */
39556     getToolbar : function(){
39557         return this.toolbar;
39558     },
39559     
39560     setActiveState : function(active)
39561     {
39562         this.active = active;
39563         this.setActiveClass(active);
39564         if(!active){
39565             if(this.fireEvent("deactivate", this) === false){
39566                 return false;
39567             }
39568             return true;
39569         }
39570         this.fireEvent("activate", this);
39571         return true;
39572     },
39573     /**
39574      * Updates this panel's element
39575      * @param {String} content The new content
39576      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39577     */
39578     setContent : function(content, loadScripts){
39579         this.el.update(content, loadScripts);
39580     },
39581
39582     ignoreResize : function(w, h){
39583         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39584             return true;
39585         }else{
39586             this.lastSize = {width: w, height: h};
39587             return false;
39588         }
39589     },
39590     /**
39591      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39592      * @return {Roo.UpdateManager} The UpdateManager
39593      */
39594     getUpdateManager : function(){
39595         return this.el.getUpdateManager();
39596     },
39597      /**
39598      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39599      * @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:
39600 <pre><code>
39601 panel.load({
39602     url: "your-url.php",
39603     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39604     callback: yourFunction,
39605     scope: yourObject, //(optional scope)
39606     discardUrl: false,
39607     nocache: false,
39608     text: "Loading...",
39609     timeout: 30,
39610     scripts: false
39611 });
39612 </code></pre>
39613      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39614      * 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.
39615      * @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}
39616      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39617      * @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.
39618      * @return {Roo.ContentPanel} this
39619      */
39620     load : function(){
39621         var um = this.el.getUpdateManager();
39622         um.update.apply(um, arguments);
39623         return this;
39624     },
39625
39626
39627     /**
39628      * 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.
39629      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39630      * @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)
39631      * @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)
39632      * @return {Roo.UpdateManager} The UpdateManager
39633      */
39634     setUrl : function(url, params, loadOnce){
39635         if(this.refreshDelegate){
39636             this.removeListener("activate", this.refreshDelegate);
39637         }
39638         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39639         this.on("activate", this.refreshDelegate);
39640         return this.el.getUpdateManager();
39641     },
39642     
39643     _handleRefresh : function(url, params, loadOnce){
39644         if(!loadOnce || !this.loaded){
39645             var updater = this.el.getUpdateManager();
39646             updater.update(url, params, this._setLoaded.createDelegate(this));
39647         }
39648     },
39649     
39650     _setLoaded : function(){
39651         this.loaded = true;
39652     }, 
39653     
39654     /**
39655      * Returns this panel's id
39656      * @return {String} 
39657      */
39658     getId : function(){
39659         return this.el.id;
39660     },
39661     
39662     /** 
39663      * Returns this panel's element - used by regiosn to add.
39664      * @return {Roo.Element} 
39665      */
39666     getEl : function(){
39667         return this.wrapEl || this.el;
39668     },
39669     
39670    
39671     
39672     adjustForComponents : function(width, height)
39673     {
39674         //Roo.log('adjustForComponents ');
39675         if(this.resizeEl != this.el){
39676             width -= this.el.getFrameWidth('lr');
39677             height -= this.el.getFrameWidth('tb');
39678         }
39679         if(this.toolbar){
39680             var te = this.toolbar.getEl();
39681             te.setWidth(width);
39682             height -= te.getHeight();
39683         }
39684         if(this.footer){
39685             var te = this.footer.getEl();
39686             te.setWidth(width);
39687             height -= te.getHeight();
39688         }
39689         
39690         
39691         if(this.adjustments){
39692             width += this.adjustments[0];
39693             height += this.adjustments[1];
39694         }
39695         return {"width": width, "height": height};
39696     },
39697     
39698     setSize : function(width, height){
39699         if(this.fitToFrame && !this.ignoreResize(width, height)){
39700             if(this.fitContainer && this.resizeEl != this.el){
39701                 this.el.setSize(width, height);
39702             }
39703             var size = this.adjustForComponents(width, height);
39704             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39705             this.fireEvent('resize', this, size.width, size.height);
39706         }
39707     },
39708     
39709     /**
39710      * Returns this panel's title
39711      * @return {String} 
39712      */
39713     getTitle : function(){
39714         
39715         if (typeof(this.title) != 'object') {
39716             return this.title;
39717         }
39718         
39719         var t = '';
39720         for (var k in this.title) {
39721             if (!this.title.hasOwnProperty(k)) {
39722                 continue;
39723             }
39724             
39725             if (k.indexOf('-') >= 0) {
39726                 var s = k.split('-');
39727                 for (var i = 0; i<s.length; i++) {
39728                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39729                 }
39730             } else {
39731                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39732             }
39733         }
39734         return t;
39735     },
39736     
39737     /**
39738      * Set this panel's title
39739      * @param {String} title
39740      */
39741     setTitle : function(title){
39742         this.title = title;
39743         if(this.region){
39744             this.region.updatePanelTitle(this, title);
39745         }
39746     },
39747     
39748     /**
39749      * Returns true is this panel was configured to be closable
39750      * @return {Boolean} 
39751      */
39752     isClosable : function(){
39753         return this.closable;
39754     },
39755     
39756     beforeSlide : function(){
39757         this.el.clip();
39758         this.resizeEl.clip();
39759     },
39760     
39761     afterSlide : function(){
39762         this.el.unclip();
39763         this.resizeEl.unclip();
39764     },
39765     
39766     /**
39767      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39768      *   Will fail silently if the {@link #setUrl} method has not been called.
39769      *   This does not activate the panel, just updates its content.
39770      */
39771     refresh : function(){
39772         if(this.refreshDelegate){
39773            this.loaded = false;
39774            this.refreshDelegate();
39775         }
39776     },
39777     
39778     /**
39779      * Destroys this panel
39780      */
39781     destroy : function(){
39782         this.el.removeAllListeners();
39783         var tempEl = document.createElement("span");
39784         tempEl.appendChild(this.el.dom);
39785         tempEl.innerHTML = "";
39786         this.el.remove();
39787         this.el = null;
39788     },
39789     
39790     /**
39791      * form - if the content panel contains a form - this is a reference to it.
39792      * @type {Roo.form.Form}
39793      */
39794     form : false,
39795     /**
39796      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39797      *    This contains a reference to it.
39798      * @type {Roo.View}
39799      */
39800     view : false,
39801     
39802       /**
39803      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39804      * <pre><code>
39805
39806 layout.addxtype({
39807        xtype : 'Form',
39808        items: [ .... ]
39809    }
39810 );
39811
39812 </code></pre>
39813      * @param {Object} cfg Xtype definition of item to add.
39814      */
39815     
39816     
39817     getChildContainer: function () {
39818         return this.getEl();
39819     }
39820     
39821     
39822     /*
39823         var  ret = new Roo.factory(cfg);
39824         return ret;
39825         
39826         
39827         // add form..
39828         if (cfg.xtype.match(/^Form$/)) {
39829             
39830             var el;
39831             //if (this.footer) {
39832             //    el = this.footer.container.insertSibling(false, 'before');
39833             //} else {
39834                 el = this.el.createChild();
39835             //}
39836
39837             this.form = new  Roo.form.Form(cfg);
39838             
39839             
39840             if ( this.form.allItems.length) {
39841                 this.form.render(el.dom);
39842             }
39843             return this.form;
39844         }
39845         // should only have one of theses..
39846         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39847             // views.. should not be just added - used named prop 'view''
39848             
39849             cfg.el = this.el.appendChild(document.createElement("div"));
39850             // factory?
39851             
39852             var ret = new Roo.factory(cfg);
39853              
39854              ret.render && ret.render(false, ''); // render blank..
39855             this.view = ret;
39856             return ret;
39857         }
39858         return false;
39859     }
39860     \*/
39861 });
39862  
39863 /**
39864  * @class Roo.bootstrap.panel.Grid
39865  * @extends Roo.bootstrap.panel.Content
39866  * @constructor
39867  * Create a new GridPanel.
39868  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39869  * @param {Object} config A the config object
39870   
39871  */
39872
39873
39874
39875 Roo.bootstrap.panel.Grid = function(config)
39876 {
39877     
39878       
39879     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39880         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39881
39882     config.el = this.wrapper;
39883     //this.el = this.wrapper;
39884     
39885       if (config.container) {
39886         // ctor'ed from a Border/panel.grid
39887         
39888         
39889         this.wrapper.setStyle("overflow", "hidden");
39890         this.wrapper.addClass('roo-grid-container');
39891
39892     }
39893     
39894     
39895     if(config.toolbar){
39896         var tool_el = this.wrapper.createChild();    
39897         this.toolbar = Roo.factory(config.toolbar);
39898         var ti = [];
39899         if (config.toolbar.items) {
39900             ti = config.toolbar.items ;
39901             delete config.toolbar.items ;
39902         }
39903         
39904         var nitems = [];
39905         this.toolbar.render(tool_el);
39906         for(var i =0;i < ti.length;i++) {
39907           //  Roo.log(['add child', items[i]]);
39908             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39909         }
39910         this.toolbar.items = nitems;
39911         
39912         delete config.toolbar;
39913     }
39914     
39915     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39916     config.grid.scrollBody = true;;
39917     config.grid.monitorWindowResize = false; // turn off autosizing
39918     config.grid.autoHeight = false;
39919     config.grid.autoWidth = false;
39920     
39921     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39922     
39923     if (config.background) {
39924         // render grid on panel activation (if panel background)
39925         this.on('activate', function(gp) {
39926             if (!gp.grid.rendered) {
39927                 gp.grid.render(this.wrapper);
39928                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39929             }
39930         });
39931             
39932     } else {
39933         this.grid.render(this.wrapper);
39934         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39935
39936     }
39937     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39938     // ??? needed ??? config.el = this.wrapper;
39939     
39940     
39941     
39942   
39943     // xtype created footer. - not sure if will work as we normally have to render first..
39944     if (this.footer && !this.footer.el && this.footer.xtype) {
39945         
39946         var ctr = this.grid.getView().getFooterPanel(true);
39947         this.footer.dataSource = this.grid.dataSource;
39948         this.footer = Roo.factory(this.footer, Roo);
39949         this.footer.render(ctr);
39950         
39951     }
39952     
39953     
39954     
39955     
39956      
39957 };
39958
39959 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39960     getId : function(){
39961         return this.grid.id;
39962     },
39963     
39964     /**
39965      * Returns the grid for this panel
39966      * @return {Roo.bootstrap.Table} 
39967      */
39968     getGrid : function(){
39969         return this.grid;    
39970     },
39971     
39972     setSize : function(width, height){
39973         if(!this.ignoreResize(width, height)){
39974             var grid = this.grid;
39975             var size = this.adjustForComponents(width, height);
39976             // tfoot is not a footer?
39977           
39978             
39979             var gridel = grid.getGridEl();
39980             gridel.setSize(size.width, size.height);
39981             
39982             var tbd = grid.getGridEl().select('tbody', true).first();
39983             var thd = grid.getGridEl().select('thead',true).first();
39984             var tbf= grid.getGridEl().select('tfoot', true).first();
39985
39986             if (tbf) {
39987                 size.height -= thd.getHeight();
39988             }
39989             if (thd) {
39990                 size.height -= thd.getHeight();
39991             }
39992             
39993             tbd.setSize(size.width, size.height );
39994             // this is for the account management tab -seems to work there.
39995             var thd = grid.getGridEl().select('thead',true).first();
39996             //if (tbd) {
39997             //    tbd.setSize(size.width, size.height - thd.getHeight());
39998             //}
39999              
40000             grid.autoSize();
40001         }
40002     },
40003      
40004     
40005     
40006     beforeSlide : function(){
40007         this.grid.getView().scroller.clip();
40008     },
40009     
40010     afterSlide : function(){
40011         this.grid.getView().scroller.unclip();
40012     },
40013     
40014     destroy : function(){
40015         this.grid.destroy();
40016         delete this.grid;
40017         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40018     }
40019 });
40020
40021 /**
40022  * @class Roo.bootstrap.panel.Nest
40023  * @extends Roo.bootstrap.panel.Content
40024  * @constructor
40025  * Create a new Panel, that can contain a layout.Border.
40026  * 
40027  * 
40028  * @param {Roo.BorderLayout} layout The layout for this panel
40029  * @param {String/Object} config A string to set only the title or a config object
40030  */
40031 Roo.bootstrap.panel.Nest = function(config)
40032 {
40033     // construct with only one argument..
40034     /* FIXME - implement nicer consturctors
40035     if (layout.layout) {
40036         config = layout;
40037         layout = config.layout;
40038         delete config.layout;
40039     }
40040     if (layout.xtype && !layout.getEl) {
40041         // then layout needs constructing..
40042         layout = Roo.factory(layout, Roo);
40043     }
40044     */
40045     
40046     config.el =  config.layout.getEl();
40047     
40048     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40049     
40050     config.layout.monitorWindowResize = false; // turn off autosizing
40051     this.layout = config.layout;
40052     this.layout.getEl().addClass("roo-layout-nested-layout");
40053     this.layout.parent = this;
40054     
40055     
40056     
40057     
40058 };
40059
40060 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40061
40062     setSize : function(width, height){
40063         if(!this.ignoreResize(width, height)){
40064             var size = this.adjustForComponents(width, height);
40065             var el = this.layout.getEl();
40066             if (size.height < 1) {
40067                 el.setWidth(size.width);   
40068             } else {
40069                 el.setSize(size.width, size.height);
40070             }
40071             var touch = el.dom.offsetWidth;
40072             this.layout.layout();
40073             // ie requires a double layout on the first pass
40074             if(Roo.isIE && !this.initialized){
40075                 this.initialized = true;
40076                 this.layout.layout();
40077             }
40078         }
40079     },
40080     
40081     // activate all subpanels if not currently active..
40082     
40083     setActiveState : function(active){
40084         this.active = active;
40085         this.setActiveClass(active);
40086         
40087         if(!active){
40088             this.fireEvent("deactivate", this);
40089             return;
40090         }
40091         
40092         this.fireEvent("activate", this);
40093         // not sure if this should happen before or after..
40094         if (!this.layout) {
40095             return; // should not happen..
40096         }
40097         var reg = false;
40098         for (var r in this.layout.regions) {
40099             reg = this.layout.getRegion(r);
40100             if (reg.getActivePanel()) {
40101                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40102                 reg.setActivePanel(reg.getActivePanel());
40103                 continue;
40104             }
40105             if (!reg.panels.length) {
40106                 continue;
40107             }
40108             reg.showPanel(reg.getPanel(0));
40109         }
40110         
40111         
40112         
40113         
40114     },
40115     
40116     /**
40117      * Returns the nested BorderLayout for this panel
40118      * @return {Roo.BorderLayout} 
40119      */
40120     getLayout : function(){
40121         return this.layout;
40122     },
40123     
40124      /**
40125      * Adds a xtype elements to the layout of the nested panel
40126      * <pre><code>
40127
40128 panel.addxtype({
40129        xtype : 'ContentPanel',
40130        region: 'west',
40131        items: [ .... ]
40132    }
40133 );
40134
40135 panel.addxtype({
40136         xtype : 'NestedLayoutPanel',
40137         region: 'west',
40138         layout: {
40139            center: { },
40140            west: { }   
40141         },
40142         items : [ ... list of content panels or nested layout panels.. ]
40143    }
40144 );
40145 </code></pre>
40146      * @param {Object} cfg Xtype definition of item to add.
40147      */
40148     addxtype : function(cfg) {
40149         return this.layout.addxtype(cfg);
40150     
40151     }
40152 });/*
40153  * Based on:
40154  * Ext JS Library 1.1.1
40155  * Copyright(c) 2006-2007, Ext JS, LLC.
40156  *
40157  * Originally Released Under LGPL - original licence link has changed is not relivant.
40158  *
40159  * Fork - LGPL
40160  * <script type="text/javascript">
40161  */
40162 /**
40163  * @class Roo.TabPanel
40164  * @extends Roo.util.Observable
40165  * A lightweight tab container.
40166  * <br><br>
40167  * Usage:
40168  * <pre><code>
40169 // basic tabs 1, built from existing content
40170 var tabs = new Roo.TabPanel("tabs1");
40171 tabs.addTab("script", "View Script");
40172 tabs.addTab("markup", "View Markup");
40173 tabs.activate("script");
40174
40175 // more advanced tabs, built from javascript
40176 var jtabs = new Roo.TabPanel("jtabs");
40177 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40178
40179 // set up the UpdateManager
40180 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40181 var updater = tab2.getUpdateManager();
40182 updater.setDefaultUrl("ajax1.htm");
40183 tab2.on('activate', updater.refresh, updater, true);
40184
40185 // Use setUrl for Ajax loading
40186 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40187 tab3.setUrl("ajax2.htm", null, true);
40188
40189 // Disabled tab
40190 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40191 tab4.disable();
40192
40193 jtabs.activate("jtabs-1");
40194  * </code></pre>
40195  * @constructor
40196  * Create a new TabPanel.
40197  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40198  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40199  */
40200 Roo.bootstrap.panel.Tabs = function(config){
40201     /**
40202     * The container element for this TabPanel.
40203     * @type Roo.Element
40204     */
40205     this.el = Roo.get(config.el);
40206     delete config.el;
40207     if(config){
40208         if(typeof config == "boolean"){
40209             this.tabPosition = config ? "bottom" : "top";
40210         }else{
40211             Roo.apply(this, config);
40212         }
40213     }
40214     
40215     if(this.tabPosition == "bottom"){
40216         // if tabs are at the bottom = create the body first.
40217         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40218         this.el.addClass("roo-tabs-bottom");
40219     }
40220     // next create the tabs holders
40221     
40222     if (this.tabPosition == "west"){
40223         
40224         var reg = this.region; // fake it..
40225         while (reg) {
40226             if (!reg.mgr.parent) {
40227                 break;
40228             }
40229             reg = reg.mgr.parent.region;
40230         }
40231         Roo.log("got nest?");
40232         Roo.log(reg);
40233         if (reg.mgr.getRegion('west')) {
40234             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40235             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40236             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40237             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40238             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40239         
40240             
40241         }
40242         
40243         
40244     } else {
40245      
40246         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40247         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40248         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40249         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40250     }
40251     
40252     
40253     if(Roo.isIE){
40254         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40255     }
40256     
40257     // finally - if tabs are at the top, then create the body last..
40258     if(this.tabPosition != "bottom"){
40259         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40260          * @type Roo.Element
40261          */
40262         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40263         this.el.addClass("roo-tabs-top");
40264     }
40265     this.items = [];
40266
40267     this.bodyEl.setStyle("position", "relative");
40268
40269     this.active = null;
40270     this.activateDelegate = this.activate.createDelegate(this);
40271
40272     this.addEvents({
40273         /**
40274          * @event tabchange
40275          * Fires when the active tab changes
40276          * @param {Roo.TabPanel} this
40277          * @param {Roo.TabPanelItem} activePanel The new active tab
40278          */
40279         "tabchange": true,
40280         /**
40281          * @event beforetabchange
40282          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40283          * @param {Roo.TabPanel} this
40284          * @param {Object} e Set cancel to true on this object to cancel the tab change
40285          * @param {Roo.TabPanelItem} tab The tab being changed to
40286          */
40287         "beforetabchange" : true
40288     });
40289
40290     Roo.EventManager.onWindowResize(this.onResize, this);
40291     this.cpad = this.el.getPadding("lr");
40292     this.hiddenCount = 0;
40293
40294
40295     // toolbar on the tabbar support...
40296     if (this.toolbar) {
40297         alert("no toolbar support yet");
40298         this.toolbar  = false;
40299         /*
40300         var tcfg = this.toolbar;
40301         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40302         this.toolbar = new Roo.Toolbar(tcfg);
40303         if (Roo.isSafari) {
40304             var tbl = tcfg.container.child('table', true);
40305             tbl.setAttribute('width', '100%');
40306         }
40307         */
40308         
40309     }
40310    
40311
40312
40313     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40314 };
40315
40316 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40317     /*
40318      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40319      */
40320     tabPosition : "top",
40321     /*
40322      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40323      */
40324     currentTabWidth : 0,
40325     /*
40326      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40327      */
40328     minTabWidth : 40,
40329     /*
40330      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40331      */
40332     maxTabWidth : 250,
40333     /*
40334      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40335      */
40336     preferredTabWidth : 175,
40337     /*
40338      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40339      */
40340     resizeTabs : false,
40341     /*
40342      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40343      */
40344     monitorResize : true,
40345     /*
40346      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40347      */
40348     toolbar : false,  // set by caller..
40349     
40350     region : false, /// set by caller
40351     
40352     disableTooltips : true, // not used yet...
40353
40354     /**
40355      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40356      * @param {String} id The id of the div to use <b>or create</b>
40357      * @param {String} text The text for the tab
40358      * @param {String} content (optional) Content to put in the TabPanelItem body
40359      * @param {Boolean} closable (optional) True to create a close icon on the tab
40360      * @return {Roo.TabPanelItem} The created TabPanelItem
40361      */
40362     addTab : function(id, text, content, closable, tpl)
40363     {
40364         var item = new Roo.bootstrap.panel.TabItem({
40365             panel: this,
40366             id : id,
40367             text : text,
40368             closable : closable,
40369             tpl : tpl
40370         });
40371         this.addTabItem(item);
40372         if(content){
40373             item.setContent(content);
40374         }
40375         return item;
40376     },
40377
40378     /**
40379      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40380      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40381      * @return {Roo.TabPanelItem}
40382      */
40383     getTab : function(id){
40384         return this.items[id];
40385     },
40386
40387     /**
40388      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40389      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40390      */
40391     hideTab : function(id){
40392         var t = this.items[id];
40393         if(!t.isHidden()){
40394            t.setHidden(true);
40395            this.hiddenCount++;
40396            this.autoSizeTabs();
40397         }
40398     },
40399
40400     /**
40401      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40402      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40403      */
40404     unhideTab : function(id){
40405         var t = this.items[id];
40406         if(t.isHidden()){
40407            t.setHidden(false);
40408            this.hiddenCount--;
40409            this.autoSizeTabs();
40410         }
40411     },
40412
40413     /**
40414      * Adds an existing {@link Roo.TabPanelItem}.
40415      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40416      */
40417     addTabItem : function(item)
40418     {
40419         this.items[item.id] = item;
40420         this.items.push(item);
40421         this.autoSizeTabs();
40422       //  if(this.resizeTabs){
40423     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40424   //         this.autoSizeTabs();
40425 //        }else{
40426 //            item.autoSize();
40427        // }
40428     },
40429
40430     /**
40431      * Removes a {@link Roo.TabPanelItem}.
40432      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40433      */
40434     removeTab : function(id){
40435         var items = this.items;
40436         var tab = items[id];
40437         if(!tab) { return; }
40438         var index = items.indexOf(tab);
40439         if(this.active == tab && items.length > 1){
40440             var newTab = this.getNextAvailable(index);
40441             if(newTab) {
40442                 newTab.activate();
40443             }
40444         }
40445         this.stripEl.dom.removeChild(tab.pnode.dom);
40446         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40447             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40448         }
40449         items.splice(index, 1);
40450         delete this.items[tab.id];
40451         tab.fireEvent("close", tab);
40452         tab.purgeListeners();
40453         this.autoSizeTabs();
40454     },
40455
40456     getNextAvailable : function(start){
40457         var items = this.items;
40458         var index = start;
40459         // look for a next tab that will slide over to
40460         // replace the one being removed
40461         while(index < items.length){
40462             var item = items[++index];
40463             if(item && !item.isHidden()){
40464                 return item;
40465             }
40466         }
40467         // if one isn't found select the previous tab (on the left)
40468         index = start;
40469         while(index >= 0){
40470             var item = items[--index];
40471             if(item && !item.isHidden()){
40472                 return item;
40473             }
40474         }
40475         return null;
40476     },
40477
40478     /**
40479      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40480      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40481      */
40482     disableTab : function(id){
40483         var tab = this.items[id];
40484         if(tab && this.active != tab){
40485             tab.disable();
40486         }
40487     },
40488
40489     /**
40490      * Enables a {@link Roo.TabPanelItem} that is disabled.
40491      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40492      */
40493     enableTab : function(id){
40494         var tab = this.items[id];
40495         tab.enable();
40496     },
40497
40498     /**
40499      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40500      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40501      * @return {Roo.TabPanelItem} The TabPanelItem.
40502      */
40503     activate : function(id)
40504     {
40505         //Roo.log('activite:'  + id);
40506         
40507         var tab = this.items[id];
40508         if(!tab){
40509             return null;
40510         }
40511         if(tab == this.active || tab.disabled){
40512             return tab;
40513         }
40514         var e = {};
40515         this.fireEvent("beforetabchange", this, e, tab);
40516         if(e.cancel !== true && !tab.disabled){
40517             if(this.active){
40518                 this.active.hide();
40519             }
40520             this.active = this.items[id];
40521             this.active.show();
40522             this.fireEvent("tabchange", this, this.active);
40523         }
40524         return tab;
40525     },
40526
40527     /**
40528      * Gets the active {@link Roo.TabPanelItem}.
40529      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40530      */
40531     getActiveTab : function(){
40532         return this.active;
40533     },
40534
40535     /**
40536      * Updates the tab body element to fit the height of the container element
40537      * for overflow scrolling
40538      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40539      */
40540     syncHeight : function(targetHeight){
40541         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40542         var bm = this.bodyEl.getMargins();
40543         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40544         this.bodyEl.setHeight(newHeight);
40545         return newHeight;
40546     },
40547
40548     onResize : function(){
40549         if(this.monitorResize){
40550             this.autoSizeTabs();
40551         }
40552     },
40553
40554     /**
40555      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40556      */
40557     beginUpdate : function(){
40558         this.updating = true;
40559     },
40560
40561     /**
40562      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40563      */
40564     endUpdate : function(){
40565         this.updating = false;
40566         this.autoSizeTabs();
40567     },
40568
40569     /**
40570      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40571      */
40572     autoSizeTabs : function()
40573     {
40574         var count = this.items.length;
40575         var vcount = count - this.hiddenCount;
40576         
40577         if (vcount < 2) {
40578             this.stripEl.hide();
40579         } else {
40580             this.stripEl.show();
40581         }
40582         
40583         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40584             return;
40585         }
40586         
40587         
40588         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40589         var availWidth = Math.floor(w / vcount);
40590         var b = this.stripBody;
40591         if(b.getWidth() > w){
40592             var tabs = this.items;
40593             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40594             if(availWidth < this.minTabWidth){
40595                 /*if(!this.sleft){    // incomplete scrolling code
40596                     this.createScrollButtons();
40597                 }
40598                 this.showScroll();
40599                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40600             }
40601         }else{
40602             if(this.currentTabWidth < this.preferredTabWidth){
40603                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40604             }
40605         }
40606     },
40607
40608     /**
40609      * Returns the number of tabs in this TabPanel.
40610      * @return {Number}
40611      */
40612      getCount : function(){
40613          return this.items.length;
40614      },
40615
40616     /**
40617      * Resizes all the tabs to the passed width
40618      * @param {Number} The new width
40619      */
40620     setTabWidth : function(width){
40621         this.currentTabWidth = width;
40622         for(var i = 0, len = this.items.length; i < len; i++) {
40623                 if(!this.items[i].isHidden()) {
40624                 this.items[i].setWidth(width);
40625             }
40626         }
40627     },
40628
40629     /**
40630      * Destroys this TabPanel
40631      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40632      */
40633     destroy : function(removeEl){
40634         Roo.EventManager.removeResizeListener(this.onResize, this);
40635         for(var i = 0, len = this.items.length; i < len; i++){
40636             this.items[i].purgeListeners();
40637         }
40638         if(removeEl === true){
40639             this.el.update("");
40640             this.el.remove();
40641         }
40642     },
40643     
40644     createStrip : function(container)
40645     {
40646         var strip = document.createElement("nav");
40647         strip.className = Roo.bootstrap.version == 4 ?
40648             "navbar-light bg-light" : 
40649             "navbar navbar-default"; //"x-tabs-wrap";
40650         container.appendChild(strip);
40651         return strip;
40652     },
40653     
40654     createStripList : function(strip)
40655     {
40656         // div wrapper for retard IE
40657         // returns the "tr" element.
40658         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40659         //'<div class="x-tabs-strip-wrap">'+
40660           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40661           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40662         return strip.firstChild; //.firstChild.firstChild.firstChild;
40663     },
40664     createBody : function(container)
40665     {
40666         var body = document.createElement("div");
40667         Roo.id(body, "tab-body");
40668         //Roo.fly(body).addClass("x-tabs-body");
40669         Roo.fly(body).addClass("tab-content");
40670         container.appendChild(body);
40671         return body;
40672     },
40673     createItemBody :function(bodyEl, id){
40674         var body = Roo.getDom(id);
40675         if(!body){
40676             body = document.createElement("div");
40677             body.id = id;
40678         }
40679         //Roo.fly(body).addClass("x-tabs-item-body");
40680         Roo.fly(body).addClass("tab-pane");
40681          bodyEl.insertBefore(body, bodyEl.firstChild);
40682         return body;
40683     },
40684     /** @private */
40685     createStripElements :  function(stripEl, text, closable, tpl)
40686     {
40687         var td = document.createElement("li"); // was td..
40688         td.className = 'nav-item';
40689         
40690         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40691         
40692         
40693         stripEl.appendChild(td);
40694         /*if(closable){
40695             td.className = "x-tabs-closable";
40696             if(!this.closeTpl){
40697                 this.closeTpl = new Roo.Template(
40698                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40699                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40700                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40701                 );
40702             }
40703             var el = this.closeTpl.overwrite(td, {"text": text});
40704             var close = el.getElementsByTagName("div")[0];
40705             var inner = el.getElementsByTagName("em")[0];
40706             return {"el": el, "close": close, "inner": inner};
40707         } else {
40708         */
40709         // not sure what this is..
40710 //            if(!this.tabTpl){
40711                 //this.tabTpl = new Roo.Template(
40712                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40713                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40714                 //);
40715 //                this.tabTpl = new Roo.Template(
40716 //                   '<a href="#">' +
40717 //                   '<span unselectable="on"' +
40718 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40719 //                            ' >{text}</span></a>'
40720 //                );
40721 //                
40722 //            }
40723
40724
40725             var template = tpl || this.tabTpl || false;
40726             
40727             if(!template){
40728                 template =  new Roo.Template(
40729                         Roo.bootstrap.version == 4 ? 
40730                             (
40731                                 '<a class="nav-link" href="#" unselectable="on"' +
40732                                      (this.disableTooltips ? '' : ' title="{text}"') +
40733                                      ' >{text}</a>'
40734                             ) : (
40735                                 '<a class="nav-link" href="#">' +
40736                                 '<span unselectable="on"' +
40737                                          (this.disableTooltips ? '' : ' title="{text}"') +
40738                                     ' >{text}</span></a>'
40739                             )
40740                 );
40741             }
40742             
40743             switch (typeof(template)) {
40744                 case 'object' :
40745                     break;
40746                 case 'string' :
40747                     template = new Roo.Template(template);
40748                     break;
40749                 default :
40750                     break;
40751             }
40752             
40753             var el = template.overwrite(td, {"text": text});
40754             
40755             var inner = el.getElementsByTagName("span")[0];
40756             
40757             return {"el": el, "inner": inner};
40758             
40759     }
40760         
40761     
40762 });
40763
40764 /**
40765  * @class Roo.TabPanelItem
40766  * @extends Roo.util.Observable
40767  * Represents an individual item (tab plus body) in a TabPanel.
40768  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40769  * @param {String} id The id of this TabPanelItem
40770  * @param {String} text The text for the tab of this TabPanelItem
40771  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40772  */
40773 Roo.bootstrap.panel.TabItem = function(config){
40774     /**
40775      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40776      * @type Roo.TabPanel
40777      */
40778     this.tabPanel = config.panel;
40779     /**
40780      * The id for this TabPanelItem
40781      * @type String
40782      */
40783     this.id = config.id;
40784     /** @private */
40785     this.disabled = false;
40786     /** @private */
40787     this.text = config.text;
40788     /** @private */
40789     this.loaded = false;
40790     this.closable = config.closable;
40791
40792     /**
40793      * The body element for this TabPanelItem.
40794      * @type Roo.Element
40795      */
40796     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40797     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40798     this.bodyEl.setStyle("display", "block");
40799     this.bodyEl.setStyle("zoom", "1");
40800     //this.hideAction();
40801
40802     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40803     /** @private */
40804     this.el = Roo.get(els.el);
40805     this.inner = Roo.get(els.inner, true);
40806      this.textEl = Roo.bootstrap.version == 4 ?
40807         this.el : Roo.get(this.el.dom.firstChild, true);
40808
40809     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40810     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40811
40812     
40813 //    this.el.on("mousedown", this.onTabMouseDown, this);
40814     this.el.on("click", this.onTabClick, this);
40815     /** @private */
40816     if(config.closable){
40817         var c = Roo.get(els.close, true);
40818         c.dom.title = this.closeText;
40819         c.addClassOnOver("close-over");
40820         c.on("click", this.closeClick, this);
40821      }
40822
40823     this.addEvents({
40824          /**
40825          * @event activate
40826          * Fires when this tab becomes the active tab.
40827          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40828          * @param {Roo.TabPanelItem} this
40829          */
40830         "activate": true,
40831         /**
40832          * @event beforeclose
40833          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40834          * @param {Roo.TabPanelItem} this
40835          * @param {Object} e Set cancel to true on this object to cancel the close.
40836          */
40837         "beforeclose": true,
40838         /**
40839          * @event close
40840          * Fires when this tab is closed.
40841          * @param {Roo.TabPanelItem} this
40842          */
40843          "close": true,
40844         /**
40845          * @event deactivate
40846          * Fires when this tab is no longer the active tab.
40847          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40848          * @param {Roo.TabPanelItem} this
40849          */
40850          "deactivate" : true
40851     });
40852     this.hidden = false;
40853
40854     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40855 };
40856
40857 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40858            {
40859     purgeListeners : function(){
40860        Roo.util.Observable.prototype.purgeListeners.call(this);
40861        this.el.removeAllListeners();
40862     },
40863     /**
40864      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40865      */
40866     show : function(){
40867         this.status_node.addClass("active");
40868         this.showAction();
40869         if(Roo.isOpera){
40870             this.tabPanel.stripWrap.repaint();
40871         }
40872         this.fireEvent("activate", this.tabPanel, this);
40873     },
40874
40875     /**
40876      * Returns true if this tab is the active tab.
40877      * @return {Boolean}
40878      */
40879     isActive : function(){
40880         return this.tabPanel.getActiveTab() == this;
40881     },
40882
40883     /**
40884      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40885      */
40886     hide : function(){
40887         this.status_node.removeClass("active");
40888         this.hideAction();
40889         this.fireEvent("deactivate", this.tabPanel, this);
40890     },
40891
40892     hideAction : function(){
40893         this.bodyEl.hide();
40894         this.bodyEl.setStyle("position", "absolute");
40895         this.bodyEl.setLeft("-20000px");
40896         this.bodyEl.setTop("-20000px");
40897     },
40898
40899     showAction : function(){
40900         this.bodyEl.setStyle("position", "relative");
40901         this.bodyEl.setTop("");
40902         this.bodyEl.setLeft("");
40903         this.bodyEl.show();
40904     },
40905
40906     /**
40907      * Set the tooltip for the tab.
40908      * @param {String} tooltip The tab's tooltip
40909      */
40910     setTooltip : function(text){
40911         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40912             this.textEl.dom.qtip = text;
40913             this.textEl.dom.removeAttribute('title');
40914         }else{
40915             this.textEl.dom.title = text;
40916         }
40917     },
40918
40919     onTabClick : function(e){
40920         e.preventDefault();
40921         this.tabPanel.activate(this.id);
40922     },
40923
40924     onTabMouseDown : function(e){
40925         e.preventDefault();
40926         this.tabPanel.activate(this.id);
40927     },
40928 /*
40929     getWidth : function(){
40930         return this.inner.getWidth();
40931     },
40932
40933     setWidth : function(width){
40934         var iwidth = width - this.linode.getPadding("lr");
40935         this.inner.setWidth(iwidth);
40936         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40937         this.linode.setWidth(width);
40938     },
40939 */
40940     /**
40941      * Show or hide the tab
40942      * @param {Boolean} hidden True to hide or false to show.
40943      */
40944     setHidden : function(hidden){
40945         this.hidden = hidden;
40946         this.linode.setStyle("display", hidden ? "none" : "");
40947     },
40948
40949     /**
40950      * Returns true if this tab is "hidden"
40951      * @return {Boolean}
40952      */
40953     isHidden : function(){
40954         return this.hidden;
40955     },
40956
40957     /**
40958      * Returns the text for this tab
40959      * @return {String}
40960      */
40961     getText : function(){
40962         return this.text;
40963     },
40964     /*
40965     autoSize : function(){
40966         //this.el.beginMeasure();
40967         this.textEl.setWidth(1);
40968         /*
40969          *  #2804 [new] Tabs in Roojs
40970          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40971          */
40972         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40973         //this.el.endMeasure();
40974     //},
40975
40976     /**
40977      * Sets the text for the tab (Note: this also sets the tooltip text)
40978      * @param {String} text The tab's text and tooltip
40979      */
40980     setText : function(text){
40981         this.text = text;
40982         this.textEl.update(text);
40983         this.setTooltip(text);
40984         //if(!this.tabPanel.resizeTabs){
40985         //    this.autoSize();
40986         //}
40987     },
40988     /**
40989      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40990      */
40991     activate : function(){
40992         this.tabPanel.activate(this.id);
40993     },
40994
40995     /**
40996      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40997      */
40998     disable : function(){
40999         if(this.tabPanel.active != this){
41000             this.disabled = true;
41001             this.status_node.addClass("disabled");
41002         }
41003     },
41004
41005     /**
41006      * Enables this TabPanelItem if it was previously disabled.
41007      */
41008     enable : function(){
41009         this.disabled = false;
41010         this.status_node.removeClass("disabled");
41011     },
41012
41013     /**
41014      * Sets the content for this TabPanelItem.
41015      * @param {String} content The content
41016      * @param {Boolean} loadScripts true to look for and load scripts
41017      */
41018     setContent : function(content, loadScripts){
41019         this.bodyEl.update(content, loadScripts);
41020     },
41021
41022     /**
41023      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41024      * @return {Roo.UpdateManager} The UpdateManager
41025      */
41026     getUpdateManager : function(){
41027         return this.bodyEl.getUpdateManager();
41028     },
41029
41030     /**
41031      * Set a URL to be used to load the content for this TabPanelItem.
41032      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41033      * @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)
41034      * @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)
41035      * @return {Roo.UpdateManager} The UpdateManager
41036      */
41037     setUrl : function(url, params, loadOnce){
41038         if(this.refreshDelegate){
41039             this.un('activate', this.refreshDelegate);
41040         }
41041         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41042         this.on("activate", this.refreshDelegate);
41043         return this.bodyEl.getUpdateManager();
41044     },
41045
41046     /** @private */
41047     _handleRefresh : function(url, params, loadOnce){
41048         if(!loadOnce || !this.loaded){
41049             var updater = this.bodyEl.getUpdateManager();
41050             updater.update(url, params, this._setLoaded.createDelegate(this));
41051         }
41052     },
41053
41054     /**
41055      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41056      *   Will fail silently if the setUrl method has not been called.
41057      *   This does not activate the panel, just updates its content.
41058      */
41059     refresh : function(){
41060         if(this.refreshDelegate){
41061            this.loaded = false;
41062            this.refreshDelegate();
41063         }
41064     },
41065
41066     /** @private */
41067     _setLoaded : function(){
41068         this.loaded = true;
41069     },
41070
41071     /** @private */
41072     closeClick : function(e){
41073         var o = {};
41074         e.stopEvent();
41075         this.fireEvent("beforeclose", this, o);
41076         if(o.cancel !== true){
41077             this.tabPanel.removeTab(this.id);
41078         }
41079     },
41080     /**
41081      * The text displayed in the tooltip for the close icon.
41082      * @type String
41083      */
41084     closeText : "Close this tab"
41085 });
41086 /**
41087 *    This script refer to:
41088 *    Title: International Telephone Input
41089 *    Author: Jack O'Connor
41090 *    Code version:  v12.1.12
41091 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41092 **/
41093
41094 Roo.bootstrap.PhoneInputData = function() {
41095     var d = [
41096       [
41097         "Afghanistan (‫افغانستان‬‎)",
41098         "af",
41099         "93"
41100       ],
41101       [
41102         "Albania (Shqipëri)",
41103         "al",
41104         "355"
41105       ],
41106       [
41107         "Algeria (‫الجزائر‬‎)",
41108         "dz",
41109         "213"
41110       ],
41111       [
41112         "American Samoa",
41113         "as",
41114         "1684"
41115       ],
41116       [
41117         "Andorra",
41118         "ad",
41119         "376"
41120       ],
41121       [
41122         "Angola",
41123         "ao",
41124         "244"
41125       ],
41126       [
41127         "Anguilla",
41128         "ai",
41129         "1264"
41130       ],
41131       [
41132         "Antigua and Barbuda",
41133         "ag",
41134         "1268"
41135       ],
41136       [
41137         "Argentina",
41138         "ar",
41139         "54"
41140       ],
41141       [
41142         "Armenia (Հայաստան)",
41143         "am",
41144         "374"
41145       ],
41146       [
41147         "Aruba",
41148         "aw",
41149         "297"
41150       ],
41151       [
41152         "Australia",
41153         "au",
41154         "61",
41155         0
41156       ],
41157       [
41158         "Austria (Österreich)",
41159         "at",
41160         "43"
41161       ],
41162       [
41163         "Azerbaijan (Azərbaycan)",
41164         "az",
41165         "994"
41166       ],
41167       [
41168         "Bahamas",
41169         "bs",
41170         "1242"
41171       ],
41172       [
41173         "Bahrain (‫البحرين‬‎)",
41174         "bh",
41175         "973"
41176       ],
41177       [
41178         "Bangladesh (বাংলাদেশ)",
41179         "bd",
41180         "880"
41181       ],
41182       [
41183         "Barbados",
41184         "bb",
41185         "1246"
41186       ],
41187       [
41188         "Belarus (Беларусь)",
41189         "by",
41190         "375"
41191       ],
41192       [
41193         "Belgium (België)",
41194         "be",
41195         "32"
41196       ],
41197       [
41198         "Belize",
41199         "bz",
41200         "501"
41201       ],
41202       [
41203         "Benin (Bénin)",
41204         "bj",
41205         "229"
41206       ],
41207       [
41208         "Bermuda",
41209         "bm",
41210         "1441"
41211       ],
41212       [
41213         "Bhutan (འབྲུག)",
41214         "bt",
41215         "975"
41216       ],
41217       [
41218         "Bolivia",
41219         "bo",
41220         "591"
41221       ],
41222       [
41223         "Bosnia and Herzegovina (Босна и Херцеговина)",
41224         "ba",
41225         "387"
41226       ],
41227       [
41228         "Botswana",
41229         "bw",
41230         "267"
41231       ],
41232       [
41233         "Brazil (Brasil)",
41234         "br",
41235         "55"
41236       ],
41237       [
41238         "British Indian Ocean Territory",
41239         "io",
41240         "246"
41241       ],
41242       [
41243         "British Virgin Islands",
41244         "vg",
41245         "1284"
41246       ],
41247       [
41248         "Brunei",
41249         "bn",
41250         "673"
41251       ],
41252       [
41253         "Bulgaria (България)",
41254         "bg",
41255         "359"
41256       ],
41257       [
41258         "Burkina Faso",
41259         "bf",
41260         "226"
41261       ],
41262       [
41263         "Burundi (Uburundi)",
41264         "bi",
41265         "257"
41266       ],
41267       [
41268         "Cambodia (កម្ពុជា)",
41269         "kh",
41270         "855"
41271       ],
41272       [
41273         "Cameroon (Cameroun)",
41274         "cm",
41275         "237"
41276       ],
41277       [
41278         "Canada",
41279         "ca",
41280         "1",
41281         1,
41282         ["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"]
41283       ],
41284       [
41285         "Cape Verde (Kabu Verdi)",
41286         "cv",
41287         "238"
41288       ],
41289       [
41290         "Caribbean Netherlands",
41291         "bq",
41292         "599",
41293         1
41294       ],
41295       [
41296         "Cayman Islands",
41297         "ky",
41298         "1345"
41299       ],
41300       [
41301         "Central African Republic (République centrafricaine)",
41302         "cf",
41303         "236"
41304       ],
41305       [
41306         "Chad (Tchad)",
41307         "td",
41308         "235"
41309       ],
41310       [
41311         "Chile",
41312         "cl",
41313         "56"
41314       ],
41315       [
41316         "China (中国)",
41317         "cn",
41318         "86"
41319       ],
41320       [
41321         "Christmas Island",
41322         "cx",
41323         "61",
41324         2
41325       ],
41326       [
41327         "Cocos (Keeling) Islands",
41328         "cc",
41329         "61",
41330         1
41331       ],
41332       [
41333         "Colombia",
41334         "co",
41335         "57"
41336       ],
41337       [
41338         "Comoros (‫جزر القمر‬‎)",
41339         "km",
41340         "269"
41341       ],
41342       [
41343         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41344         "cd",
41345         "243"
41346       ],
41347       [
41348         "Congo (Republic) (Congo-Brazzaville)",
41349         "cg",
41350         "242"
41351       ],
41352       [
41353         "Cook Islands",
41354         "ck",
41355         "682"
41356       ],
41357       [
41358         "Costa Rica",
41359         "cr",
41360         "506"
41361       ],
41362       [
41363         "Côte d’Ivoire",
41364         "ci",
41365         "225"
41366       ],
41367       [
41368         "Croatia (Hrvatska)",
41369         "hr",
41370         "385"
41371       ],
41372       [
41373         "Cuba",
41374         "cu",
41375         "53"
41376       ],
41377       [
41378         "Curaçao",
41379         "cw",
41380         "599",
41381         0
41382       ],
41383       [
41384         "Cyprus (Κύπρος)",
41385         "cy",
41386         "357"
41387       ],
41388       [
41389         "Czech Republic (Česká republika)",
41390         "cz",
41391         "420"
41392       ],
41393       [
41394         "Denmark (Danmark)",
41395         "dk",
41396         "45"
41397       ],
41398       [
41399         "Djibouti",
41400         "dj",
41401         "253"
41402       ],
41403       [
41404         "Dominica",
41405         "dm",
41406         "1767"
41407       ],
41408       [
41409         "Dominican Republic (República Dominicana)",
41410         "do",
41411         "1",
41412         2,
41413         ["809", "829", "849"]
41414       ],
41415       [
41416         "Ecuador",
41417         "ec",
41418         "593"
41419       ],
41420       [
41421         "Egypt (‫مصر‬‎)",
41422         "eg",
41423         "20"
41424       ],
41425       [
41426         "El Salvador",
41427         "sv",
41428         "503"
41429       ],
41430       [
41431         "Equatorial Guinea (Guinea Ecuatorial)",
41432         "gq",
41433         "240"
41434       ],
41435       [
41436         "Eritrea",
41437         "er",
41438         "291"
41439       ],
41440       [
41441         "Estonia (Eesti)",
41442         "ee",
41443         "372"
41444       ],
41445       [
41446         "Ethiopia",
41447         "et",
41448         "251"
41449       ],
41450       [
41451         "Falkland Islands (Islas Malvinas)",
41452         "fk",
41453         "500"
41454       ],
41455       [
41456         "Faroe Islands (Føroyar)",
41457         "fo",
41458         "298"
41459       ],
41460       [
41461         "Fiji",
41462         "fj",
41463         "679"
41464       ],
41465       [
41466         "Finland (Suomi)",
41467         "fi",
41468         "358",
41469         0
41470       ],
41471       [
41472         "France",
41473         "fr",
41474         "33"
41475       ],
41476       [
41477         "French Guiana (Guyane française)",
41478         "gf",
41479         "594"
41480       ],
41481       [
41482         "French Polynesia (Polynésie française)",
41483         "pf",
41484         "689"
41485       ],
41486       [
41487         "Gabon",
41488         "ga",
41489         "241"
41490       ],
41491       [
41492         "Gambia",
41493         "gm",
41494         "220"
41495       ],
41496       [
41497         "Georgia (საქართველო)",
41498         "ge",
41499         "995"
41500       ],
41501       [
41502         "Germany (Deutschland)",
41503         "de",
41504         "49"
41505       ],
41506       [
41507         "Ghana (Gaana)",
41508         "gh",
41509         "233"
41510       ],
41511       [
41512         "Gibraltar",
41513         "gi",
41514         "350"
41515       ],
41516       [
41517         "Greece (Ελλάδα)",
41518         "gr",
41519         "30"
41520       ],
41521       [
41522         "Greenland (Kalaallit Nunaat)",
41523         "gl",
41524         "299"
41525       ],
41526       [
41527         "Grenada",
41528         "gd",
41529         "1473"
41530       ],
41531       [
41532         "Guadeloupe",
41533         "gp",
41534         "590",
41535         0
41536       ],
41537       [
41538         "Guam",
41539         "gu",
41540         "1671"
41541       ],
41542       [
41543         "Guatemala",
41544         "gt",
41545         "502"
41546       ],
41547       [
41548         "Guernsey",
41549         "gg",
41550         "44",
41551         1
41552       ],
41553       [
41554         "Guinea (Guinée)",
41555         "gn",
41556         "224"
41557       ],
41558       [
41559         "Guinea-Bissau (Guiné Bissau)",
41560         "gw",
41561         "245"
41562       ],
41563       [
41564         "Guyana",
41565         "gy",
41566         "592"
41567       ],
41568       [
41569         "Haiti",
41570         "ht",
41571         "509"
41572       ],
41573       [
41574         "Honduras",
41575         "hn",
41576         "504"
41577       ],
41578       [
41579         "Hong Kong (香港)",
41580         "hk",
41581         "852"
41582       ],
41583       [
41584         "Hungary (Magyarország)",
41585         "hu",
41586         "36"
41587       ],
41588       [
41589         "Iceland (Ísland)",
41590         "is",
41591         "354"
41592       ],
41593       [
41594         "India (भारत)",
41595         "in",
41596         "91"
41597       ],
41598       [
41599         "Indonesia",
41600         "id",
41601         "62"
41602       ],
41603       [
41604         "Iran (‫ایران‬‎)",
41605         "ir",
41606         "98"
41607       ],
41608       [
41609         "Iraq (‫العراق‬‎)",
41610         "iq",
41611         "964"
41612       ],
41613       [
41614         "Ireland",
41615         "ie",
41616         "353"
41617       ],
41618       [
41619         "Isle of Man",
41620         "im",
41621         "44",
41622         2
41623       ],
41624       [
41625         "Israel (‫ישראל‬‎)",
41626         "il",
41627         "972"
41628       ],
41629       [
41630         "Italy (Italia)",
41631         "it",
41632         "39",
41633         0
41634       ],
41635       [
41636         "Jamaica",
41637         "jm",
41638         "1876"
41639       ],
41640       [
41641         "Japan (日本)",
41642         "jp",
41643         "81"
41644       ],
41645       [
41646         "Jersey",
41647         "je",
41648         "44",
41649         3
41650       ],
41651       [
41652         "Jordan (‫الأردن‬‎)",
41653         "jo",
41654         "962"
41655       ],
41656       [
41657         "Kazakhstan (Казахстан)",
41658         "kz",
41659         "7",
41660         1
41661       ],
41662       [
41663         "Kenya",
41664         "ke",
41665         "254"
41666       ],
41667       [
41668         "Kiribati",
41669         "ki",
41670         "686"
41671       ],
41672       [
41673         "Kosovo",
41674         "xk",
41675         "383"
41676       ],
41677       [
41678         "Kuwait (‫الكويت‬‎)",
41679         "kw",
41680         "965"
41681       ],
41682       [
41683         "Kyrgyzstan (Кыргызстан)",
41684         "kg",
41685         "996"
41686       ],
41687       [
41688         "Laos (ລາວ)",
41689         "la",
41690         "856"
41691       ],
41692       [
41693         "Latvia (Latvija)",
41694         "lv",
41695         "371"
41696       ],
41697       [
41698         "Lebanon (‫لبنان‬‎)",
41699         "lb",
41700         "961"
41701       ],
41702       [
41703         "Lesotho",
41704         "ls",
41705         "266"
41706       ],
41707       [
41708         "Liberia",
41709         "lr",
41710         "231"
41711       ],
41712       [
41713         "Libya (‫ليبيا‬‎)",
41714         "ly",
41715         "218"
41716       ],
41717       [
41718         "Liechtenstein",
41719         "li",
41720         "423"
41721       ],
41722       [
41723         "Lithuania (Lietuva)",
41724         "lt",
41725         "370"
41726       ],
41727       [
41728         "Luxembourg",
41729         "lu",
41730         "352"
41731       ],
41732       [
41733         "Macau (澳門)",
41734         "mo",
41735         "853"
41736       ],
41737       [
41738         "Macedonia (FYROM) (Македонија)",
41739         "mk",
41740         "389"
41741       ],
41742       [
41743         "Madagascar (Madagasikara)",
41744         "mg",
41745         "261"
41746       ],
41747       [
41748         "Malawi",
41749         "mw",
41750         "265"
41751       ],
41752       [
41753         "Malaysia",
41754         "my",
41755         "60"
41756       ],
41757       [
41758         "Maldives",
41759         "mv",
41760         "960"
41761       ],
41762       [
41763         "Mali",
41764         "ml",
41765         "223"
41766       ],
41767       [
41768         "Malta",
41769         "mt",
41770         "356"
41771       ],
41772       [
41773         "Marshall Islands",
41774         "mh",
41775         "692"
41776       ],
41777       [
41778         "Martinique",
41779         "mq",
41780         "596"
41781       ],
41782       [
41783         "Mauritania (‫موريتانيا‬‎)",
41784         "mr",
41785         "222"
41786       ],
41787       [
41788         "Mauritius (Moris)",
41789         "mu",
41790         "230"
41791       ],
41792       [
41793         "Mayotte",
41794         "yt",
41795         "262",
41796         1
41797       ],
41798       [
41799         "Mexico (México)",
41800         "mx",
41801         "52"
41802       ],
41803       [
41804         "Micronesia",
41805         "fm",
41806         "691"
41807       ],
41808       [
41809         "Moldova (Republica Moldova)",
41810         "md",
41811         "373"
41812       ],
41813       [
41814         "Monaco",
41815         "mc",
41816         "377"
41817       ],
41818       [
41819         "Mongolia (Монгол)",
41820         "mn",
41821         "976"
41822       ],
41823       [
41824         "Montenegro (Crna Gora)",
41825         "me",
41826         "382"
41827       ],
41828       [
41829         "Montserrat",
41830         "ms",
41831         "1664"
41832       ],
41833       [
41834         "Morocco (‫المغرب‬‎)",
41835         "ma",
41836         "212",
41837         0
41838       ],
41839       [
41840         "Mozambique (Moçambique)",
41841         "mz",
41842         "258"
41843       ],
41844       [
41845         "Myanmar (Burma) (မြန်မာ)",
41846         "mm",
41847         "95"
41848       ],
41849       [
41850         "Namibia (Namibië)",
41851         "na",
41852         "264"
41853       ],
41854       [
41855         "Nauru",
41856         "nr",
41857         "674"
41858       ],
41859       [
41860         "Nepal (नेपाल)",
41861         "np",
41862         "977"
41863       ],
41864       [
41865         "Netherlands (Nederland)",
41866         "nl",
41867         "31"
41868       ],
41869       [
41870         "New Caledonia (Nouvelle-Calédonie)",
41871         "nc",
41872         "687"
41873       ],
41874       [
41875         "New Zealand",
41876         "nz",
41877         "64"
41878       ],
41879       [
41880         "Nicaragua",
41881         "ni",
41882         "505"
41883       ],
41884       [
41885         "Niger (Nijar)",
41886         "ne",
41887         "227"
41888       ],
41889       [
41890         "Nigeria",
41891         "ng",
41892         "234"
41893       ],
41894       [
41895         "Niue",
41896         "nu",
41897         "683"
41898       ],
41899       [
41900         "Norfolk Island",
41901         "nf",
41902         "672"
41903       ],
41904       [
41905         "North Korea (조선 민주주의 인민 공화국)",
41906         "kp",
41907         "850"
41908       ],
41909       [
41910         "Northern Mariana Islands",
41911         "mp",
41912         "1670"
41913       ],
41914       [
41915         "Norway (Norge)",
41916         "no",
41917         "47",
41918         0
41919       ],
41920       [
41921         "Oman (‫عُمان‬‎)",
41922         "om",
41923         "968"
41924       ],
41925       [
41926         "Pakistan (‫پاکستان‬‎)",
41927         "pk",
41928         "92"
41929       ],
41930       [
41931         "Palau",
41932         "pw",
41933         "680"
41934       ],
41935       [
41936         "Palestine (‫فلسطين‬‎)",
41937         "ps",
41938         "970"
41939       ],
41940       [
41941         "Panama (Panamá)",
41942         "pa",
41943         "507"
41944       ],
41945       [
41946         "Papua New Guinea",
41947         "pg",
41948         "675"
41949       ],
41950       [
41951         "Paraguay",
41952         "py",
41953         "595"
41954       ],
41955       [
41956         "Peru (Perú)",
41957         "pe",
41958         "51"
41959       ],
41960       [
41961         "Philippines",
41962         "ph",
41963         "63"
41964       ],
41965       [
41966         "Poland (Polska)",
41967         "pl",
41968         "48"
41969       ],
41970       [
41971         "Portugal",
41972         "pt",
41973         "351"
41974       ],
41975       [
41976         "Puerto Rico",
41977         "pr",
41978         "1",
41979         3,
41980         ["787", "939"]
41981       ],
41982       [
41983         "Qatar (‫قطر‬‎)",
41984         "qa",
41985         "974"
41986       ],
41987       [
41988         "Réunion (La Réunion)",
41989         "re",
41990         "262",
41991         0
41992       ],
41993       [
41994         "Romania (România)",
41995         "ro",
41996         "40"
41997       ],
41998       [
41999         "Russia (Россия)",
42000         "ru",
42001         "7",
42002         0
42003       ],
42004       [
42005         "Rwanda",
42006         "rw",
42007         "250"
42008       ],
42009       [
42010         "Saint Barthélemy",
42011         "bl",
42012         "590",
42013         1
42014       ],
42015       [
42016         "Saint Helena",
42017         "sh",
42018         "290"
42019       ],
42020       [
42021         "Saint Kitts and Nevis",
42022         "kn",
42023         "1869"
42024       ],
42025       [
42026         "Saint Lucia",
42027         "lc",
42028         "1758"
42029       ],
42030       [
42031         "Saint Martin (Saint-Martin (partie française))",
42032         "mf",
42033         "590",
42034         2
42035       ],
42036       [
42037         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42038         "pm",
42039         "508"
42040       ],
42041       [
42042         "Saint Vincent and the Grenadines",
42043         "vc",
42044         "1784"
42045       ],
42046       [
42047         "Samoa",
42048         "ws",
42049         "685"
42050       ],
42051       [
42052         "San Marino",
42053         "sm",
42054         "378"
42055       ],
42056       [
42057         "São Tomé and Príncipe (São Tomé e Príncipe)",
42058         "st",
42059         "239"
42060       ],
42061       [
42062         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42063         "sa",
42064         "966"
42065       ],
42066       [
42067         "Senegal (Sénégal)",
42068         "sn",
42069         "221"
42070       ],
42071       [
42072         "Serbia (Србија)",
42073         "rs",
42074         "381"
42075       ],
42076       [
42077         "Seychelles",
42078         "sc",
42079         "248"
42080       ],
42081       [
42082         "Sierra Leone",
42083         "sl",
42084         "232"
42085       ],
42086       [
42087         "Singapore",
42088         "sg",
42089         "65"
42090       ],
42091       [
42092         "Sint Maarten",
42093         "sx",
42094         "1721"
42095       ],
42096       [
42097         "Slovakia (Slovensko)",
42098         "sk",
42099         "421"
42100       ],
42101       [
42102         "Slovenia (Slovenija)",
42103         "si",
42104         "386"
42105       ],
42106       [
42107         "Solomon Islands",
42108         "sb",
42109         "677"
42110       ],
42111       [
42112         "Somalia (Soomaaliya)",
42113         "so",
42114         "252"
42115       ],
42116       [
42117         "South Africa",
42118         "za",
42119         "27"
42120       ],
42121       [
42122         "South Korea (대한민국)",
42123         "kr",
42124         "82"
42125       ],
42126       [
42127         "South Sudan (‫جنوب السودان‬‎)",
42128         "ss",
42129         "211"
42130       ],
42131       [
42132         "Spain (España)",
42133         "es",
42134         "34"
42135       ],
42136       [
42137         "Sri Lanka (ශ්‍රී ලංකාව)",
42138         "lk",
42139         "94"
42140       ],
42141       [
42142         "Sudan (‫السودان‬‎)",
42143         "sd",
42144         "249"
42145       ],
42146       [
42147         "Suriname",
42148         "sr",
42149         "597"
42150       ],
42151       [
42152         "Svalbard and Jan Mayen",
42153         "sj",
42154         "47",
42155         1
42156       ],
42157       [
42158         "Swaziland",
42159         "sz",
42160         "268"
42161       ],
42162       [
42163         "Sweden (Sverige)",
42164         "se",
42165         "46"
42166       ],
42167       [
42168         "Switzerland (Schweiz)",
42169         "ch",
42170         "41"
42171       ],
42172       [
42173         "Syria (‫سوريا‬‎)",
42174         "sy",
42175         "963"
42176       ],
42177       [
42178         "Taiwan (台灣)",
42179         "tw",
42180         "886"
42181       ],
42182       [
42183         "Tajikistan",
42184         "tj",
42185         "992"
42186       ],
42187       [
42188         "Tanzania",
42189         "tz",
42190         "255"
42191       ],
42192       [
42193         "Thailand (ไทย)",
42194         "th",
42195         "66"
42196       ],
42197       [
42198         "Timor-Leste",
42199         "tl",
42200         "670"
42201       ],
42202       [
42203         "Togo",
42204         "tg",
42205         "228"
42206       ],
42207       [
42208         "Tokelau",
42209         "tk",
42210         "690"
42211       ],
42212       [
42213         "Tonga",
42214         "to",
42215         "676"
42216       ],
42217       [
42218         "Trinidad and Tobago",
42219         "tt",
42220         "1868"
42221       ],
42222       [
42223         "Tunisia (‫تونس‬‎)",
42224         "tn",
42225         "216"
42226       ],
42227       [
42228         "Turkey (Türkiye)",
42229         "tr",
42230         "90"
42231       ],
42232       [
42233         "Turkmenistan",
42234         "tm",
42235         "993"
42236       ],
42237       [
42238         "Turks and Caicos Islands",
42239         "tc",
42240         "1649"
42241       ],
42242       [
42243         "Tuvalu",
42244         "tv",
42245         "688"
42246       ],
42247       [
42248         "U.S. Virgin Islands",
42249         "vi",
42250         "1340"
42251       ],
42252       [
42253         "Uganda",
42254         "ug",
42255         "256"
42256       ],
42257       [
42258         "Ukraine (Україна)",
42259         "ua",
42260         "380"
42261       ],
42262       [
42263         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42264         "ae",
42265         "971"
42266       ],
42267       [
42268         "United Kingdom",
42269         "gb",
42270         "44",
42271         0
42272       ],
42273       [
42274         "United States",
42275         "us",
42276         "1",
42277         0
42278       ],
42279       [
42280         "Uruguay",
42281         "uy",
42282         "598"
42283       ],
42284       [
42285         "Uzbekistan (Oʻzbekiston)",
42286         "uz",
42287         "998"
42288       ],
42289       [
42290         "Vanuatu",
42291         "vu",
42292         "678"
42293       ],
42294       [
42295         "Vatican City (Città del Vaticano)",
42296         "va",
42297         "39",
42298         1
42299       ],
42300       [
42301         "Venezuela",
42302         "ve",
42303         "58"
42304       ],
42305       [
42306         "Vietnam (Việt Nam)",
42307         "vn",
42308         "84"
42309       ],
42310       [
42311         "Wallis and Futuna (Wallis-et-Futuna)",
42312         "wf",
42313         "681"
42314       ],
42315       [
42316         "Western Sahara (‫الصحراء الغربية‬‎)",
42317         "eh",
42318         "212",
42319         1
42320       ],
42321       [
42322         "Yemen (‫اليمن‬‎)",
42323         "ye",
42324         "967"
42325       ],
42326       [
42327         "Zambia",
42328         "zm",
42329         "260"
42330       ],
42331       [
42332         "Zimbabwe",
42333         "zw",
42334         "263"
42335       ],
42336       [
42337         "Åland Islands",
42338         "ax",
42339         "358",
42340         1
42341       ]
42342   ];
42343   
42344   return d;
42345 }/**
42346 *    This script refer to:
42347 *    Title: International Telephone Input
42348 *    Author: Jack O'Connor
42349 *    Code version:  v12.1.12
42350 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42351 **/
42352
42353 /**
42354  * @class Roo.bootstrap.PhoneInput
42355  * @extends Roo.bootstrap.TriggerField
42356  * An input with International dial-code selection
42357  
42358  * @cfg {String} defaultDialCode default '+852'
42359  * @cfg {Array} preferedCountries default []
42360   
42361  * @constructor
42362  * Create a new PhoneInput.
42363  * @param {Object} config Configuration options
42364  */
42365
42366 Roo.bootstrap.PhoneInput = function(config) {
42367     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42368 };
42369
42370 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42371         
42372         listWidth: undefined,
42373         
42374         selectedClass: 'active',
42375         
42376         invalidClass : "has-warning",
42377         
42378         validClass: 'has-success',
42379         
42380         allowed: '0123456789',
42381         
42382         max_length: 15,
42383         
42384         /**
42385          * @cfg {String} defaultDialCode The default dial code when initializing the input
42386          */
42387         defaultDialCode: '+852',
42388         
42389         /**
42390          * @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
42391          */
42392         preferedCountries: false,
42393         
42394         getAutoCreate : function()
42395         {
42396             var data = Roo.bootstrap.PhoneInputData();
42397             var align = this.labelAlign || this.parentLabelAlign();
42398             var id = Roo.id();
42399             
42400             this.allCountries = [];
42401             this.dialCodeMapping = [];
42402             
42403             for (var i = 0; i < data.length; i++) {
42404               var c = data[i];
42405               this.allCountries[i] = {
42406                 name: c[0],
42407                 iso2: c[1],
42408                 dialCode: c[2],
42409                 priority: c[3] || 0,
42410                 areaCodes: c[4] || null
42411               };
42412               this.dialCodeMapping[c[2]] = {
42413                   name: c[0],
42414                   iso2: c[1],
42415                   priority: c[3] || 0,
42416                   areaCodes: c[4] || null
42417               };
42418             }
42419             
42420             var cfg = {
42421                 cls: 'form-group',
42422                 cn: []
42423             };
42424             
42425             var input =  {
42426                 tag: 'input',
42427                 id : id,
42428                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42429                 maxlength: this.max_length,
42430                 cls : 'form-control tel-input',
42431                 autocomplete: 'new-password'
42432             };
42433             
42434             var hiddenInput = {
42435                 tag: 'input',
42436                 type: 'hidden',
42437                 cls: 'hidden-tel-input'
42438             };
42439             
42440             if (this.name) {
42441                 hiddenInput.name = this.name;
42442             }
42443             
42444             if (this.disabled) {
42445                 input.disabled = true;
42446             }
42447             
42448             var flag_container = {
42449                 tag: 'div',
42450                 cls: 'flag-box',
42451                 cn: [
42452                     {
42453                         tag: 'div',
42454                         cls: 'flag'
42455                     },
42456                     {
42457                         tag: 'div',
42458                         cls: 'caret'
42459                     }
42460                 ]
42461             };
42462             
42463             var box = {
42464                 tag: 'div',
42465                 cls: this.hasFeedback ? 'has-feedback' : '',
42466                 cn: [
42467                     hiddenInput,
42468                     input,
42469                     {
42470                         tag: 'input',
42471                         cls: 'dial-code-holder',
42472                         disabled: true
42473                     }
42474                 ]
42475             };
42476             
42477             var container = {
42478                 cls: 'roo-select2-container input-group',
42479                 cn: [
42480                     flag_container,
42481                     box
42482                 ]
42483             };
42484             
42485             if (this.fieldLabel.length) {
42486                 var indicator = {
42487                     tag: 'i',
42488                     tooltip: 'This field is required'
42489                 };
42490                 
42491                 var label = {
42492                     tag: 'label',
42493                     'for':  id,
42494                     cls: 'control-label',
42495                     cn: []
42496                 };
42497                 
42498                 var label_text = {
42499                     tag: 'span',
42500                     html: this.fieldLabel
42501                 };
42502                 
42503                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42504                 label.cn = [
42505                     indicator,
42506                     label_text
42507                 ];
42508                 
42509                 if(this.indicatorpos == 'right') {
42510                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42511                     label.cn = [
42512                         label_text,
42513                         indicator
42514                     ];
42515                 }
42516                 
42517                 if(align == 'left') {
42518                     container = {
42519                         tag: 'div',
42520                         cn: [
42521                             container
42522                         ]
42523                     };
42524                     
42525                     if(this.labelWidth > 12){
42526                         label.style = "width: " + this.labelWidth + 'px';
42527                     }
42528                     if(this.labelWidth < 13 && this.labelmd == 0){
42529                         this.labelmd = this.labelWidth;
42530                     }
42531                     if(this.labellg > 0){
42532                         label.cls += ' col-lg-' + this.labellg;
42533                         input.cls += ' col-lg-' + (12 - this.labellg);
42534                     }
42535                     if(this.labelmd > 0){
42536                         label.cls += ' col-md-' + this.labelmd;
42537                         container.cls += ' col-md-' + (12 - this.labelmd);
42538                     }
42539                     if(this.labelsm > 0){
42540                         label.cls += ' col-sm-' + this.labelsm;
42541                         container.cls += ' col-sm-' + (12 - this.labelsm);
42542                     }
42543                     if(this.labelxs > 0){
42544                         label.cls += ' col-xs-' + this.labelxs;
42545                         container.cls += ' col-xs-' + (12 - this.labelxs);
42546                     }
42547                 }
42548             }
42549             
42550             cfg.cn = [
42551                 label,
42552                 container
42553             ];
42554             
42555             var settings = this;
42556             
42557             ['xs','sm','md','lg'].map(function(size){
42558                 if (settings[size]) {
42559                     cfg.cls += ' col-' + size + '-' + settings[size];
42560                 }
42561             });
42562             
42563             this.store = new Roo.data.Store({
42564                 proxy : new Roo.data.MemoryProxy({}),
42565                 reader : new Roo.data.JsonReader({
42566                     fields : [
42567                         {
42568                             'name' : 'name',
42569                             'type' : 'string'
42570                         },
42571                         {
42572                             'name' : 'iso2',
42573                             'type' : 'string'
42574                         },
42575                         {
42576                             'name' : 'dialCode',
42577                             'type' : 'string'
42578                         },
42579                         {
42580                             'name' : 'priority',
42581                             'type' : 'string'
42582                         },
42583                         {
42584                             'name' : 'areaCodes',
42585                             'type' : 'string'
42586                         }
42587                     ]
42588                 })
42589             });
42590             
42591             if(!this.preferedCountries) {
42592                 this.preferedCountries = [
42593                     'hk',
42594                     'gb',
42595                     'us'
42596                 ];
42597             }
42598             
42599             var p = this.preferedCountries.reverse();
42600             
42601             if(p) {
42602                 for (var i = 0; i < p.length; i++) {
42603                     for (var j = 0; j < this.allCountries.length; j++) {
42604                         if(this.allCountries[j].iso2 == p[i]) {
42605                             var t = this.allCountries[j];
42606                             this.allCountries.splice(j,1);
42607                             this.allCountries.unshift(t);
42608                         }
42609                     } 
42610                 }
42611             }
42612             
42613             this.store.proxy.data = {
42614                 success: true,
42615                 data: this.allCountries
42616             };
42617             
42618             return cfg;
42619         },
42620         
42621         initEvents : function()
42622         {
42623             this.createList();
42624             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42625             
42626             this.indicator = this.indicatorEl();
42627             this.flag = this.flagEl();
42628             this.dialCodeHolder = this.dialCodeHolderEl();
42629             
42630             this.trigger = this.el.select('div.flag-box',true).first();
42631             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42632             
42633             var _this = this;
42634             
42635             (function(){
42636                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42637                 _this.list.setWidth(lw);
42638             }).defer(100);
42639             
42640             this.list.on('mouseover', this.onViewOver, this);
42641             this.list.on('mousemove', this.onViewMove, this);
42642             this.inputEl().on("keyup", this.onKeyUp, this);
42643             this.inputEl().on("keypress", this.onKeyPress, this);
42644             
42645             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42646
42647             this.view = new Roo.View(this.list, this.tpl, {
42648                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42649             });
42650             
42651             this.view.on('click', this.onViewClick, this);
42652             this.setValue(this.defaultDialCode);
42653         },
42654         
42655         onTriggerClick : function(e)
42656         {
42657             Roo.log('trigger click');
42658             if(this.disabled){
42659                 return;
42660             }
42661             
42662             if(this.isExpanded()){
42663                 this.collapse();
42664                 this.hasFocus = false;
42665             }else {
42666                 this.store.load({});
42667                 this.hasFocus = true;
42668                 this.expand();
42669             }
42670         },
42671         
42672         isExpanded : function()
42673         {
42674             return this.list.isVisible();
42675         },
42676         
42677         collapse : function()
42678         {
42679             if(!this.isExpanded()){
42680                 return;
42681             }
42682             this.list.hide();
42683             Roo.get(document).un('mousedown', this.collapseIf, this);
42684             Roo.get(document).un('mousewheel', this.collapseIf, this);
42685             this.fireEvent('collapse', this);
42686             this.validate();
42687         },
42688         
42689         expand : function()
42690         {
42691             Roo.log('expand');
42692
42693             if(this.isExpanded() || !this.hasFocus){
42694                 return;
42695             }
42696             
42697             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42698             this.list.setWidth(lw);
42699             
42700             this.list.show();
42701             this.restrictHeight();
42702             
42703             Roo.get(document).on('mousedown', this.collapseIf, this);
42704             Roo.get(document).on('mousewheel', this.collapseIf, this);
42705             
42706             this.fireEvent('expand', this);
42707         },
42708         
42709         restrictHeight : function()
42710         {
42711             this.list.alignTo(this.inputEl(), this.listAlign);
42712             this.list.alignTo(this.inputEl(), this.listAlign);
42713         },
42714         
42715         onViewOver : function(e, t)
42716         {
42717             if(this.inKeyMode){
42718                 return;
42719             }
42720             var item = this.view.findItemFromChild(t);
42721             
42722             if(item){
42723                 var index = this.view.indexOf(item);
42724                 this.select(index, false);
42725             }
42726         },
42727
42728         // private
42729         onViewClick : function(view, doFocus, el, e)
42730         {
42731             var index = this.view.getSelectedIndexes()[0];
42732             
42733             var r = this.store.getAt(index);
42734             
42735             if(r){
42736                 this.onSelect(r, index);
42737             }
42738             if(doFocus !== false && !this.blockFocus){
42739                 this.inputEl().focus();
42740             }
42741         },
42742         
42743         onViewMove : function(e, t)
42744         {
42745             this.inKeyMode = false;
42746         },
42747         
42748         select : function(index, scrollIntoView)
42749         {
42750             this.selectedIndex = index;
42751             this.view.select(index);
42752             if(scrollIntoView !== false){
42753                 var el = this.view.getNode(index);
42754                 if(el){
42755                     this.list.scrollChildIntoView(el, false);
42756                 }
42757             }
42758         },
42759         
42760         createList : function()
42761         {
42762             this.list = Roo.get(document.body).createChild({
42763                 tag: 'ul',
42764                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42765                 style: 'display:none'
42766             });
42767             
42768             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42769         },
42770         
42771         collapseIf : function(e)
42772         {
42773             var in_combo  = e.within(this.el);
42774             var in_list =  e.within(this.list);
42775             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42776             
42777             if (in_combo || in_list || is_list) {
42778                 return;
42779             }
42780             this.collapse();
42781         },
42782         
42783         onSelect : function(record, index)
42784         {
42785             if(this.fireEvent('beforeselect', this, record, index) !== false){
42786                 
42787                 this.setFlagClass(record.data.iso2);
42788                 this.setDialCode(record.data.dialCode);
42789                 this.hasFocus = false;
42790                 this.collapse();
42791                 this.fireEvent('select', this, record, index);
42792             }
42793         },
42794         
42795         flagEl : function()
42796         {
42797             var flag = this.el.select('div.flag',true).first();
42798             if(!flag){
42799                 return false;
42800             }
42801             return flag;
42802         },
42803         
42804         dialCodeHolderEl : function()
42805         {
42806             var d = this.el.select('input.dial-code-holder',true).first();
42807             if(!d){
42808                 return false;
42809             }
42810             return d;
42811         },
42812         
42813         setDialCode : function(v)
42814         {
42815             this.dialCodeHolder.dom.value = '+'+v;
42816         },
42817         
42818         setFlagClass : function(n)
42819         {
42820             this.flag.dom.className = 'flag '+n;
42821         },
42822         
42823         getValue : function()
42824         {
42825             var v = this.inputEl().getValue();
42826             if(this.dialCodeHolder) {
42827                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42828             }
42829             return v;
42830         },
42831         
42832         setValue : function(v)
42833         {
42834             var d = this.getDialCode(v);
42835             
42836             //invalid dial code
42837             if(v.length == 0 || !d || d.length == 0) {
42838                 if(this.rendered){
42839                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42840                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42841                 }
42842                 return;
42843             }
42844             
42845             //valid dial code
42846             this.setFlagClass(this.dialCodeMapping[d].iso2);
42847             this.setDialCode(d);
42848             this.inputEl().dom.value = v.replace('+'+d,'');
42849             this.hiddenEl().dom.value = this.getValue();
42850             
42851             this.validate();
42852         },
42853         
42854         getDialCode : function(v)
42855         {
42856             v = v ||  '';
42857             
42858             if (v.length == 0) {
42859                 return this.dialCodeHolder.dom.value;
42860             }
42861             
42862             var dialCode = "";
42863             if (v.charAt(0) != "+") {
42864                 return false;
42865             }
42866             var numericChars = "";
42867             for (var i = 1; i < v.length; i++) {
42868               var c = v.charAt(i);
42869               if (!isNaN(c)) {
42870                 numericChars += c;
42871                 if (this.dialCodeMapping[numericChars]) {
42872                   dialCode = v.substr(1, i);
42873                 }
42874                 if (numericChars.length == 4) {
42875                   break;
42876                 }
42877               }
42878             }
42879             return dialCode;
42880         },
42881         
42882         reset : function()
42883         {
42884             this.setValue(this.defaultDialCode);
42885             this.validate();
42886         },
42887         
42888         hiddenEl : function()
42889         {
42890             return this.el.select('input.hidden-tel-input',true).first();
42891         },
42892         
42893         // after setting val
42894         onKeyUp : function(e){
42895             this.setValue(this.getValue());
42896         },
42897         
42898         onKeyPress : function(e){
42899             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42900                 e.stopEvent();
42901             }
42902         }
42903         
42904 });
42905 /**
42906  * @class Roo.bootstrap.MoneyField
42907  * @extends Roo.bootstrap.ComboBox
42908  * Bootstrap MoneyField class
42909  * 
42910  * @constructor
42911  * Create a new MoneyField.
42912  * @param {Object} config Configuration options
42913  */
42914
42915 Roo.bootstrap.MoneyField = function(config) {
42916     
42917     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42918     
42919 };
42920
42921 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42922     
42923     /**
42924      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42925      */
42926     allowDecimals : true,
42927     /**
42928      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42929      */
42930     decimalSeparator : ".",
42931     /**
42932      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42933      */
42934     decimalPrecision : 0,
42935     /**
42936      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42937      */
42938     allowNegative : true,
42939     /**
42940      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42941      */
42942     allowZero: true,
42943     /**
42944      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42945      */
42946     minValue : Number.NEGATIVE_INFINITY,
42947     /**
42948      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42949      */
42950     maxValue : Number.MAX_VALUE,
42951     /**
42952      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42953      */
42954     minText : "The minimum value for this field is {0}",
42955     /**
42956      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42957      */
42958     maxText : "The maximum value for this field is {0}",
42959     /**
42960      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42961      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42962      */
42963     nanText : "{0} is not a valid number",
42964     /**
42965      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42966      */
42967     castInt : true,
42968     /**
42969      * @cfg {String} defaults currency of the MoneyField
42970      * value should be in lkey
42971      */
42972     defaultCurrency : false,
42973     /**
42974      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42975      */
42976     thousandsDelimiter : false,
42977     /**
42978      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42979      */
42980     max_length: false,
42981     
42982     inputlg : 9,
42983     inputmd : 9,
42984     inputsm : 9,
42985     inputxs : 6,
42986     
42987     store : false,
42988     
42989     getAutoCreate : function()
42990     {
42991         var align = this.labelAlign || this.parentLabelAlign();
42992         
42993         var id = Roo.id();
42994
42995         var cfg = {
42996             cls: 'form-group',
42997             cn: []
42998         };
42999
43000         var input =  {
43001             tag: 'input',
43002             id : id,
43003             cls : 'form-control roo-money-amount-input',
43004             autocomplete: 'new-password'
43005         };
43006         
43007         var hiddenInput = {
43008             tag: 'input',
43009             type: 'hidden',
43010             id: Roo.id(),
43011             cls: 'hidden-number-input'
43012         };
43013         
43014         if(this.max_length) {
43015             input.maxlength = this.max_length; 
43016         }
43017         
43018         if (this.name) {
43019             hiddenInput.name = this.name;
43020         }
43021
43022         if (this.disabled) {
43023             input.disabled = true;
43024         }
43025
43026         var clg = 12 - this.inputlg;
43027         var cmd = 12 - this.inputmd;
43028         var csm = 12 - this.inputsm;
43029         var cxs = 12 - this.inputxs;
43030         
43031         var container = {
43032             tag : 'div',
43033             cls : 'row roo-money-field',
43034             cn : [
43035                 {
43036                     tag : 'div',
43037                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43038                     cn : [
43039                         {
43040                             tag : 'div',
43041                             cls: 'roo-select2-container input-group',
43042                             cn: [
43043                                 {
43044                                     tag : 'input',
43045                                     cls : 'form-control roo-money-currency-input',
43046                                     autocomplete: 'new-password',
43047                                     readOnly : 1,
43048                                     name : this.currencyName
43049                                 },
43050                                 {
43051                                     tag :'span',
43052                                     cls : 'input-group-addon',
43053                                     cn : [
43054                                         {
43055                                             tag: 'span',
43056                                             cls: 'caret'
43057                                         }
43058                                     ]
43059                                 }
43060                             ]
43061                         }
43062                     ]
43063                 },
43064                 {
43065                     tag : 'div',
43066                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43067                     cn : [
43068                         {
43069                             tag: 'div',
43070                             cls: this.hasFeedback ? 'has-feedback' : '',
43071                             cn: [
43072                                 input
43073                             ]
43074                         }
43075                     ]
43076                 }
43077             ]
43078             
43079         };
43080         
43081         if (this.fieldLabel.length) {
43082             var indicator = {
43083                 tag: 'i',
43084                 tooltip: 'This field is required'
43085             };
43086
43087             var label = {
43088                 tag: 'label',
43089                 'for':  id,
43090                 cls: 'control-label',
43091                 cn: []
43092             };
43093
43094             var label_text = {
43095                 tag: 'span',
43096                 html: this.fieldLabel
43097             };
43098
43099             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43100             label.cn = [
43101                 indicator,
43102                 label_text
43103             ];
43104
43105             if(this.indicatorpos == 'right') {
43106                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43107                 label.cn = [
43108                     label_text,
43109                     indicator
43110                 ];
43111             }
43112
43113             if(align == 'left') {
43114                 container = {
43115                     tag: 'div',
43116                     cn: [
43117                         container
43118                     ]
43119                 };
43120
43121                 if(this.labelWidth > 12){
43122                     label.style = "width: " + this.labelWidth + 'px';
43123                 }
43124                 if(this.labelWidth < 13 && this.labelmd == 0){
43125                     this.labelmd = this.labelWidth;
43126                 }
43127                 if(this.labellg > 0){
43128                     label.cls += ' col-lg-' + this.labellg;
43129                     input.cls += ' col-lg-' + (12 - this.labellg);
43130                 }
43131                 if(this.labelmd > 0){
43132                     label.cls += ' col-md-' + this.labelmd;
43133                     container.cls += ' col-md-' + (12 - this.labelmd);
43134                 }
43135                 if(this.labelsm > 0){
43136                     label.cls += ' col-sm-' + this.labelsm;
43137                     container.cls += ' col-sm-' + (12 - this.labelsm);
43138                 }
43139                 if(this.labelxs > 0){
43140                     label.cls += ' col-xs-' + this.labelxs;
43141                     container.cls += ' col-xs-' + (12 - this.labelxs);
43142                 }
43143             }
43144         }
43145
43146         cfg.cn = [
43147             label,
43148             container,
43149             hiddenInput
43150         ];
43151         
43152         var settings = this;
43153
43154         ['xs','sm','md','lg'].map(function(size){
43155             if (settings[size]) {
43156                 cfg.cls += ' col-' + size + '-' + settings[size];
43157             }
43158         });
43159         
43160         return cfg;
43161     },
43162     
43163     initEvents : function()
43164     {
43165         this.indicator = this.indicatorEl();
43166         
43167         this.initCurrencyEvent();
43168         
43169         this.initNumberEvent();
43170     },
43171     
43172     initCurrencyEvent : function()
43173     {
43174         if (!this.store) {
43175             throw "can not find store for combo";
43176         }
43177         
43178         this.store = Roo.factory(this.store, Roo.data);
43179         this.store.parent = this;
43180         
43181         this.createList();
43182         
43183         this.triggerEl = this.el.select('.input-group-addon', true).first();
43184         
43185         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43186         
43187         var _this = this;
43188         
43189         (function(){
43190             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43191             _this.list.setWidth(lw);
43192         }).defer(100);
43193         
43194         this.list.on('mouseover', this.onViewOver, this);
43195         this.list.on('mousemove', this.onViewMove, this);
43196         this.list.on('scroll', this.onViewScroll, this);
43197         
43198         if(!this.tpl){
43199             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43200         }
43201         
43202         this.view = new Roo.View(this.list, this.tpl, {
43203             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43204         });
43205         
43206         this.view.on('click', this.onViewClick, this);
43207         
43208         this.store.on('beforeload', this.onBeforeLoad, this);
43209         this.store.on('load', this.onLoad, this);
43210         this.store.on('loadexception', this.onLoadException, this);
43211         
43212         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43213             "up" : function(e){
43214                 this.inKeyMode = true;
43215                 this.selectPrev();
43216             },
43217
43218             "down" : function(e){
43219                 if(!this.isExpanded()){
43220                     this.onTriggerClick();
43221                 }else{
43222                     this.inKeyMode = true;
43223                     this.selectNext();
43224                 }
43225             },
43226
43227             "enter" : function(e){
43228                 this.collapse();
43229                 
43230                 if(this.fireEvent("specialkey", this, e)){
43231                     this.onViewClick(false);
43232                 }
43233                 
43234                 return true;
43235             },
43236
43237             "esc" : function(e){
43238                 this.collapse();
43239             },
43240
43241             "tab" : function(e){
43242                 this.collapse();
43243                 
43244                 if(this.fireEvent("specialkey", this, e)){
43245                     this.onViewClick(false);
43246                 }
43247                 
43248                 return true;
43249             },
43250
43251             scope : this,
43252
43253             doRelay : function(foo, bar, hname){
43254                 if(hname == 'down' || this.scope.isExpanded()){
43255                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43256                 }
43257                 return true;
43258             },
43259
43260             forceKeyDown: true
43261         });
43262         
43263         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43264         
43265     },
43266     
43267     initNumberEvent : function(e)
43268     {
43269         this.inputEl().on("keydown" , this.fireKey,  this);
43270         this.inputEl().on("focus", this.onFocus,  this);
43271         this.inputEl().on("blur", this.onBlur,  this);
43272         
43273         this.inputEl().relayEvent('keyup', this);
43274         
43275         if(this.indicator){
43276             this.indicator.addClass('invisible');
43277         }
43278  
43279         this.originalValue = this.getValue();
43280         
43281         if(this.validationEvent == 'keyup'){
43282             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43283             this.inputEl().on('keyup', this.filterValidation, this);
43284         }
43285         else if(this.validationEvent !== false){
43286             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43287         }
43288         
43289         if(this.selectOnFocus){
43290             this.on("focus", this.preFocus, this);
43291             
43292         }
43293         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43294             this.inputEl().on("keypress", this.filterKeys, this);
43295         } else {
43296             this.inputEl().relayEvent('keypress', this);
43297         }
43298         
43299         var allowed = "0123456789";
43300         
43301         if(this.allowDecimals){
43302             allowed += this.decimalSeparator;
43303         }
43304         
43305         if(this.allowNegative){
43306             allowed += "-";
43307         }
43308         
43309         if(this.thousandsDelimiter) {
43310             allowed += ",";
43311         }
43312         
43313         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43314         
43315         var keyPress = function(e){
43316             
43317             var k = e.getKey();
43318             
43319             var c = e.getCharCode();
43320             
43321             if(
43322                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43323                     allowed.indexOf(String.fromCharCode(c)) === -1
43324             ){
43325                 e.stopEvent();
43326                 return;
43327             }
43328             
43329             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43330                 return;
43331             }
43332             
43333             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43334                 e.stopEvent();
43335             }
43336         };
43337         
43338         this.inputEl().on("keypress", keyPress, this);
43339         
43340     },
43341     
43342     onTriggerClick : function(e)
43343     {   
43344         if(this.disabled){
43345             return;
43346         }
43347         
43348         this.page = 0;
43349         this.loadNext = false;
43350         
43351         if(this.isExpanded()){
43352             this.collapse();
43353             return;
43354         }
43355         
43356         this.hasFocus = true;
43357         
43358         if(this.triggerAction == 'all') {
43359             this.doQuery(this.allQuery, true);
43360             return;
43361         }
43362         
43363         this.doQuery(this.getRawValue());
43364     },
43365     
43366     getCurrency : function()
43367     {   
43368         var v = this.currencyEl().getValue();
43369         
43370         return v;
43371     },
43372     
43373     restrictHeight : function()
43374     {
43375         this.list.alignTo(this.currencyEl(), this.listAlign);
43376         this.list.alignTo(this.currencyEl(), this.listAlign);
43377     },
43378     
43379     onViewClick : function(view, doFocus, el, e)
43380     {
43381         var index = this.view.getSelectedIndexes()[0];
43382         
43383         var r = this.store.getAt(index);
43384         
43385         if(r){
43386             this.onSelect(r, index);
43387         }
43388     },
43389     
43390     onSelect : function(record, index){
43391         
43392         if(this.fireEvent('beforeselect', this, record, index) !== false){
43393         
43394             this.setFromCurrencyData(index > -1 ? record.data : false);
43395             
43396             this.collapse();
43397             
43398             this.fireEvent('select', this, record, index);
43399         }
43400     },
43401     
43402     setFromCurrencyData : function(o)
43403     {
43404         var currency = '';
43405         
43406         this.lastCurrency = o;
43407         
43408         if (this.currencyField) {
43409             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43410         } else {
43411             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43412         }
43413         
43414         this.lastSelectionText = currency;
43415         
43416         //setting default currency
43417         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43418             this.setCurrency(this.defaultCurrency);
43419             return;
43420         }
43421         
43422         this.setCurrency(currency);
43423     },
43424     
43425     setFromData : function(o)
43426     {
43427         var c = {};
43428         
43429         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43430         
43431         this.setFromCurrencyData(c);
43432         
43433         var value = '';
43434         
43435         if (this.name) {
43436             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43437         } else {
43438             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43439         }
43440         
43441         this.setValue(value);
43442         
43443     },
43444     
43445     setCurrency : function(v)
43446     {   
43447         this.currencyValue = v;
43448         
43449         if(this.rendered){
43450             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43451             this.validate();
43452         }
43453     },
43454     
43455     setValue : function(v)
43456     {
43457         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43458         
43459         this.value = v;
43460         
43461         if(this.rendered){
43462             
43463             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43464             
43465             this.inputEl().dom.value = (v == '') ? '' :
43466                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43467             
43468             if(!this.allowZero && v === '0') {
43469                 this.hiddenEl().dom.value = '';
43470                 this.inputEl().dom.value = '';
43471             }
43472             
43473             this.validate();
43474         }
43475     },
43476     
43477     getRawValue : function()
43478     {
43479         var v = this.inputEl().getValue();
43480         
43481         return v;
43482     },
43483     
43484     getValue : function()
43485     {
43486         return this.fixPrecision(this.parseValue(this.getRawValue()));
43487     },
43488     
43489     parseValue : function(value)
43490     {
43491         if(this.thousandsDelimiter) {
43492             value += "";
43493             r = new RegExp(",", "g");
43494             value = value.replace(r, "");
43495         }
43496         
43497         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43498         return isNaN(value) ? '' : value;
43499         
43500     },
43501     
43502     fixPrecision : function(value)
43503     {
43504         if(this.thousandsDelimiter) {
43505             value += "";
43506             r = new RegExp(",", "g");
43507             value = value.replace(r, "");
43508         }
43509         
43510         var nan = isNaN(value);
43511         
43512         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43513             return nan ? '' : value;
43514         }
43515         return parseFloat(value).toFixed(this.decimalPrecision);
43516     },
43517     
43518     decimalPrecisionFcn : function(v)
43519     {
43520         return Math.floor(v);
43521     },
43522     
43523     validateValue : function(value)
43524     {
43525         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43526             return false;
43527         }
43528         
43529         var num = this.parseValue(value);
43530         
43531         if(isNaN(num)){
43532             this.markInvalid(String.format(this.nanText, value));
43533             return false;
43534         }
43535         
43536         if(num < this.minValue){
43537             this.markInvalid(String.format(this.minText, this.minValue));
43538             return false;
43539         }
43540         
43541         if(num > this.maxValue){
43542             this.markInvalid(String.format(this.maxText, this.maxValue));
43543             return false;
43544         }
43545         
43546         return true;
43547     },
43548     
43549     validate : function()
43550     {
43551         if(this.disabled || this.allowBlank){
43552             this.markValid();
43553             return true;
43554         }
43555         
43556         var currency = this.getCurrency();
43557         
43558         if(this.validateValue(this.getRawValue()) && currency.length){
43559             this.markValid();
43560             return true;
43561         }
43562         
43563         this.markInvalid();
43564         return false;
43565     },
43566     
43567     getName: function()
43568     {
43569         return this.name;
43570     },
43571     
43572     beforeBlur : function()
43573     {
43574         if(!this.castInt){
43575             return;
43576         }
43577         
43578         var v = this.parseValue(this.getRawValue());
43579         
43580         if(v || v == 0){
43581             this.setValue(v);
43582         }
43583     },
43584     
43585     onBlur : function()
43586     {
43587         this.beforeBlur();
43588         
43589         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43590             //this.el.removeClass(this.focusClass);
43591         }
43592         
43593         this.hasFocus = false;
43594         
43595         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43596             this.validate();
43597         }
43598         
43599         var v = this.getValue();
43600         
43601         if(String(v) !== String(this.startValue)){
43602             this.fireEvent('change', this, v, this.startValue);
43603         }
43604         
43605         this.fireEvent("blur", this);
43606     },
43607     
43608     inputEl : function()
43609     {
43610         return this.el.select('.roo-money-amount-input', true).first();
43611     },
43612     
43613     currencyEl : function()
43614     {
43615         return this.el.select('.roo-money-currency-input', true).first();
43616     },
43617     
43618     hiddenEl : function()
43619     {
43620         return this.el.select('input.hidden-number-input',true).first();
43621     }
43622     
43623 });/**
43624  * @class Roo.bootstrap.BezierSignature
43625  * @extends Roo.bootstrap.Component
43626  * Bootstrap BezierSignature class
43627  * This script refer to:
43628  *    Title: Signature Pad
43629  *    Author: szimek
43630  *    Availability: https://github.com/szimek/signature_pad
43631  *
43632  * @constructor
43633  * Create a new BezierSignature
43634  * @param {Object} config The config object
43635  */
43636
43637 Roo.bootstrap.BezierSignature = function(config){
43638     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43639     this.addEvents({
43640         "resize" : true
43641     });
43642 };
43643
43644 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43645 {
43646      
43647     curve_data: [],
43648     
43649     is_empty: true,
43650     
43651     mouse_btn_down: true,
43652     
43653     /**
43654      * @cfg {int} canvas height
43655      */
43656     canvas_height: '200px',
43657     
43658     /**
43659      * @cfg {float|function} Radius of a single dot.
43660      */ 
43661     dot_size: false,
43662     
43663     /**
43664      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43665      */
43666     min_width: 0.5,
43667     
43668     /**
43669      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43670      */
43671     max_width: 2.5,
43672     
43673     /**
43674      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43675      */
43676     throttle: 16,
43677     
43678     /**
43679      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43680      */
43681     min_distance: 5,
43682     
43683     /**
43684      * @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.
43685      */
43686     bg_color: 'rgba(0, 0, 0, 0)',
43687     
43688     /**
43689      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43690      */
43691     dot_color: 'black',
43692     
43693     /**
43694      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43695      */ 
43696     velocity_filter_weight: 0.7,
43697     
43698     /**
43699      * @cfg {function} Callback when stroke begin. 
43700      */
43701     onBegin: false,
43702     
43703     /**
43704      * @cfg {function} Callback when stroke end.
43705      */
43706     onEnd: false,
43707     
43708     getAutoCreate : function()
43709     {
43710         var cls = 'roo-signature column';
43711         
43712         if(this.cls){
43713             cls += ' ' + this.cls;
43714         }
43715         
43716         var col_sizes = [
43717             'lg',
43718             'md',
43719             'sm',
43720             'xs'
43721         ];
43722         
43723         for(var i = 0; i < col_sizes.length; i++) {
43724             if(this[col_sizes[i]]) {
43725                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43726             }
43727         }
43728         
43729         var cfg = {
43730             tag: 'div',
43731             cls: cls,
43732             cn: [
43733                 {
43734                     tag: 'div',
43735                     cls: 'roo-signature-body',
43736                     cn: [
43737                         {
43738                             tag: 'canvas',
43739                             cls: 'roo-signature-body-canvas',
43740                             height: this.canvas_height,
43741                             width: this.canvas_width
43742                         }
43743                     ]
43744                 },
43745                 {
43746                     tag: 'input',
43747                     type: 'file',
43748                     style: 'display: none'
43749                 }
43750             ]
43751         };
43752         
43753         return cfg;
43754     },
43755     
43756     initEvents: function() 
43757     {
43758         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43759         
43760         var canvas = this.canvasEl();
43761         
43762         // mouse && touch event swapping...
43763         canvas.dom.style.touchAction = 'none';
43764         canvas.dom.style.msTouchAction = 'none';
43765         
43766         this.mouse_btn_down = false;
43767         canvas.on('mousedown', this._handleMouseDown, this);
43768         canvas.on('mousemove', this._handleMouseMove, this);
43769         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43770         
43771         if (window.PointerEvent) {
43772             canvas.on('pointerdown', this._handleMouseDown, this);
43773             canvas.on('pointermove', this._handleMouseMove, this);
43774             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43775         }
43776         
43777         if ('ontouchstart' in window) {
43778             canvas.on('touchstart', this._handleTouchStart, this);
43779             canvas.on('touchmove', this._handleTouchMove, this);
43780             canvas.on('touchend', this._handleTouchEnd, this);
43781         }
43782         
43783         Roo.EventManager.onWindowResize(this.resize, this, true);
43784         
43785         // file input event
43786         this.fileEl().on('change', this.uploadImage, this);
43787         
43788         this.clear();
43789         
43790         this.resize();
43791     },
43792     
43793     resize: function(){
43794         
43795         var canvas = this.canvasEl().dom;
43796         var ctx = this.canvasElCtx();
43797         var img_data = false;
43798         
43799         if(canvas.width > 0) {
43800             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43801         }
43802         // setting canvas width will clean img data
43803         canvas.width = 0;
43804         
43805         var style = window.getComputedStyle ? 
43806             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43807             
43808         var padding_left = parseInt(style.paddingLeft) || 0;
43809         var padding_right = parseInt(style.paddingRight) || 0;
43810         
43811         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43812         
43813         if(img_data) {
43814             ctx.putImageData(img_data, 0, 0);
43815         }
43816     },
43817     
43818     _handleMouseDown: function(e)
43819     {
43820         if (e.browserEvent.which === 1) {
43821             this.mouse_btn_down = true;
43822             this.strokeBegin(e);
43823         }
43824     },
43825     
43826     _handleMouseMove: function (e)
43827     {
43828         if (this.mouse_btn_down) {
43829             this.strokeMoveUpdate(e);
43830         }
43831     },
43832     
43833     _handleMouseUp: function (e)
43834     {
43835         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43836             this.mouse_btn_down = false;
43837             this.strokeEnd(e);
43838         }
43839     },
43840     
43841     _handleTouchStart: function (e) {
43842         
43843         e.preventDefault();
43844         if (e.browserEvent.targetTouches.length === 1) {
43845             // var touch = e.browserEvent.changedTouches[0];
43846             // this.strokeBegin(touch);
43847             
43848              this.strokeBegin(e); // assume e catching the correct xy...
43849         }
43850     },
43851     
43852     _handleTouchMove: function (e) {
43853         e.preventDefault();
43854         // var touch = event.targetTouches[0];
43855         // _this._strokeMoveUpdate(touch);
43856         this.strokeMoveUpdate(e);
43857     },
43858     
43859     _handleTouchEnd: function (e) {
43860         var wasCanvasTouched = e.target === this.canvasEl().dom;
43861         if (wasCanvasTouched) {
43862             e.preventDefault();
43863             // var touch = event.changedTouches[0];
43864             // _this._strokeEnd(touch);
43865             this.strokeEnd(e);
43866         }
43867     },
43868     
43869     reset: function () {
43870         this._lastPoints = [];
43871         this._lastVelocity = 0;
43872         this._lastWidth = (this.min_width + this.max_width) / 2;
43873         this.canvasElCtx().fillStyle = this.dot_color;
43874     },
43875     
43876     strokeMoveUpdate: function(e)
43877     {
43878         this.strokeUpdate(e);
43879         
43880         if (this.throttle) {
43881             this.throttleStroke(this.strokeUpdate, this.throttle);
43882         }
43883         else {
43884             this.strokeUpdate(e);
43885         }
43886     },
43887     
43888     strokeBegin: function(e)
43889     {
43890         var newPointGroup = {
43891             color: this.dot_color,
43892             points: []
43893         };
43894         
43895         if (typeof this.onBegin === 'function') {
43896             this.onBegin(e);
43897         }
43898         
43899         this.curve_data.push(newPointGroup);
43900         this.reset();
43901         this.strokeUpdate(e);
43902     },
43903     
43904     strokeUpdate: function(e)
43905     {
43906         var rect = this.canvasEl().dom.getBoundingClientRect();
43907         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43908         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43909         var lastPoints = lastPointGroup.points;
43910         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43911         var isLastPointTooClose = lastPoint
43912             ? point.distanceTo(lastPoint) <= this.min_distance
43913             : false;
43914         var color = lastPointGroup.color;
43915         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43916             var curve = this.addPoint(point);
43917             if (!lastPoint) {
43918                 this.drawDot({color: color, point: point});
43919             }
43920             else if (curve) {
43921                 this.drawCurve({color: color, curve: curve});
43922             }
43923             lastPoints.push({
43924                 time: point.time,
43925                 x: point.x,
43926                 y: point.y
43927             });
43928         }
43929     },
43930     
43931     strokeEnd: function(e)
43932     {
43933         this.strokeUpdate(e);
43934         if (typeof this.onEnd === 'function') {
43935             this.onEnd(e);
43936         }
43937     },
43938     
43939     addPoint:  function (point) {
43940         var _lastPoints = this._lastPoints;
43941         _lastPoints.push(point);
43942         if (_lastPoints.length > 2) {
43943             if (_lastPoints.length === 3) {
43944                 _lastPoints.unshift(_lastPoints[0]);
43945             }
43946             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43947             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43948             _lastPoints.shift();
43949             return curve;
43950         }
43951         return null;
43952     },
43953     
43954     calculateCurveWidths: function (startPoint, endPoint) {
43955         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43956             (1 - this.velocity_filter_weight) * this._lastVelocity;
43957
43958         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43959         var widths = {
43960             end: newWidth,
43961             start: this._lastWidth
43962         };
43963         
43964         this._lastVelocity = velocity;
43965         this._lastWidth = newWidth;
43966         return widths;
43967     },
43968     
43969     drawDot: function (_a) {
43970         var color = _a.color, point = _a.point;
43971         var ctx = this.canvasElCtx();
43972         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43973         ctx.beginPath();
43974         this.drawCurveSegment(point.x, point.y, width);
43975         ctx.closePath();
43976         ctx.fillStyle = color;
43977         ctx.fill();
43978     },
43979     
43980     drawCurve: function (_a) {
43981         var color = _a.color, curve = _a.curve;
43982         var ctx = this.canvasElCtx();
43983         var widthDelta = curve.endWidth - curve.startWidth;
43984         var drawSteps = Math.floor(curve.length()) * 2;
43985         ctx.beginPath();
43986         ctx.fillStyle = color;
43987         for (var i = 0; i < drawSteps; i += 1) {
43988         var t = i / drawSteps;
43989         var tt = t * t;
43990         var ttt = tt * t;
43991         var u = 1 - t;
43992         var uu = u * u;
43993         var uuu = uu * u;
43994         var x = uuu * curve.startPoint.x;
43995         x += 3 * uu * t * curve.control1.x;
43996         x += 3 * u * tt * curve.control2.x;
43997         x += ttt * curve.endPoint.x;
43998         var y = uuu * curve.startPoint.y;
43999         y += 3 * uu * t * curve.control1.y;
44000         y += 3 * u * tt * curve.control2.y;
44001         y += ttt * curve.endPoint.y;
44002         var width = curve.startWidth + ttt * widthDelta;
44003         this.drawCurveSegment(x, y, width);
44004         }
44005         ctx.closePath();
44006         ctx.fill();
44007     },
44008     
44009     drawCurveSegment: function (x, y, width) {
44010         var ctx = this.canvasElCtx();
44011         ctx.moveTo(x, y);
44012         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44013         this.is_empty = false;
44014     },
44015     
44016     clear: function()
44017     {
44018         var ctx = this.canvasElCtx();
44019         var canvas = this.canvasEl().dom;
44020         ctx.fillStyle = this.bg_color;
44021         ctx.clearRect(0, 0, canvas.width, canvas.height);
44022         ctx.fillRect(0, 0, canvas.width, canvas.height);
44023         this.curve_data = [];
44024         this.reset();
44025         this.is_empty = true;
44026     },
44027     
44028     fileEl: function()
44029     {
44030         return  this.el.select('input',true).first();
44031     },
44032     
44033     canvasEl: function()
44034     {
44035         return this.el.select('canvas',true).first();
44036     },
44037     
44038     canvasElCtx: function()
44039     {
44040         return this.el.select('canvas',true).first().dom.getContext('2d');
44041     },
44042     
44043     getImage: function(type)
44044     {
44045         if(this.is_empty) {
44046             return false;
44047         }
44048         
44049         // encryption ?
44050         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44051     },
44052     
44053     drawFromImage: function(img_src)
44054     {
44055         var img = new Image();
44056         
44057         img.onload = function(){
44058             this.canvasElCtx().drawImage(img, 0, 0);
44059         }.bind(this);
44060         
44061         img.src = img_src;
44062         
44063         this.is_empty = false;
44064     },
44065     
44066     selectImage: function()
44067     {
44068         this.fileEl().dom.click();
44069     },
44070     
44071     uploadImage: function(e)
44072     {
44073         var reader = new FileReader();
44074         
44075         reader.onload = function(e){
44076             var img = new Image();
44077             img.onload = function(){
44078                 this.reset();
44079                 this.canvasElCtx().drawImage(img, 0, 0);
44080             }.bind(this);
44081             img.src = e.target.result;
44082         }.bind(this);
44083         
44084         reader.readAsDataURL(e.target.files[0]);
44085     },
44086     
44087     // Bezier Point Constructor
44088     Point: (function () {
44089         function Point(x, y, time) {
44090             this.x = x;
44091             this.y = y;
44092             this.time = time || Date.now();
44093         }
44094         Point.prototype.distanceTo = function (start) {
44095             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44096         };
44097         Point.prototype.equals = function (other) {
44098             return this.x === other.x && this.y === other.y && this.time === other.time;
44099         };
44100         Point.prototype.velocityFrom = function (start) {
44101             return this.time !== start.time
44102             ? this.distanceTo(start) / (this.time - start.time)
44103             : 0;
44104         };
44105         return Point;
44106     }()),
44107     
44108     
44109     // Bezier Constructor
44110     Bezier: (function () {
44111         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44112             this.startPoint = startPoint;
44113             this.control2 = control2;
44114             this.control1 = control1;
44115             this.endPoint = endPoint;
44116             this.startWidth = startWidth;
44117             this.endWidth = endWidth;
44118         }
44119         Bezier.fromPoints = function (points, widths, scope) {
44120             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44121             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44122             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44123         };
44124         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44125             var dx1 = s1.x - s2.x;
44126             var dy1 = s1.y - s2.y;
44127             var dx2 = s2.x - s3.x;
44128             var dy2 = s2.y - s3.y;
44129             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44130             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44131             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44132             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44133             var dxm = m1.x - m2.x;
44134             var dym = m1.y - m2.y;
44135             var k = l2 / (l1 + l2);
44136             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44137             var tx = s2.x - cm.x;
44138             var ty = s2.y - cm.y;
44139             return {
44140                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44141                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44142             };
44143         };
44144         Bezier.prototype.length = function () {
44145             var steps = 10;
44146             var length = 0;
44147             var px;
44148             var py;
44149             for (var i = 0; i <= steps; i += 1) {
44150                 var t = i / steps;
44151                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44152                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44153                 if (i > 0) {
44154                     var xdiff = cx - px;
44155                     var ydiff = cy - py;
44156                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44157                 }
44158                 px = cx;
44159                 py = cy;
44160             }
44161             return length;
44162         };
44163         Bezier.prototype.point = function (t, start, c1, c2, end) {
44164             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44165             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44166             + (3.0 * c2 * (1.0 - t) * t * t)
44167             + (end * t * t * t);
44168         };
44169         return Bezier;
44170     }()),
44171     
44172     throttleStroke: function(fn, wait) {
44173       if (wait === void 0) { wait = 250; }
44174       var previous = 0;
44175       var timeout = null;
44176       var result;
44177       var storedContext;
44178       var storedArgs;
44179       var later = function () {
44180           previous = Date.now();
44181           timeout = null;
44182           result = fn.apply(storedContext, storedArgs);
44183           if (!timeout) {
44184               storedContext = null;
44185               storedArgs = [];
44186           }
44187       };
44188       return function wrapper() {
44189           var args = [];
44190           for (var _i = 0; _i < arguments.length; _i++) {
44191               args[_i] = arguments[_i];
44192           }
44193           var now = Date.now();
44194           var remaining = wait - (now - previous);
44195           storedContext = this;
44196           storedArgs = args;
44197           if (remaining <= 0 || remaining > wait) {
44198               if (timeout) {
44199                   clearTimeout(timeout);
44200                   timeout = null;
44201               }
44202               previous = now;
44203               result = fn.apply(storedContext, storedArgs);
44204               if (!timeout) {
44205                   storedContext = null;
44206                   storedArgs = [];
44207               }
44208           }
44209           else if (!timeout) {
44210               timeout = window.setTimeout(later, remaining);
44211           }
44212           return result;
44213       };
44214   }
44215   
44216 });
44217
44218  
44219
44220