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 {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19608  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19609  *      - if false and it has a 'parent' then it will be automatically added to that element
19610  *      - if string - Roo.get  will be called 
19611  * @cfg {Number} delay - delay before showing
19612  
19613  * @constructor
19614  * Create a new Popover
19615  * @param {Object} config The config object
19616  */
19617
19618 Roo.bootstrap.Popover = function(config){
19619     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19620     
19621     this.addEvents({
19622         // raw events
19623          /**
19624          * @event show
19625          * After the popover show
19626          * 
19627          * @param {Roo.bootstrap.Popover} this
19628          */
19629         "show" : true,
19630         /**
19631          * @event hide
19632          * After the popover hide
19633          * 
19634          * @param {Roo.bootstrap.Popover} this
19635          */
19636         "hide" : true
19637     });
19638 };
19639
19640 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19641     
19642     title: false,
19643     html: false,
19644     
19645     placement : 'right',
19646     trigger : 'hover', // hover
19647     modal : false,
19648     delay : 0,
19649     
19650     over: false,
19651     
19652     can_build_overlaid : false,
19653     
19654     maskEl : false, // the mask element
19655     
19656     getChildContainer : function()
19657     {
19658         return this.el.select('.popover-content',true).first();
19659     },
19660     
19661     getAutoCreate : function(){
19662          
19663         var cfg = {
19664            cls : 'popover roo-dynamic shadow roo-popover',
19665            style: 'display:block',
19666            cn : [
19667                 {
19668                     cls : 'arrow'
19669                 },
19670                 {
19671                     cls : 'popover-inner ',
19672                     cn : [
19673                         {
19674                             tag: 'h3',
19675                             cls: 'popover-title popover-header',
19676                             html : this.title || ''
19677                         },
19678                         {
19679                             cls : 'popover-content popover-body'  + this.cls,
19680                             html : this.html || ''
19681                         }
19682                     ]
19683                     
19684                 }
19685            ]
19686         };
19687         
19688         return cfg;
19689     },
19690     /**
19691      * @param {string} the title
19692      */
19693     setTitle: function(str)
19694     {
19695         this.title = str;
19696         if (this.el) {
19697             this.el.select('.popover-title',true).first().dom.innerHTML = str;
19698         }
19699         
19700     },
19701     /**
19702      * @param {string} the body content
19703      */
19704     setContent: function(str)
19705     {
19706         this.html = str;
19707         if (this.el) {
19708             this.el.select('.popover-content',true).first().dom.innerHTML = str;
19709         }
19710         
19711     },
19712     // as it get's added to the bottom of the page.
19713     onRender : function(ct, position)
19714     {
19715         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19716         if(!this.el){
19717             var cfg = Roo.apply({},  this.getAutoCreate());
19718             cfg.id = Roo.id();
19719             
19720             if (this.cls) {
19721                 cfg.cls += ' ' + this.cls;
19722             }
19723             if (this.style) {
19724                 cfg.style = this.style;
19725             }
19726             //Roo.log("adding to ");
19727             this.el = Roo.get(document.body).createChild(cfg, position);
19728 //            Roo.log(this.el);
19729         }
19730         
19731         var nitems = [];
19732         if(typeof(this.items) != 'undefined'){
19733             var items = this.items;
19734             delete this.items;
19735
19736             for(var i =0;i < items.length;i++) {
19737                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19738             }
19739         }
19740
19741         this.items = nitems;
19742         
19743         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19744         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19745         
19746         
19747         this.initEvents();
19748     },
19749     
19750     resizeMask : function()
19751     {
19752         this.maskEl.setSize(
19753             Roo.lib.Dom.getViewWidth(true),
19754             Roo.lib.Dom.getViewHeight(true)
19755         );
19756     }
19757     
19758     initEvents : function()
19759     {
19760         
19761         Roo.bootstrap.Popover.register(this);
19762         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19763         this.el.enableDisplayMode('block');
19764         this.el.hide();
19765         if (this.over === false && !this.parent()) {
19766             return; 
19767         }
19768         if (this.triggers === false) {
19769             return;
19770         }
19771          
19772         // support parent
19773         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19774         var triggers = this.trigger ? this.trigger.split(' ') : [];
19775         Roo.each(triggers, function(trigger) {
19776         
19777             if (trigger == 'click') {
19778                 on_el.on('click', this.toggle, this);
19779             } else if (trigger != 'manual') {
19780                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19781                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19782       
19783                 on_el.on(eventIn  ,this.enter, this);
19784                 on_el.on(eventOut, this.leave, this);
19785             }
19786         }, this);
19787         
19788     },
19789     
19790     
19791     // private
19792     timeout : null,
19793     hoverState : null,
19794     
19795     toggle : function () {
19796         this.hoverState == 'in' ? this.leave() : this.enter();
19797     },
19798     
19799     enter : function () {
19800         
19801         clearTimeout(this.timeout);
19802     
19803         this.hoverState = 'in';
19804     
19805         if (!this.delay || !this.delay.show) {
19806             this.show();
19807             return;
19808         }
19809         var _t = this;
19810         this.timeout = setTimeout(function () {
19811             if (_t.hoverState == 'in') {
19812                 _t.show();
19813             }
19814         }, this.delay.show)
19815     },
19816     
19817     leave : function() {
19818         clearTimeout(this.timeout);
19819     
19820         this.hoverState = 'out';
19821     
19822         if (!this.delay || !this.delay.hide) {
19823             this.hide();
19824             return;
19825         }
19826         var _t = this;
19827         this.timeout = setTimeout(function () {
19828             if (_t.hoverState == 'out') {
19829                 _t.hide();
19830             }
19831         }, this.delay.hide)
19832     },
19833     /**
19834      * Show the popover
19835      * @param {Roo.Element|string|false} - element to align and point to.
19836      */
19837     show : function (on_el)
19838     {
19839         
19840         on_el = on_el || false; // default to false
19841         if (!on_el) {
19842             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19843                 on_el = this.parent().el;
19844             } else if (this.over) {
19845                 Roo.get(this.over);
19846             }
19847             
19848         }
19849         
19850         if (!this.el) {
19851             this.render(document.body);
19852         }
19853         
19854         
19855         this.el.removeClass([
19856             'fade','top','bottom', 'left', 'right','in',
19857             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19858         ]);
19859         
19860         if (!this.title.length) {
19861             this.el.select('.popover-title',true).hide();
19862         }
19863         
19864         
19865         var placement = typeof this.placement == 'function' ?
19866             this.placement.call(this, this.el, on_el) :
19867             this.placement;
19868             
19869         /*
19870         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19871         
19872         // I think  'auto right' - but 
19873         
19874         var autoPlace = autoToken.test(placement);
19875         if (autoPlace) {
19876             placement = placement.replace(autoToken, '') || 'top';
19877         }
19878         */
19879         
19880         
19881         this.el.show();
19882         this.el.dom.style.display='block';
19883         
19884         //this.el.appendTo(on_el);
19885         
19886         var p = this.getPosition();
19887         var box = this.el.getBox();
19888         
19889         
19890         var align = Roo.bootstrap.Popover.alignment[placement];
19891         this.el.addClass(align[2]);
19892
19893 //        Roo.log(align);
19894
19895         if (on_el) {
19896             this.el.alignTo(on_el, align[0],align[1]);
19897         } else {
19898             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19899             var es = this.el.getSize();
19900             var x = Roo.lib.Dom.getViewWidth()/2;
19901             var y = Roo.lib.Dom.getViewHeight()/2;
19902             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19903             
19904         }
19905
19906         
19907         //var arrow = this.el.select('.arrow',true).first();
19908         //arrow.set(align[2], 
19909         
19910         this.el.addClass('in');
19911         
19912         
19913         if (this.el.hasClass('fade')) {
19914             // fade it?
19915         }
19916         
19917         this.hoverState = 'in';
19918         
19919         this.fireEvent('show', this);
19920         
19921     },
19922     hide : function()
19923     {
19924         this.el.setXY([0,0]);
19925         this.el.removeClass('in');
19926         this.el.hide();
19927         this.hoverState = null;
19928         
19929         this.fireEvent('hide', this);
19930     }
19931     
19932 });
19933
19934
19935 Roo.apply(Roo.bootstrap.Popover, {
19936
19937     alignment : {
19938         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19939         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19940         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19941         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19942     },
19943
19944     clickHander : false,
19945     
19946
19947     onMouseDown : function(e)
19948     {
19949         if (!e.getTarget(".roo-popover")) {
19950             this.hideAll();
19951         }
19952          
19953     },
19954     
19955     popups : [],
19956     
19957     register : function(popup)
19958     {
19959         if (!Roo.bootstrap.Popover.clickHandler) {
19960             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19961         }
19962         // hide other popups.
19963         this.hideAll();
19964         this.popups.push(popup);
19965     },
19966     hideAll : function()
19967     {
19968         this.popups.forEach(function(p) {
19969             p.hide();
19970         });
19971     }
19972
19973 });/*
19974  * - LGPL
19975  *
19976  * Progress
19977  * 
19978  */
19979
19980 /**
19981  * @class Roo.bootstrap.Progress
19982  * @extends Roo.bootstrap.Component
19983  * Bootstrap Progress class
19984  * @cfg {Boolean} striped striped of the progress bar
19985  * @cfg {Boolean} active animated of the progress bar
19986  * 
19987  * 
19988  * @constructor
19989  * Create a new Progress
19990  * @param {Object} config The config object
19991  */
19992
19993 Roo.bootstrap.Progress = function(config){
19994     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19995 };
19996
19997 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19998     
19999     striped : false,
20000     active: false,
20001     
20002     getAutoCreate : function(){
20003         var cfg = {
20004             tag: 'div',
20005             cls: 'progress'
20006         };
20007         
20008         
20009         if(this.striped){
20010             cfg.cls += ' progress-striped';
20011         }
20012       
20013         if(this.active){
20014             cfg.cls += ' active';
20015         }
20016         
20017         
20018         return cfg;
20019     }
20020    
20021 });
20022
20023  
20024
20025  /*
20026  * - LGPL
20027  *
20028  * ProgressBar
20029  * 
20030  */
20031
20032 /**
20033  * @class Roo.bootstrap.ProgressBar
20034  * @extends Roo.bootstrap.Component
20035  * Bootstrap ProgressBar class
20036  * @cfg {Number} aria_valuenow aria-value now
20037  * @cfg {Number} aria_valuemin aria-value min
20038  * @cfg {Number} aria_valuemax aria-value max
20039  * @cfg {String} label label for the progress bar
20040  * @cfg {String} panel (success | info | warning | danger )
20041  * @cfg {String} role role of the progress bar
20042  * @cfg {String} sr_only text
20043  * 
20044  * 
20045  * @constructor
20046  * Create a new ProgressBar
20047  * @param {Object} config The config object
20048  */
20049
20050 Roo.bootstrap.ProgressBar = function(config){
20051     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20052 };
20053
20054 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20055     
20056     aria_valuenow : 0,
20057     aria_valuemin : 0,
20058     aria_valuemax : 100,
20059     label : false,
20060     panel : false,
20061     role : false,
20062     sr_only: false,
20063     
20064     getAutoCreate : function()
20065     {
20066         
20067         var cfg = {
20068             tag: 'div',
20069             cls: 'progress-bar',
20070             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20071         };
20072         
20073         if(this.sr_only){
20074             cfg.cn = {
20075                 tag: 'span',
20076                 cls: 'sr-only',
20077                 html: this.sr_only
20078             }
20079         }
20080         
20081         if(this.role){
20082             cfg.role = this.role;
20083         }
20084         
20085         if(this.aria_valuenow){
20086             cfg['aria-valuenow'] = this.aria_valuenow;
20087         }
20088         
20089         if(this.aria_valuemin){
20090             cfg['aria-valuemin'] = this.aria_valuemin;
20091         }
20092         
20093         if(this.aria_valuemax){
20094             cfg['aria-valuemax'] = this.aria_valuemax;
20095         }
20096         
20097         if(this.label && !this.sr_only){
20098             cfg.html = this.label;
20099         }
20100         
20101         if(this.panel){
20102             cfg.cls += ' progress-bar-' + this.panel;
20103         }
20104         
20105         return cfg;
20106     },
20107     
20108     update : function(aria_valuenow)
20109     {
20110         this.aria_valuenow = aria_valuenow;
20111         
20112         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20113     }
20114    
20115 });
20116
20117  
20118
20119  /*
20120  * - LGPL
20121  *
20122  * column
20123  * 
20124  */
20125
20126 /**
20127  * @class Roo.bootstrap.TabGroup
20128  * @extends Roo.bootstrap.Column
20129  * Bootstrap Column class
20130  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20131  * @cfg {Boolean} carousel true to make the group behave like a carousel
20132  * @cfg {Boolean} bullets show bullets for the panels
20133  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20134  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20135  * @cfg {Boolean} showarrow (true|false) show arrow default true
20136  * 
20137  * @constructor
20138  * Create a new TabGroup
20139  * @param {Object} config The config object
20140  */
20141
20142 Roo.bootstrap.TabGroup = function(config){
20143     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20144     if (!this.navId) {
20145         this.navId = Roo.id();
20146     }
20147     this.tabs = [];
20148     Roo.bootstrap.TabGroup.register(this);
20149     
20150 };
20151
20152 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20153     
20154     carousel : false,
20155     transition : false,
20156     bullets : 0,
20157     timer : 0,
20158     autoslide : false,
20159     slideFn : false,
20160     slideOnTouch : false,
20161     showarrow : true,
20162     
20163     getAutoCreate : function()
20164     {
20165         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20166         
20167         cfg.cls += ' tab-content';
20168         
20169         if (this.carousel) {
20170             cfg.cls += ' carousel slide';
20171             
20172             cfg.cn = [{
20173                cls : 'carousel-inner',
20174                cn : []
20175             }];
20176         
20177             if(this.bullets  && !Roo.isTouch){
20178                 
20179                 var bullets = {
20180                     cls : 'carousel-bullets',
20181                     cn : []
20182                 };
20183                
20184                 if(this.bullets_cls){
20185                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20186                 }
20187                 
20188                 bullets.cn.push({
20189                     cls : 'clear'
20190                 });
20191                 
20192                 cfg.cn[0].cn.push(bullets);
20193             }
20194             
20195             if(this.showarrow){
20196                 cfg.cn[0].cn.push({
20197                     tag : 'div',
20198                     class : 'carousel-arrow',
20199                     cn : [
20200                         {
20201                             tag : 'div',
20202                             class : 'carousel-prev',
20203                             cn : [
20204                                 {
20205                                     tag : 'i',
20206                                     class : 'fa fa-chevron-left'
20207                                 }
20208                             ]
20209                         },
20210                         {
20211                             tag : 'div',
20212                             class : 'carousel-next',
20213                             cn : [
20214                                 {
20215                                     tag : 'i',
20216                                     class : 'fa fa-chevron-right'
20217                                 }
20218                             ]
20219                         }
20220                     ]
20221                 });
20222             }
20223             
20224         }
20225         
20226         return cfg;
20227     },
20228     
20229     initEvents:  function()
20230     {
20231 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20232 //            this.el.on("touchstart", this.onTouchStart, this);
20233 //        }
20234         
20235         if(this.autoslide){
20236             var _this = this;
20237             
20238             this.slideFn = window.setInterval(function() {
20239                 _this.showPanelNext();
20240             }, this.timer);
20241         }
20242         
20243         if(this.showarrow){
20244             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20245             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20246         }
20247         
20248         
20249     },
20250     
20251 //    onTouchStart : function(e, el, o)
20252 //    {
20253 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20254 //            return;
20255 //        }
20256 //        
20257 //        this.showPanelNext();
20258 //    },
20259     
20260     
20261     getChildContainer : function()
20262     {
20263         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20264     },
20265     
20266     /**
20267     * register a Navigation item
20268     * @param {Roo.bootstrap.NavItem} the navitem to add
20269     */
20270     register : function(item)
20271     {
20272         this.tabs.push( item);
20273         item.navId = this.navId; // not really needed..
20274         this.addBullet();
20275     
20276     },
20277     
20278     getActivePanel : function()
20279     {
20280         var r = false;
20281         Roo.each(this.tabs, function(t) {
20282             if (t.active) {
20283                 r = t;
20284                 return false;
20285             }
20286             return null;
20287         });
20288         return r;
20289         
20290     },
20291     getPanelByName : function(n)
20292     {
20293         var r = false;
20294         Roo.each(this.tabs, function(t) {
20295             if (t.tabId == n) {
20296                 r = t;
20297                 return false;
20298             }
20299             return null;
20300         });
20301         return r;
20302     },
20303     indexOfPanel : function(p)
20304     {
20305         var r = false;
20306         Roo.each(this.tabs, function(t,i) {
20307             if (t.tabId == p.tabId) {
20308                 r = i;
20309                 return false;
20310             }
20311             return null;
20312         });
20313         return r;
20314     },
20315     /**
20316      * show a specific panel
20317      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20318      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20319      */
20320     showPanel : function (pan)
20321     {
20322         if(this.transition || typeof(pan) == 'undefined'){
20323             Roo.log("waiting for the transitionend");
20324             return false;
20325         }
20326         
20327         if (typeof(pan) == 'number') {
20328             pan = this.tabs[pan];
20329         }
20330         
20331         if (typeof(pan) == 'string') {
20332             pan = this.getPanelByName(pan);
20333         }
20334         
20335         var cur = this.getActivePanel();
20336         
20337         if(!pan || !cur){
20338             Roo.log('pan or acitve pan is undefined');
20339             return false;
20340         }
20341         
20342         if (pan.tabId == this.getActivePanel().tabId) {
20343             return true;
20344         }
20345         
20346         if (false === cur.fireEvent('beforedeactivate')) {
20347             return false;
20348         }
20349         
20350         if(this.bullets > 0 && !Roo.isTouch){
20351             this.setActiveBullet(this.indexOfPanel(pan));
20352         }
20353         
20354         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20355             
20356             //class="carousel-item carousel-item-next carousel-item-left"
20357             
20358             this.transition = true;
20359             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20360             var lr = dir == 'next' ? 'left' : 'right';
20361             pan.el.addClass(dir); // or prev
20362             pan.el.addClass('carousel-item-' + dir); // or prev
20363             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20364             cur.el.addClass(lr); // or right
20365             pan.el.addClass(lr);
20366             cur.el.addClass('carousel-item-' +lr); // or right
20367             pan.el.addClass('carousel-item-' +lr);
20368             
20369             
20370             var _this = this;
20371             cur.el.on('transitionend', function() {
20372                 Roo.log("trans end?");
20373                 
20374                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20375                 pan.setActive(true);
20376                 
20377                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20378                 cur.setActive(false);
20379                 
20380                 _this.transition = false;
20381                 
20382             }, this, { single:  true } );
20383             
20384             return true;
20385         }
20386         
20387         cur.setActive(false);
20388         pan.setActive(true);
20389         
20390         return true;
20391         
20392     },
20393     showPanelNext : function()
20394     {
20395         var i = this.indexOfPanel(this.getActivePanel());
20396         
20397         if (i >= this.tabs.length - 1 && !this.autoslide) {
20398             return;
20399         }
20400         
20401         if (i >= this.tabs.length - 1 && this.autoslide) {
20402             i = -1;
20403         }
20404         
20405         this.showPanel(this.tabs[i+1]);
20406     },
20407     
20408     showPanelPrev : function()
20409     {
20410         var i = this.indexOfPanel(this.getActivePanel());
20411         
20412         if (i  < 1 && !this.autoslide) {
20413             return;
20414         }
20415         
20416         if (i < 1 && this.autoslide) {
20417             i = this.tabs.length;
20418         }
20419         
20420         this.showPanel(this.tabs[i-1]);
20421     },
20422     
20423     
20424     addBullet: function()
20425     {
20426         if(!this.bullets || Roo.isTouch){
20427             return;
20428         }
20429         var ctr = this.el.select('.carousel-bullets',true).first();
20430         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20431         var bullet = ctr.createChild({
20432             cls : 'bullet bullet-' + i
20433         },ctr.dom.lastChild);
20434         
20435         
20436         var _this = this;
20437         
20438         bullet.on('click', (function(e, el, o, ii, t){
20439
20440             e.preventDefault();
20441
20442             this.showPanel(ii);
20443
20444             if(this.autoslide && this.slideFn){
20445                 clearInterval(this.slideFn);
20446                 this.slideFn = window.setInterval(function() {
20447                     _this.showPanelNext();
20448                 }, this.timer);
20449             }
20450
20451         }).createDelegate(this, [i, bullet], true));
20452                 
20453         
20454     },
20455      
20456     setActiveBullet : function(i)
20457     {
20458         if(Roo.isTouch){
20459             return;
20460         }
20461         
20462         Roo.each(this.el.select('.bullet', true).elements, function(el){
20463             el.removeClass('selected');
20464         });
20465
20466         var bullet = this.el.select('.bullet-' + i, true).first();
20467         
20468         if(!bullet){
20469             return;
20470         }
20471         
20472         bullet.addClass('selected');
20473     }
20474     
20475     
20476   
20477 });
20478
20479  
20480
20481  
20482  
20483 Roo.apply(Roo.bootstrap.TabGroup, {
20484     
20485     groups: {},
20486      /**
20487     * register a Navigation Group
20488     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20489     */
20490     register : function(navgrp)
20491     {
20492         this.groups[navgrp.navId] = navgrp;
20493         
20494     },
20495     /**
20496     * fetch a Navigation Group based on the navigation ID
20497     * if one does not exist , it will get created.
20498     * @param {string} the navgroup to add
20499     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20500     */
20501     get: function(navId) {
20502         if (typeof(this.groups[navId]) == 'undefined') {
20503             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20504         }
20505         return this.groups[navId] ;
20506     }
20507     
20508     
20509     
20510 });
20511
20512  /*
20513  * - LGPL
20514  *
20515  * TabPanel
20516  * 
20517  */
20518
20519 /**
20520  * @class Roo.bootstrap.TabPanel
20521  * @extends Roo.bootstrap.Component
20522  * Bootstrap TabPanel class
20523  * @cfg {Boolean} active panel active
20524  * @cfg {String} html panel content
20525  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20526  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20527  * @cfg {String} href click to link..
20528  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20529  * 
20530  * 
20531  * @constructor
20532  * Create a new TabPanel
20533  * @param {Object} config The config object
20534  */
20535
20536 Roo.bootstrap.TabPanel = function(config){
20537     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20538     this.addEvents({
20539         /**
20540              * @event changed
20541              * Fires when the active status changes
20542              * @param {Roo.bootstrap.TabPanel} this
20543              * @param {Boolean} state the new state
20544             
20545          */
20546         'changed': true,
20547         /**
20548              * @event beforedeactivate
20549              * Fires before a tab is de-activated - can be used to do validation on a form.
20550              * @param {Roo.bootstrap.TabPanel} this
20551              * @return {Boolean} false if there is an error
20552             
20553          */
20554         'beforedeactivate': true
20555      });
20556     
20557     this.tabId = this.tabId || Roo.id();
20558   
20559 };
20560
20561 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20562     
20563     active: false,
20564     html: false,
20565     tabId: false,
20566     navId : false,
20567     href : '',
20568     touchSlide : false,
20569     getAutoCreate : function(){
20570         
20571         
20572         var cfg = {
20573             tag: 'div',
20574             // item is needed for carousel - not sure if it has any effect otherwise
20575             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20576             html: this.html || ''
20577         };
20578         
20579         if(this.active){
20580             cfg.cls += ' active';
20581         }
20582         
20583         if(this.tabId){
20584             cfg.tabId = this.tabId;
20585         }
20586         
20587         
20588         
20589         return cfg;
20590     },
20591     
20592     initEvents:  function()
20593     {
20594         var p = this.parent();
20595         
20596         this.navId = this.navId || p.navId;
20597         
20598         if (typeof(this.navId) != 'undefined') {
20599             // not really needed.. but just in case.. parent should be a NavGroup.
20600             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20601             
20602             tg.register(this);
20603             
20604             var i = tg.tabs.length - 1;
20605             
20606             if(this.active && tg.bullets > 0 && i < tg.bullets){
20607                 tg.setActiveBullet(i);
20608             }
20609         }
20610         
20611         this.el.on('click', this.onClick, this);
20612         
20613         if(Roo.isTouch && this.touchSlide){
20614             this.el.on("touchstart", this.onTouchStart, this);
20615             this.el.on("touchmove", this.onTouchMove, this);
20616             this.el.on("touchend", this.onTouchEnd, this);
20617         }
20618         
20619     },
20620     
20621     onRender : function(ct, position)
20622     {
20623         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20624     },
20625     
20626     setActive : function(state)
20627     {
20628         Roo.log("panel - set active " + this.tabId + "=" + state);
20629         
20630         this.active = state;
20631         if (!state) {
20632             this.el.removeClass('active');
20633             
20634         } else  if (!this.el.hasClass('active')) {
20635             this.el.addClass('active');
20636         }
20637         
20638         this.fireEvent('changed', this, state);
20639     },
20640     
20641     onClick : function(e)
20642     {
20643         e.preventDefault();
20644         
20645         if(!this.href.length){
20646             return;
20647         }
20648         
20649         window.location.href = this.href;
20650     },
20651     
20652     startX : 0,
20653     startY : 0,
20654     endX : 0,
20655     endY : 0,
20656     swiping : false,
20657     
20658     onTouchStart : function(e)
20659     {
20660         this.swiping = false;
20661         
20662         this.startX = e.browserEvent.touches[0].clientX;
20663         this.startY = e.browserEvent.touches[0].clientY;
20664     },
20665     
20666     onTouchMove : function(e)
20667     {
20668         this.swiping = true;
20669         
20670         this.endX = e.browserEvent.touches[0].clientX;
20671         this.endY = e.browserEvent.touches[0].clientY;
20672     },
20673     
20674     onTouchEnd : function(e)
20675     {
20676         if(!this.swiping){
20677             this.onClick(e);
20678             return;
20679         }
20680         
20681         var tabGroup = this.parent();
20682         
20683         if(this.endX > this.startX){ // swiping right
20684             tabGroup.showPanelPrev();
20685             return;
20686         }
20687         
20688         if(this.startX > this.endX){ // swiping left
20689             tabGroup.showPanelNext();
20690             return;
20691         }
20692     }
20693     
20694     
20695 });
20696  
20697
20698  
20699
20700  /*
20701  * - LGPL
20702  *
20703  * DateField
20704  * 
20705  */
20706
20707 /**
20708  * @class Roo.bootstrap.DateField
20709  * @extends Roo.bootstrap.Input
20710  * Bootstrap DateField class
20711  * @cfg {Number} weekStart default 0
20712  * @cfg {String} viewMode default empty, (months|years)
20713  * @cfg {String} minViewMode default empty, (months|years)
20714  * @cfg {Number} startDate default -Infinity
20715  * @cfg {Number} endDate default Infinity
20716  * @cfg {Boolean} todayHighlight default false
20717  * @cfg {Boolean} todayBtn default false
20718  * @cfg {Boolean} calendarWeeks default false
20719  * @cfg {Object} daysOfWeekDisabled default empty
20720  * @cfg {Boolean} singleMode default false (true | false)
20721  * 
20722  * @cfg {Boolean} keyboardNavigation default true
20723  * @cfg {String} language default en
20724  * 
20725  * @constructor
20726  * Create a new DateField
20727  * @param {Object} config The config object
20728  */
20729
20730 Roo.bootstrap.DateField = function(config){
20731     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20732      this.addEvents({
20733             /**
20734              * @event show
20735              * Fires when this field show.
20736              * @param {Roo.bootstrap.DateField} this
20737              * @param {Mixed} date The date value
20738              */
20739             show : true,
20740             /**
20741              * @event show
20742              * Fires when this field hide.
20743              * @param {Roo.bootstrap.DateField} this
20744              * @param {Mixed} date The date value
20745              */
20746             hide : true,
20747             /**
20748              * @event select
20749              * Fires when select a date.
20750              * @param {Roo.bootstrap.DateField} this
20751              * @param {Mixed} date The date value
20752              */
20753             select : true,
20754             /**
20755              * @event beforeselect
20756              * Fires when before select a date.
20757              * @param {Roo.bootstrap.DateField} this
20758              * @param {Mixed} date The date value
20759              */
20760             beforeselect : true
20761         });
20762 };
20763
20764 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20765     
20766     /**
20767      * @cfg {String} format
20768      * The default date format string which can be overriden for localization support.  The format must be
20769      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20770      */
20771     format : "m/d/y",
20772     /**
20773      * @cfg {String} altFormats
20774      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20775      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20776      */
20777     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20778     
20779     weekStart : 0,
20780     
20781     viewMode : '',
20782     
20783     minViewMode : '',
20784     
20785     todayHighlight : false,
20786     
20787     todayBtn: false,
20788     
20789     language: 'en',
20790     
20791     keyboardNavigation: true,
20792     
20793     calendarWeeks: false,
20794     
20795     startDate: -Infinity,
20796     
20797     endDate: Infinity,
20798     
20799     daysOfWeekDisabled: [],
20800     
20801     _events: [],
20802     
20803     singleMode : false,
20804     
20805     UTCDate: function()
20806     {
20807         return new Date(Date.UTC.apply(Date, arguments));
20808     },
20809     
20810     UTCToday: function()
20811     {
20812         var today = new Date();
20813         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20814     },
20815     
20816     getDate: function() {
20817             var d = this.getUTCDate();
20818             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20819     },
20820     
20821     getUTCDate: function() {
20822             return this.date;
20823     },
20824     
20825     setDate: function(d) {
20826             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20827     },
20828     
20829     setUTCDate: function(d) {
20830             this.date = d;
20831             this.setValue(this.formatDate(this.date));
20832     },
20833         
20834     onRender: function(ct, position)
20835     {
20836         
20837         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20838         
20839         this.language = this.language || 'en';
20840         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20841         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20842         
20843         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20844         this.format = this.format || 'm/d/y';
20845         this.isInline = false;
20846         this.isInput = true;
20847         this.component = this.el.select('.add-on', true).first() || false;
20848         this.component = (this.component && this.component.length === 0) ? false : this.component;
20849         this.hasInput = this.component && this.inputEl().length;
20850         
20851         if (typeof(this.minViewMode === 'string')) {
20852             switch (this.minViewMode) {
20853                 case 'months':
20854                     this.minViewMode = 1;
20855                     break;
20856                 case 'years':
20857                     this.minViewMode = 2;
20858                     break;
20859                 default:
20860                     this.minViewMode = 0;
20861                     break;
20862             }
20863         }
20864         
20865         if (typeof(this.viewMode === 'string')) {
20866             switch (this.viewMode) {
20867                 case 'months':
20868                     this.viewMode = 1;
20869                     break;
20870                 case 'years':
20871                     this.viewMode = 2;
20872                     break;
20873                 default:
20874                     this.viewMode = 0;
20875                     break;
20876             }
20877         }
20878                 
20879         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20880         
20881 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20882         
20883         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20884         
20885         this.picker().on('mousedown', this.onMousedown, this);
20886         this.picker().on('click', this.onClick, this);
20887         
20888         this.picker().addClass('datepicker-dropdown');
20889         
20890         this.startViewMode = this.viewMode;
20891         
20892         if(this.singleMode){
20893             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20894                 v.setVisibilityMode(Roo.Element.DISPLAY);
20895                 v.hide();
20896             });
20897             
20898             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20899                 v.setStyle('width', '189px');
20900             });
20901         }
20902         
20903         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20904             if(!this.calendarWeeks){
20905                 v.remove();
20906                 return;
20907             }
20908             
20909             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20910             v.attr('colspan', function(i, val){
20911                 return parseInt(val) + 1;
20912             });
20913         });
20914                         
20915         
20916         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20917         
20918         this.setStartDate(this.startDate);
20919         this.setEndDate(this.endDate);
20920         
20921         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20922         
20923         this.fillDow();
20924         this.fillMonths();
20925         this.update();
20926         this.showMode();
20927         
20928         if(this.isInline) {
20929             this.showPopup();
20930         }
20931     },
20932     
20933     picker : function()
20934     {
20935         return this.pickerEl;
20936 //        return this.el.select('.datepicker', true).first();
20937     },
20938     
20939     fillDow: function()
20940     {
20941         var dowCnt = this.weekStart;
20942         
20943         var dow = {
20944             tag: 'tr',
20945             cn: [
20946                 
20947             ]
20948         };
20949         
20950         if(this.calendarWeeks){
20951             dow.cn.push({
20952                 tag: 'th',
20953                 cls: 'cw',
20954                 html: '&nbsp;'
20955             })
20956         }
20957         
20958         while (dowCnt < this.weekStart + 7) {
20959             dow.cn.push({
20960                 tag: 'th',
20961                 cls: 'dow',
20962                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20963             });
20964         }
20965         
20966         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20967     },
20968     
20969     fillMonths: function()
20970     {    
20971         var i = 0;
20972         var months = this.picker().select('>.datepicker-months td', true).first();
20973         
20974         months.dom.innerHTML = '';
20975         
20976         while (i < 12) {
20977             var month = {
20978                 tag: 'span',
20979                 cls: 'month',
20980                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20981             };
20982             
20983             months.createChild(month);
20984         }
20985         
20986     },
20987     
20988     update: function()
20989     {
20990         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;
20991         
20992         if (this.date < this.startDate) {
20993             this.viewDate = new Date(this.startDate);
20994         } else if (this.date > this.endDate) {
20995             this.viewDate = new Date(this.endDate);
20996         } else {
20997             this.viewDate = new Date(this.date);
20998         }
20999         
21000         this.fill();
21001     },
21002     
21003     fill: function() 
21004     {
21005         var d = new Date(this.viewDate),
21006                 year = d.getUTCFullYear(),
21007                 month = d.getUTCMonth(),
21008                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21009                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21010                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21011                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21012                 currentDate = this.date && this.date.valueOf(),
21013                 today = this.UTCToday();
21014         
21015         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21016         
21017 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21018         
21019 //        this.picker.select('>tfoot th.today').
21020 //                                              .text(dates[this.language].today)
21021 //                                              .toggle(this.todayBtn !== false);
21022     
21023         this.updateNavArrows();
21024         this.fillMonths();
21025                                                 
21026         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21027         
21028         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21029          
21030         prevMonth.setUTCDate(day);
21031         
21032         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21033         
21034         var nextMonth = new Date(prevMonth);
21035         
21036         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21037         
21038         nextMonth = nextMonth.valueOf();
21039         
21040         var fillMonths = false;
21041         
21042         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21043         
21044         while(prevMonth.valueOf() <= nextMonth) {
21045             var clsName = '';
21046             
21047             if (prevMonth.getUTCDay() === this.weekStart) {
21048                 if(fillMonths){
21049                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21050                 }
21051                     
21052                 fillMonths = {
21053                     tag: 'tr',
21054                     cn: []
21055                 };
21056                 
21057                 if(this.calendarWeeks){
21058                     // ISO 8601: First week contains first thursday.
21059                     // ISO also states week starts on Monday, but we can be more abstract here.
21060                     var
21061                     // Start of current week: based on weekstart/current date
21062                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21063                     // Thursday of this week
21064                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21065                     // First Thursday of year, year from thursday
21066                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21067                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21068                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21069                     
21070                     fillMonths.cn.push({
21071                         tag: 'td',
21072                         cls: 'cw',
21073                         html: calWeek
21074                     });
21075                 }
21076             }
21077             
21078             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21079                 clsName += ' old';
21080             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21081                 clsName += ' new';
21082             }
21083             if (this.todayHighlight &&
21084                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21085                 prevMonth.getUTCMonth() == today.getMonth() &&
21086                 prevMonth.getUTCDate() == today.getDate()) {
21087                 clsName += ' today';
21088             }
21089             
21090             if (currentDate && prevMonth.valueOf() === currentDate) {
21091                 clsName += ' active';
21092             }
21093             
21094             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21095                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21096                     clsName += ' disabled';
21097             }
21098             
21099             fillMonths.cn.push({
21100                 tag: 'td',
21101                 cls: 'day ' + clsName,
21102                 html: prevMonth.getDate()
21103             });
21104             
21105             prevMonth.setDate(prevMonth.getDate()+1);
21106         }
21107           
21108         var currentYear = this.date && this.date.getUTCFullYear();
21109         var currentMonth = this.date && this.date.getUTCMonth();
21110         
21111         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21112         
21113         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21114             v.removeClass('active');
21115             
21116             if(currentYear === year && k === currentMonth){
21117                 v.addClass('active');
21118             }
21119             
21120             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21121                 v.addClass('disabled');
21122             }
21123             
21124         });
21125         
21126         
21127         year = parseInt(year/10, 10) * 10;
21128         
21129         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21130         
21131         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21132         
21133         year -= 1;
21134         for (var i = -1; i < 11; i++) {
21135             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21136                 tag: 'span',
21137                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21138                 html: year
21139             });
21140             
21141             year += 1;
21142         }
21143     },
21144     
21145     showMode: function(dir) 
21146     {
21147         if (dir) {
21148             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21149         }
21150         
21151         Roo.each(this.picker().select('>div',true).elements, function(v){
21152             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21153             v.hide();
21154         });
21155         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21156     },
21157     
21158     place: function()
21159     {
21160         if(this.isInline) {
21161             return;
21162         }
21163         
21164         this.picker().removeClass(['bottom', 'top']);
21165         
21166         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21167             /*
21168              * place to the top of element!
21169              *
21170              */
21171             
21172             this.picker().addClass('top');
21173             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21174             
21175             return;
21176         }
21177         
21178         this.picker().addClass('bottom');
21179         
21180         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21181     },
21182     
21183     parseDate : function(value)
21184     {
21185         if(!value || value instanceof Date){
21186             return value;
21187         }
21188         var v = Date.parseDate(value, this.format);
21189         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21190             v = Date.parseDate(value, 'Y-m-d');
21191         }
21192         if(!v && this.altFormats){
21193             if(!this.altFormatsArray){
21194                 this.altFormatsArray = this.altFormats.split("|");
21195             }
21196             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21197                 v = Date.parseDate(value, this.altFormatsArray[i]);
21198             }
21199         }
21200         return v;
21201     },
21202     
21203     formatDate : function(date, fmt)
21204     {   
21205         return (!date || !(date instanceof Date)) ?
21206         date : date.dateFormat(fmt || this.format);
21207     },
21208     
21209     onFocus : function()
21210     {
21211         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21212         this.showPopup();
21213     },
21214     
21215     onBlur : function()
21216     {
21217         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21218         
21219         var d = this.inputEl().getValue();
21220         
21221         this.setValue(d);
21222                 
21223         this.hidePopup();
21224     },
21225     
21226     showPopup : function()
21227     {
21228         this.picker().show();
21229         this.update();
21230         this.place();
21231         
21232         this.fireEvent('showpopup', this, this.date);
21233     },
21234     
21235     hidePopup : function()
21236     {
21237         if(this.isInline) {
21238             return;
21239         }
21240         this.picker().hide();
21241         this.viewMode = this.startViewMode;
21242         this.showMode();
21243         
21244         this.fireEvent('hidepopup', this, this.date);
21245         
21246     },
21247     
21248     onMousedown: function(e)
21249     {
21250         e.stopPropagation();
21251         e.preventDefault();
21252     },
21253     
21254     keyup: function(e)
21255     {
21256         Roo.bootstrap.DateField.superclass.keyup.call(this);
21257         this.update();
21258     },
21259
21260     setValue: function(v)
21261     {
21262         if(this.fireEvent('beforeselect', this, v) !== false){
21263             var d = new Date(this.parseDate(v) ).clearTime();
21264         
21265             if(isNaN(d.getTime())){
21266                 this.date = this.viewDate = '';
21267                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21268                 return;
21269             }
21270
21271             v = this.formatDate(d);
21272
21273             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21274
21275             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21276
21277             this.update();
21278
21279             this.fireEvent('select', this, this.date);
21280         }
21281     },
21282     
21283     getValue: function()
21284     {
21285         return this.formatDate(this.date);
21286     },
21287     
21288     fireKey: function(e)
21289     {
21290         if (!this.picker().isVisible()){
21291             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21292                 this.showPopup();
21293             }
21294             return;
21295         }
21296         
21297         var dateChanged = false,
21298         dir, day, month,
21299         newDate, newViewDate;
21300         
21301         switch(e.keyCode){
21302             case 27: // escape
21303                 this.hidePopup();
21304                 e.preventDefault();
21305                 break;
21306             case 37: // left
21307             case 39: // right
21308                 if (!this.keyboardNavigation) {
21309                     break;
21310                 }
21311                 dir = e.keyCode == 37 ? -1 : 1;
21312                 
21313                 if (e.ctrlKey){
21314                     newDate = this.moveYear(this.date, dir);
21315                     newViewDate = this.moveYear(this.viewDate, dir);
21316                 } else if (e.shiftKey){
21317                     newDate = this.moveMonth(this.date, dir);
21318                     newViewDate = this.moveMonth(this.viewDate, dir);
21319                 } else {
21320                     newDate = new Date(this.date);
21321                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21322                     newViewDate = new Date(this.viewDate);
21323                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21324                 }
21325                 if (this.dateWithinRange(newDate)){
21326                     this.date = newDate;
21327                     this.viewDate = newViewDate;
21328                     this.setValue(this.formatDate(this.date));
21329 //                    this.update();
21330                     e.preventDefault();
21331                     dateChanged = true;
21332                 }
21333                 break;
21334             case 38: // up
21335             case 40: // down
21336                 if (!this.keyboardNavigation) {
21337                     break;
21338                 }
21339                 dir = e.keyCode == 38 ? -1 : 1;
21340                 if (e.ctrlKey){
21341                     newDate = this.moveYear(this.date, dir);
21342                     newViewDate = this.moveYear(this.viewDate, dir);
21343                 } else if (e.shiftKey){
21344                     newDate = this.moveMonth(this.date, dir);
21345                     newViewDate = this.moveMonth(this.viewDate, dir);
21346                 } else {
21347                     newDate = new Date(this.date);
21348                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21349                     newViewDate = new Date(this.viewDate);
21350                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21351                 }
21352                 if (this.dateWithinRange(newDate)){
21353                     this.date = newDate;
21354                     this.viewDate = newViewDate;
21355                     this.setValue(this.formatDate(this.date));
21356 //                    this.update();
21357                     e.preventDefault();
21358                     dateChanged = true;
21359                 }
21360                 break;
21361             case 13: // enter
21362                 this.setValue(this.formatDate(this.date));
21363                 this.hidePopup();
21364                 e.preventDefault();
21365                 break;
21366             case 9: // tab
21367                 this.setValue(this.formatDate(this.date));
21368                 this.hidePopup();
21369                 break;
21370             case 16: // shift
21371             case 17: // ctrl
21372             case 18: // alt
21373                 break;
21374             default :
21375                 this.hidePopup();
21376                 
21377         }
21378     },
21379     
21380     
21381     onClick: function(e) 
21382     {
21383         e.stopPropagation();
21384         e.preventDefault();
21385         
21386         var target = e.getTarget();
21387         
21388         if(target.nodeName.toLowerCase() === 'i'){
21389             target = Roo.get(target).dom.parentNode;
21390         }
21391         
21392         var nodeName = target.nodeName;
21393         var className = target.className;
21394         var html = target.innerHTML;
21395         //Roo.log(nodeName);
21396         
21397         switch(nodeName.toLowerCase()) {
21398             case 'th':
21399                 switch(className) {
21400                     case 'switch':
21401                         this.showMode(1);
21402                         break;
21403                     case 'prev':
21404                     case 'next':
21405                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21406                         switch(this.viewMode){
21407                                 case 0:
21408                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21409                                         break;
21410                                 case 1:
21411                                 case 2:
21412                                         this.viewDate = this.moveYear(this.viewDate, dir);
21413                                         break;
21414                         }
21415                         this.fill();
21416                         break;
21417                     case 'today':
21418                         var date = new Date();
21419                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21420 //                        this.fill()
21421                         this.setValue(this.formatDate(this.date));
21422                         
21423                         this.hidePopup();
21424                         break;
21425                 }
21426                 break;
21427             case 'span':
21428                 if (className.indexOf('disabled') < 0) {
21429                     this.viewDate.setUTCDate(1);
21430                     if (className.indexOf('month') > -1) {
21431                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21432                     } else {
21433                         var year = parseInt(html, 10) || 0;
21434                         this.viewDate.setUTCFullYear(year);
21435                         
21436                     }
21437                     
21438                     if(this.singleMode){
21439                         this.setValue(this.formatDate(this.viewDate));
21440                         this.hidePopup();
21441                         return;
21442                     }
21443                     
21444                     this.showMode(-1);
21445                     this.fill();
21446                 }
21447                 break;
21448                 
21449             case 'td':
21450                 //Roo.log(className);
21451                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21452                     var day = parseInt(html, 10) || 1;
21453                     var year = this.viewDate.getUTCFullYear(),
21454                         month = this.viewDate.getUTCMonth();
21455
21456                     if (className.indexOf('old') > -1) {
21457                         if(month === 0 ){
21458                             month = 11;
21459                             year -= 1;
21460                         }else{
21461                             month -= 1;
21462                         }
21463                     } else if (className.indexOf('new') > -1) {
21464                         if (month == 11) {
21465                             month = 0;
21466                             year += 1;
21467                         } else {
21468                             month += 1;
21469                         }
21470                     }
21471                     //Roo.log([year,month,day]);
21472                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21473                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21474 //                    this.fill();
21475                     //Roo.log(this.formatDate(this.date));
21476                     this.setValue(this.formatDate(this.date));
21477                     this.hidePopup();
21478                 }
21479                 break;
21480         }
21481     },
21482     
21483     setStartDate: function(startDate)
21484     {
21485         this.startDate = startDate || -Infinity;
21486         if (this.startDate !== -Infinity) {
21487             this.startDate = this.parseDate(this.startDate);
21488         }
21489         this.update();
21490         this.updateNavArrows();
21491     },
21492
21493     setEndDate: function(endDate)
21494     {
21495         this.endDate = endDate || Infinity;
21496         if (this.endDate !== Infinity) {
21497             this.endDate = this.parseDate(this.endDate);
21498         }
21499         this.update();
21500         this.updateNavArrows();
21501     },
21502     
21503     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21504     {
21505         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21506         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21507             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21508         }
21509         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21510             return parseInt(d, 10);
21511         });
21512         this.update();
21513         this.updateNavArrows();
21514     },
21515     
21516     updateNavArrows: function() 
21517     {
21518         if(this.singleMode){
21519             return;
21520         }
21521         
21522         var d = new Date(this.viewDate),
21523         year = d.getUTCFullYear(),
21524         month = d.getUTCMonth();
21525         
21526         Roo.each(this.picker().select('.prev', true).elements, function(v){
21527             v.show();
21528             switch (this.viewMode) {
21529                 case 0:
21530
21531                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21532                         v.hide();
21533                     }
21534                     break;
21535                 case 1:
21536                 case 2:
21537                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21538                         v.hide();
21539                     }
21540                     break;
21541             }
21542         });
21543         
21544         Roo.each(this.picker().select('.next', true).elements, function(v){
21545             v.show();
21546             switch (this.viewMode) {
21547                 case 0:
21548
21549                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21550                         v.hide();
21551                     }
21552                     break;
21553                 case 1:
21554                 case 2:
21555                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21556                         v.hide();
21557                     }
21558                     break;
21559             }
21560         })
21561     },
21562     
21563     moveMonth: function(date, dir)
21564     {
21565         if (!dir) {
21566             return date;
21567         }
21568         var new_date = new Date(date.valueOf()),
21569         day = new_date.getUTCDate(),
21570         month = new_date.getUTCMonth(),
21571         mag = Math.abs(dir),
21572         new_month, test;
21573         dir = dir > 0 ? 1 : -1;
21574         if (mag == 1){
21575             test = dir == -1
21576             // If going back one month, make sure month is not current month
21577             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21578             ? function(){
21579                 return new_date.getUTCMonth() == month;
21580             }
21581             // If going forward one month, make sure month is as expected
21582             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21583             : function(){
21584                 return new_date.getUTCMonth() != new_month;
21585             };
21586             new_month = month + dir;
21587             new_date.setUTCMonth(new_month);
21588             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21589             if (new_month < 0 || new_month > 11) {
21590                 new_month = (new_month + 12) % 12;
21591             }
21592         } else {
21593             // For magnitudes >1, move one month at a time...
21594             for (var i=0; i<mag; i++) {
21595                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21596                 new_date = this.moveMonth(new_date, dir);
21597             }
21598             // ...then reset the day, keeping it in the new month
21599             new_month = new_date.getUTCMonth();
21600             new_date.setUTCDate(day);
21601             test = function(){
21602                 return new_month != new_date.getUTCMonth();
21603             };
21604         }
21605         // Common date-resetting loop -- if date is beyond end of month, make it
21606         // end of month
21607         while (test()){
21608             new_date.setUTCDate(--day);
21609             new_date.setUTCMonth(new_month);
21610         }
21611         return new_date;
21612     },
21613
21614     moveYear: function(date, dir)
21615     {
21616         return this.moveMonth(date, dir*12);
21617     },
21618
21619     dateWithinRange: function(date)
21620     {
21621         return date >= this.startDate && date <= this.endDate;
21622     },
21623
21624     
21625     remove: function() 
21626     {
21627         this.picker().remove();
21628     },
21629     
21630     validateValue : function(value)
21631     {
21632         if(this.getVisibilityEl().hasClass('hidden')){
21633             return true;
21634         }
21635         
21636         if(value.length < 1)  {
21637             if(this.allowBlank){
21638                 return true;
21639             }
21640             return false;
21641         }
21642         
21643         if(value.length < this.minLength){
21644             return false;
21645         }
21646         if(value.length > this.maxLength){
21647             return false;
21648         }
21649         if(this.vtype){
21650             var vt = Roo.form.VTypes;
21651             if(!vt[this.vtype](value, this)){
21652                 return false;
21653             }
21654         }
21655         if(typeof this.validator == "function"){
21656             var msg = this.validator(value);
21657             if(msg !== true){
21658                 return false;
21659             }
21660         }
21661         
21662         if(this.regex && !this.regex.test(value)){
21663             return false;
21664         }
21665         
21666         if(typeof(this.parseDate(value)) == 'undefined'){
21667             return false;
21668         }
21669         
21670         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21671             return false;
21672         }      
21673         
21674         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21675             return false;
21676         } 
21677         
21678         
21679         return true;
21680     },
21681     
21682     reset : function()
21683     {
21684         this.date = this.viewDate = '';
21685         
21686         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21687     }
21688    
21689 });
21690
21691 Roo.apply(Roo.bootstrap.DateField,  {
21692     
21693     head : {
21694         tag: 'thead',
21695         cn: [
21696         {
21697             tag: 'tr',
21698             cn: [
21699             {
21700                 tag: 'th',
21701                 cls: 'prev',
21702                 html: '<i class="fa fa-arrow-left"/>'
21703             },
21704             {
21705                 tag: 'th',
21706                 cls: 'switch',
21707                 colspan: '5'
21708             },
21709             {
21710                 tag: 'th',
21711                 cls: 'next',
21712                 html: '<i class="fa fa-arrow-right"/>'
21713             }
21714
21715             ]
21716         }
21717         ]
21718     },
21719     
21720     content : {
21721         tag: 'tbody',
21722         cn: [
21723         {
21724             tag: 'tr',
21725             cn: [
21726             {
21727                 tag: 'td',
21728                 colspan: '7'
21729             }
21730             ]
21731         }
21732         ]
21733     },
21734     
21735     footer : {
21736         tag: 'tfoot',
21737         cn: [
21738         {
21739             tag: 'tr',
21740             cn: [
21741             {
21742                 tag: 'th',
21743                 colspan: '7',
21744                 cls: 'today'
21745             }
21746                     
21747             ]
21748         }
21749         ]
21750     },
21751     
21752     dates:{
21753         en: {
21754             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21755             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21756             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21757             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21758             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21759             today: "Today"
21760         }
21761     },
21762     
21763     modes: [
21764     {
21765         clsName: 'days',
21766         navFnc: 'Month',
21767         navStep: 1
21768     },
21769     {
21770         clsName: 'months',
21771         navFnc: 'FullYear',
21772         navStep: 1
21773     },
21774     {
21775         clsName: 'years',
21776         navFnc: 'FullYear',
21777         navStep: 10
21778     }]
21779 });
21780
21781 Roo.apply(Roo.bootstrap.DateField,  {
21782   
21783     template : {
21784         tag: 'div',
21785         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21786         cn: [
21787         {
21788             tag: 'div',
21789             cls: 'datepicker-days',
21790             cn: [
21791             {
21792                 tag: 'table',
21793                 cls: 'table-condensed',
21794                 cn:[
21795                 Roo.bootstrap.DateField.head,
21796                 {
21797                     tag: 'tbody'
21798                 },
21799                 Roo.bootstrap.DateField.footer
21800                 ]
21801             }
21802             ]
21803         },
21804         {
21805             tag: 'div',
21806             cls: 'datepicker-months',
21807             cn: [
21808             {
21809                 tag: 'table',
21810                 cls: 'table-condensed',
21811                 cn:[
21812                 Roo.bootstrap.DateField.head,
21813                 Roo.bootstrap.DateField.content,
21814                 Roo.bootstrap.DateField.footer
21815                 ]
21816             }
21817             ]
21818         },
21819         {
21820             tag: 'div',
21821             cls: 'datepicker-years',
21822             cn: [
21823             {
21824                 tag: 'table',
21825                 cls: 'table-condensed',
21826                 cn:[
21827                 Roo.bootstrap.DateField.head,
21828                 Roo.bootstrap.DateField.content,
21829                 Roo.bootstrap.DateField.footer
21830                 ]
21831             }
21832             ]
21833         }
21834         ]
21835     }
21836 });
21837
21838  
21839
21840  /*
21841  * - LGPL
21842  *
21843  * TimeField
21844  * 
21845  */
21846
21847 /**
21848  * @class Roo.bootstrap.TimeField
21849  * @extends Roo.bootstrap.Input
21850  * Bootstrap DateField class
21851  * 
21852  * 
21853  * @constructor
21854  * Create a new TimeField
21855  * @param {Object} config The config object
21856  */
21857
21858 Roo.bootstrap.TimeField = function(config){
21859     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21860     this.addEvents({
21861             /**
21862              * @event show
21863              * Fires when this field show.
21864              * @param {Roo.bootstrap.DateField} thisthis
21865              * @param {Mixed} date The date value
21866              */
21867             show : true,
21868             /**
21869              * @event show
21870              * Fires when this field hide.
21871              * @param {Roo.bootstrap.DateField} this
21872              * @param {Mixed} date The date value
21873              */
21874             hide : true,
21875             /**
21876              * @event select
21877              * Fires when select a date.
21878              * @param {Roo.bootstrap.DateField} this
21879              * @param {Mixed} date The date value
21880              */
21881             select : true
21882         });
21883 };
21884
21885 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21886     
21887     /**
21888      * @cfg {String} format
21889      * The default time format string which can be overriden for localization support.  The format must be
21890      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21891      */
21892     format : "H:i",
21893        
21894     onRender: function(ct, position)
21895     {
21896         
21897         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21898                 
21899         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21900         
21901         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21902         
21903         this.pop = this.picker().select('>.datepicker-time',true).first();
21904         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21905         
21906         this.picker().on('mousedown', this.onMousedown, this);
21907         this.picker().on('click', this.onClick, this);
21908         
21909         this.picker().addClass('datepicker-dropdown');
21910     
21911         this.fillTime();
21912         this.update();
21913             
21914         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21915         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21916         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21917         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21918         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21919         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21920
21921     },
21922     
21923     fireKey: function(e){
21924         if (!this.picker().isVisible()){
21925             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21926                 this.show();
21927             }
21928             return;
21929         }
21930
21931         e.preventDefault();
21932         
21933         switch(e.keyCode){
21934             case 27: // escape
21935                 this.hide();
21936                 break;
21937             case 37: // left
21938             case 39: // right
21939                 this.onTogglePeriod();
21940                 break;
21941             case 38: // up
21942                 this.onIncrementMinutes();
21943                 break;
21944             case 40: // down
21945                 this.onDecrementMinutes();
21946                 break;
21947             case 13: // enter
21948             case 9: // tab
21949                 this.setTime();
21950                 break;
21951         }
21952     },
21953     
21954     onClick: function(e) {
21955         e.stopPropagation();
21956         e.preventDefault();
21957     },
21958     
21959     picker : function()
21960     {
21961         return this.el.select('.datepicker', true).first();
21962     },
21963     
21964     fillTime: function()
21965     {    
21966         var time = this.pop.select('tbody', true).first();
21967         
21968         time.dom.innerHTML = '';
21969         
21970         time.createChild({
21971             tag: 'tr',
21972             cn: [
21973                 {
21974                     tag: 'td',
21975                     cn: [
21976                         {
21977                             tag: 'a',
21978                             href: '#',
21979                             cls: 'btn',
21980                             cn: [
21981                                 {
21982                                     tag: 'span',
21983                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21984                                 }
21985                             ]
21986                         } 
21987                     ]
21988                 },
21989                 {
21990                     tag: 'td',
21991                     cls: 'separator'
21992                 },
21993                 {
21994                     tag: 'td',
21995                     cn: [
21996                         {
21997                             tag: 'a',
21998                             href: '#',
21999                             cls: 'btn',
22000                             cn: [
22001                                 {
22002                                     tag: 'span',
22003                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22004                                 }
22005                             ]
22006                         }
22007                     ]
22008                 },
22009                 {
22010                     tag: 'td',
22011                     cls: 'separator'
22012                 }
22013             ]
22014         });
22015         
22016         time.createChild({
22017             tag: 'tr',
22018             cn: [
22019                 {
22020                     tag: 'td',
22021                     cn: [
22022                         {
22023                             tag: 'span',
22024                             cls: 'timepicker-hour',
22025                             html: '00'
22026                         }  
22027                     ]
22028                 },
22029                 {
22030                     tag: 'td',
22031                     cls: 'separator',
22032                     html: ':'
22033                 },
22034                 {
22035                     tag: 'td',
22036                     cn: [
22037                         {
22038                             tag: 'span',
22039                             cls: 'timepicker-minute',
22040                             html: '00'
22041                         }  
22042                     ]
22043                 },
22044                 {
22045                     tag: 'td',
22046                     cls: 'separator'
22047                 },
22048                 {
22049                     tag: 'td',
22050                     cn: [
22051                         {
22052                             tag: 'button',
22053                             type: 'button',
22054                             cls: 'btn btn-primary period',
22055                             html: 'AM'
22056                             
22057                         }
22058                     ]
22059                 }
22060             ]
22061         });
22062         
22063         time.createChild({
22064             tag: 'tr',
22065             cn: [
22066                 {
22067                     tag: 'td',
22068                     cn: [
22069                         {
22070                             tag: 'a',
22071                             href: '#',
22072                             cls: 'btn',
22073                             cn: [
22074                                 {
22075                                     tag: 'span',
22076                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22077                                 }
22078                             ]
22079                         }
22080                     ]
22081                 },
22082                 {
22083                     tag: 'td',
22084                     cls: 'separator'
22085                 },
22086                 {
22087                     tag: 'td',
22088                     cn: [
22089                         {
22090                             tag: 'a',
22091                             href: '#',
22092                             cls: 'btn',
22093                             cn: [
22094                                 {
22095                                     tag: 'span',
22096                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22097                                 }
22098                             ]
22099                         }
22100                     ]
22101                 },
22102                 {
22103                     tag: 'td',
22104                     cls: 'separator'
22105                 }
22106             ]
22107         });
22108         
22109     },
22110     
22111     update: function()
22112     {
22113         
22114         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22115         
22116         this.fill();
22117     },
22118     
22119     fill: function() 
22120     {
22121         var hours = this.time.getHours();
22122         var minutes = this.time.getMinutes();
22123         var period = 'AM';
22124         
22125         if(hours > 11){
22126             period = 'PM';
22127         }
22128         
22129         if(hours == 0){
22130             hours = 12;
22131         }
22132         
22133         
22134         if(hours > 12){
22135             hours = hours - 12;
22136         }
22137         
22138         if(hours < 10){
22139             hours = '0' + hours;
22140         }
22141         
22142         if(minutes < 10){
22143             minutes = '0' + minutes;
22144         }
22145         
22146         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22147         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22148         this.pop.select('button', true).first().dom.innerHTML = period;
22149         
22150     },
22151     
22152     place: function()
22153     {   
22154         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22155         
22156         var cls = ['bottom'];
22157         
22158         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22159             cls.pop();
22160             cls.push('top');
22161         }
22162         
22163         cls.push('right');
22164         
22165         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22166             cls.pop();
22167             cls.push('left');
22168         }
22169         
22170         this.picker().addClass(cls.join('-'));
22171         
22172         var _this = this;
22173         
22174         Roo.each(cls, function(c){
22175             if(c == 'bottom'){
22176                 _this.picker().setTop(_this.inputEl().getHeight());
22177                 return;
22178             }
22179             if(c == 'top'){
22180                 _this.picker().setTop(0 - _this.picker().getHeight());
22181                 return;
22182             }
22183             
22184             if(c == 'left'){
22185                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22186                 return;
22187             }
22188             if(c == 'right'){
22189                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22190                 return;
22191             }
22192         });
22193         
22194     },
22195   
22196     onFocus : function()
22197     {
22198         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22199         this.show();
22200     },
22201     
22202     onBlur : function()
22203     {
22204         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22205         this.hide();
22206     },
22207     
22208     show : function()
22209     {
22210         this.picker().show();
22211         this.pop.show();
22212         this.update();
22213         this.place();
22214         
22215         this.fireEvent('show', this, this.date);
22216     },
22217     
22218     hide : function()
22219     {
22220         this.picker().hide();
22221         this.pop.hide();
22222         
22223         this.fireEvent('hide', this, this.date);
22224     },
22225     
22226     setTime : function()
22227     {
22228         this.hide();
22229         this.setValue(this.time.format(this.format));
22230         
22231         this.fireEvent('select', this, this.date);
22232         
22233         
22234     },
22235     
22236     onMousedown: function(e){
22237         e.stopPropagation();
22238         e.preventDefault();
22239     },
22240     
22241     onIncrementHours: function()
22242     {
22243         Roo.log('onIncrementHours');
22244         this.time = this.time.add(Date.HOUR, 1);
22245         this.update();
22246         
22247     },
22248     
22249     onDecrementHours: function()
22250     {
22251         Roo.log('onDecrementHours');
22252         this.time = this.time.add(Date.HOUR, -1);
22253         this.update();
22254     },
22255     
22256     onIncrementMinutes: function()
22257     {
22258         Roo.log('onIncrementMinutes');
22259         this.time = this.time.add(Date.MINUTE, 1);
22260         this.update();
22261     },
22262     
22263     onDecrementMinutes: function()
22264     {
22265         Roo.log('onDecrementMinutes');
22266         this.time = this.time.add(Date.MINUTE, -1);
22267         this.update();
22268     },
22269     
22270     onTogglePeriod: function()
22271     {
22272         Roo.log('onTogglePeriod');
22273         this.time = this.time.add(Date.HOUR, 12);
22274         this.update();
22275     }
22276     
22277    
22278 });
22279
22280 Roo.apply(Roo.bootstrap.TimeField,  {
22281     
22282     content : {
22283         tag: 'tbody',
22284         cn: [
22285             {
22286                 tag: 'tr',
22287                 cn: [
22288                 {
22289                     tag: 'td',
22290                     colspan: '7'
22291                 }
22292                 ]
22293             }
22294         ]
22295     },
22296     
22297     footer : {
22298         tag: 'tfoot',
22299         cn: [
22300             {
22301                 tag: 'tr',
22302                 cn: [
22303                 {
22304                     tag: 'th',
22305                     colspan: '7',
22306                     cls: '',
22307                     cn: [
22308                         {
22309                             tag: 'button',
22310                             cls: 'btn btn-info ok',
22311                             html: 'OK'
22312                         }
22313                     ]
22314                 }
22315
22316                 ]
22317             }
22318         ]
22319     }
22320 });
22321
22322 Roo.apply(Roo.bootstrap.TimeField,  {
22323   
22324     template : {
22325         tag: 'div',
22326         cls: 'datepicker dropdown-menu',
22327         cn: [
22328             {
22329                 tag: 'div',
22330                 cls: 'datepicker-time',
22331                 cn: [
22332                 {
22333                     tag: 'table',
22334                     cls: 'table-condensed',
22335                     cn:[
22336                     Roo.bootstrap.TimeField.content,
22337                     Roo.bootstrap.TimeField.footer
22338                     ]
22339                 }
22340                 ]
22341             }
22342         ]
22343     }
22344 });
22345
22346  
22347
22348  /*
22349  * - LGPL
22350  *
22351  * MonthField
22352  * 
22353  */
22354
22355 /**
22356  * @class Roo.bootstrap.MonthField
22357  * @extends Roo.bootstrap.Input
22358  * Bootstrap MonthField class
22359  * 
22360  * @cfg {String} language default en
22361  * 
22362  * @constructor
22363  * Create a new MonthField
22364  * @param {Object} config The config object
22365  */
22366
22367 Roo.bootstrap.MonthField = function(config){
22368     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22369     
22370     this.addEvents({
22371         /**
22372          * @event show
22373          * Fires when this field show.
22374          * @param {Roo.bootstrap.MonthField} this
22375          * @param {Mixed} date The date value
22376          */
22377         show : true,
22378         /**
22379          * @event show
22380          * Fires when this field hide.
22381          * @param {Roo.bootstrap.MonthField} this
22382          * @param {Mixed} date The date value
22383          */
22384         hide : true,
22385         /**
22386          * @event select
22387          * Fires when select a date.
22388          * @param {Roo.bootstrap.MonthField} this
22389          * @param {String} oldvalue The old value
22390          * @param {String} newvalue The new value
22391          */
22392         select : true
22393     });
22394 };
22395
22396 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22397     
22398     onRender: function(ct, position)
22399     {
22400         
22401         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22402         
22403         this.language = this.language || 'en';
22404         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22405         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22406         
22407         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22408         this.isInline = false;
22409         this.isInput = true;
22410         this.component = this.el.select('.add-on', true).first() || false;
22411         this.component = (this.component && this.component.length === 0) ? false : this.component;
22412         this.hasInput = this.component && this.inputEL().length;
22413         
22414         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22415         
22416         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22417         
22418         this.picker().on('mousedown', this.onMousedown, this);
22419         this.picker().on('click', this.onClick, this);
22420         
22421         this.picker().addClass('datepicker-dropdown');
22422         
22423         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22424             v.setStyle('width', '189px');
22425         });
22426         
22427         this.fillMonths();
22428         
22429         this.update();
22430         
22431         if(this.isInline) {
22432             this.show();
22433         }
22434         
22435     },
22436     
22437     setValue: function(v, suppressEvent)
22438     {   
22439         var o = this.getValue();
22440         
22441         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22442         
22443         this.update();
22444
22445         if(suppressEvent !== true){
22446             this.fireEvent('select', this, o, v);
22447         }
22448         
22449     },
22450     
22451     getValue: function()
22452     {
22453         return this.value;
22454     },
22455     
22456     onClick: function(e) 
22457     {
22458         e.stopPropagation();
22459         e.preventDefault();
22460         
22461         var target = e.getTarget();
22462         
22463         if(target.nodeName.toLowerCase() === 'i'){
22464             target = Roo.get(target).dom.parentNode;
22465         }
22466         
22467         var nodeName = target.nodeName;
22468         var className = target.className;
22469         var html = target.innerHTML;
22470         
22471         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22472             return;
22473         }
22474         
22475         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22476         
22477         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22478         
22479         this.hide();
22480                         
22481     },
22482     
22483     picker : function()
22484     {
22485         return this.pickerEl;
22486     },
22487     
22488     fillMonths: function()
22489     {    
22490         var i = 0;
22491         var months = this.picker().select('>.datepicker-months td', true).first();
22492         
22493         months.dom.innerHTML = '';
22494         
22495         while (i < 12) {
22496             var month = {
22497                 tag: 'span',
22498                 cls: 'month',
22499                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22500             };
22501             
22502             months.createChild(month);
22503         }
22504         
22505     },
22506     
22507     update: function()
22508     {
22509         var _this = this;
22510         
22511         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22512             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22513         }
22514         
22515         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22516             e.removeClass('active');
22517             
22518             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22519                 e.addClass('active');
22520             }
22521         })
22522     },
22523     
22524     place: function()
22525     {
22526         if(this.isInline) {
22527             return;
22528         }
22529         
22530         this.picker().removeClass(['bottom', 'top']);
22531         
22532         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22533             /*
22534              * place to the top of element!
22535              *
22536              */
22537             
22538             this.picker().addClass('top');
22539             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22540             
22541             return;
22542         }
22543         
22544         this.picker().addClass('bottom');
22545         
22546         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22547     },
22548     
22549     onFocus : function()
22550     {
22551         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22552         this.show();
22553     },
22554     
22555     onBlur : function()
22556     {
22557         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22558         
22559         var d = this.inputEl().getValue();
22560         
22561         this.setValue(d);
22562                 
22563         this.hide();
22564     },
22565     
22566     show : function()
22567     {
22568         this.picker().show();
22569         this.picker().select('>.datepicker-months', true).first().show();
22570         this.update();
22571         this.place();
22572         
22573         this.fireEvent('show', this, this.date);
22574     },
22575     
22576     hide : function()
22577     {
22578         if(this.isInline) {
22579             return;
22580         }
22581         this.picker().hide();
22582         this.fireEvent('hide', this, this.date);
22583         
22584     },
22585     
22586     onMousedown: function(e)
22587     {
22588         e.stopPropagation();
22589         e.preventDefault();
22590     },
22591     
22592     keyup: function(e)
22593     {
22594         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22595         this.update();
22596     },
22597
22598     fireKey: function(e)
22599     {
22600         if (!this.picker().isVisible()){
22601             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22602                 this.show();
22603             }
22604             return;
22605         }
22606         
22607         var dir;
22608         
22609         switch(e.keyCode){
22610             case 27: // escape
22611                 this.hide();
22612                 e.preventDefault();
22613                 break;
22614             case 37: // left
22615             case 39: // right
22616                 dir = e.keyCode == 37 ? -1 : 1;
22617                 
22618                 this.vIndex = this.vIndex + dir;
22619                 
22620                 if(this.vIndex < 0){
22621                     this.vIndex = 0;
22622                 }
22623                 
22624                 if(this.vIndex > 11){
22625                     this.vIndex = 11;
22626                 }
22627                 
22628                 if(isNaN(this.vIndex)){
22629                     this.vIndex = 0;
22630                 }
22631                 
22632                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22633                 
22634                 break;
22635             case 38: // up
22636             case 40: // down
22637                 
22638                 dir = e.keyCode == 38 ? -1 : 1;
22639                 
22640                 this.vIndex = this.vIndex + dir * 4;
22641                 
22642                 if(this.vIndex < 0){
22643                     this.vIndex = 0;
22644                 }
22645                 
22646                 if(this.vIndex > 11){
22647                     this.vIndex = 11;
22648                 }
22649                 
22650                 if(isNaN(this.vIndex)){
22651                     this.vIndex = 0;
22652                 }
22653                 
22654                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22655                 break;
22656                 
22657             case 13: // enter
22658                 
22659                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22660                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22661                 }
22662                 
22663                 this.hide();
22664                 e.preventDefault();
22665                 break;
22666             case 9: // tab
22667                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22668                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22669                 }
22670                 this.hide();
22671                 break;
22672             case 16: // shift
22673             case 17: // ctrl
22674             case 18: // alt
22675                 break;
22676             default :
22677                 this.hide();
22678                 
22679         }
22680     },
22681     
22682     remove: function() 
22683     {
22684         this.picker().remove();
22685     }
22686    
22687 });
22688
22689 Roo.apply(Roo.bootstrap.MonthField,  {
22690     
22691     content : {
22692         tag: 'tbody',
22693         cn: [
22694         {
22695             tag: 'tr',
22696             cn: [
22697             {
22698                 tag: 'td',
22699                 colspan: '7'
22700             }
22701             ]
22702         }
22703         ]
22704     },
22705     
22706     dates:{
22707         en: {
22708             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22709             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22710         }
22711     }
22712 });
22713
22714 Roo.apply(Roo.bootstrap.MonthField,  {
22715   
22716     template : {
22717         tag: 'div',
22718         cls: 'datepicker dropdown-menu roo-dynamic',
22719         cn: [
22720             {
22721                 tag: 'div',
22722                 cls: 'datepicker-months',
22723                 cn: [
22724                 {
22725                     tag: 'table',
22726                     cls: 'table-condensed',
22727                     cn:[
22728                         Roo.bootstrap.DateField.content
22729                     ]
22730                 }
22731                 ]
22732             }
22733         ]
22734     }
22735 });
22736
22737  
22738
22739  
22740  /*
22741  * - LGPL
22742  *
22743  * CheckBox
22744  * 
22745  */
22746
22747 /**
22748  * @class Roo.bootstrap.CheckBox
22749  * @extends Roo.bootstrap.Input
22750  * Bootstrap CheckBox class
22751  * 
22752  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22753  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22754  * @cfg {String} boxLabel The text that appears beside the checkbox
22755  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22756  * @cfg {Boolean} checked initnal the element
22757  * @cfg {Boolean} inline inline the element (default false)
22758  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22759  * @cfg {String} tooltip label tooltip
22760  * 
22761  * @constructor
22762  * Create a new CheckBox
22763  * @param {Object} config The config object
22764  */
22765
22766 Roo.bootstrap.CheckBox = function(config){
22767     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22768    
22769     this.addEvents({
22770         /**
22771         * @event check
22772         * Fires when the element is checked or unchecked.
22773         * @param {Roo.bootstrap.CheckBox} this This input
22774         * @param {Boolean} checked The new checked value
22775         */
22776        check : true,
22777        /**
22778         * @event click
22779         * Fires when the element is click.
22780         * @param {Roo.bootstrap.CheckBox} this This input
22781         */
22782        click : true
22783     });
22784     
22785 };
22786
22787 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22788   
22789     inputType: 'checkbox',
22790     inputValue: 1,
22791     valueOff: 0,
22792     boxLabel: false,
22793     checked: false,
22794     weight : false,
22795     inline: false,
22796     tooltip : '',
22797     
22798     // checkbox success does not make any sense really.. 
22799     invalidClass : "",
22800     validClass : "",
22801     
22802     
22803     getAutoCreate : function()
22804     {
22805         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22806         
22807         var id = Roo.id();
22808         
22809         var cfg = {};
22810         
22811         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22812         
22813         if(this.inline){
22814             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22815         }
22816         
22817         var input =  {
22818             tag: 'input',
22819             id : id,
22820             type : this.inputType,
22821             value : this.inputValue,
22822             cls : 'roo-' + this.inputType, //'form-box',
22823             placeholder : this.placeholder || ''
22824             
22825         };
22826         
22827         if(this.inputType != 'radio'){
22828             var hidden =  {
22829                 tag: 'input',
22830                 type : 'hidden',
22831                 cls : 'roo-hidden-value',
22832                 value : this.checked ? this.inputValue : this.valueOff
22833             };
22834         }
22835         
22836             
22837         if (this.weight) { // Validity check?
22838             cfg.cls += " " + this.inputType + "-" + this.weight;
22839         }
22840         
22841         if (this.disabled) {
22842             input.disabled=true;
22843         }
22844         
22845         if(this.checked){
22846             input.checked = this.checked;
22847         }
22848         
22849         if (this.name) {
22850             
22851             input.name = this.name;
22852             
22853             if(this.inputType != 'radio'){
22854                 hidden.name = this.name;
22855                 input.name = '_hidden_' + this.name;
22856             }
22857         }
22858         
22859         if (this.size) {
22860             input.cls += ' input-' + this.size;
22861         }
22862         
22863         var settings=this;
22864         
22865         ['xs','sm','md','lg'].map(function(size){
22866             if (settings[size]) {
22867                 cfg.cls += ' col-' + size + '-' + settings[size];
22868             }
22869         });
22870         
22871         var inputblock = input;
22872          
22873         if (this.before || this.after) {
22874             
22875             inputblock = {
22876                 cls : 'input-group',
22877                 cn :  [] 
22878             };
22879             
22880             if (this.before) {
22881                 inputblock.cn.push({
22882                     tag :'span',
22883                     cls : 'input-group-addon',
22884                     html : this.before
22885                 });
22886             }
22887             
22888             inputblock.cn.push(input);
22889             
22890             if(this.inputType != 'radio'){
22891                 inputblock.cn.push(hidden);
22892             }
22893             
22894             if (this.after) {
22895                 inputblock.cn.push({
22896                     tag :'span',
22897                     cls : 'input-group-addon',
22898                     html : this.after
22899                 });
22900             }
22901             
22902         }
22903         var boxLabelCfg = false;
22904         
22905         if(this.boxLabel){
22906            
22907             boxLabelCfg = {
22908                 tag: 'label',
22909                 //'for': id, // box label is handled by onclick - so no for...
22910                 cls: 'box-label',
22911                 html: this.boxLabel
22912             };
22913             if(this.tooltip){
22914                 boxLabelCfg.tooltip = this.tooltip;
22915             }
22916              
22917         }
22918         
22919         
22920         if (align ==='left' && this.fieldLabel.length) {
22921 //                Roo.log("left and has label");
22922             cfg.cn = [
22923                 {
22924                     tag: 'label',
22925                     'for' :  id,
22926                     cls : 'control-label',
22927                     html : this.fieldLabel
22928                 },
22929                 {
22930                     cls : "", 
22931                     cn: [
22932                         inputblock
22933                     ]
22934                 }
22935             ];
22936             
22937             if (boxLabelCfg) {
22938                 cfg.cn[1].cn.push(boxLabelCfg);
22939             }
22940             
22941             if(this.labelWidth > 12){
22942                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22943             }
22944             
22945             if(this.labelWidth < 13 && this.labelmd == 0){
22946                 this.labelmd = this.labelWidth;
22947             }
22948             
22949             if(this.labellg > 0){
22950                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22951                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22952             }
22953             
22954             if(this.labelmd > 0){
22955                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22956                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22957             }
22958             
22959             if(this.labelsm > 0){
22960                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22961                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22962             }
22963             
22964             if(this.labelxs > 0){
22965                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22966                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22967             }
22968             
22969         } else if ( this.fieldLabel.length) {
22970 //                Roo.log(" label");
22971                 cfg.cn = [
22972                    
22973                     {
22974                         tag: this.boxLabel ? 'span' : 'label',
22975                         'for': id,
22976                         cls: 'control-label box-input-label',
22977                         //cls : 'input-group-addon',
22978                         html : this.fieldLabel
22979                     },
22980                     
22981                     inputblock
22982                     
22983                 ];
22984                 if (boxLabelCfg) {
22985                     cfg.cn.push(boxLabelCfg);
22986                 }
22987
22988         } else {
22989             
22990 //                Roo.log(" no label && no align");
22991                 cfg.cn = [  inputblock ] ;
22992                 if (boxLabelCfg) {
22993                     cfg.cn.push(boxLabelCfg);
22994                 }
22995
22996                 
22997         }
22998         
22999        
23000         
23001         if(this.inputType != 'radio'){
23002             cfg.cn.push(hidden);
23003         }
23004         
23005         return cfg;
23006         
23007     },
23008     
23009     /**
23010      * return the real input element.
23011      */
23012     inputEl: function ()
23013     {
23014         return this.el.select('input.roo-' + this.inputType,true).first();
23015     },
23016     hiddenEl: function ()
23017     {
23018         return this.el.select('input.roo-hidden-value',true).first();
23019     },
23020     
23021     labelEl: function()
23022     {
23023         return this.el.select('label.control-label',true).first();
23024     },
23025     /* depricated... */
23026     
23027     label: function()
23028     {
23029         return this.labelEl();
23030     },
23031     
23032     boxLabelEl: function()
23033     {
23034         return this.el.select('label.box-label',true).first();
23035     },
23036     
23037     initEvents : function()
23038     {
23039 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23040         
23041         this.inputEl().on('click', this.onClick,  this);
23042         
23043         if (this.boxLabel) { 
23044             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23045         }
23046         
23047         this.startValue = this.getValue();
23048         
23049         if(this.groupId){
23050             Roo.bootstrap.CheckBox.register(this);
23051         }
23052     },
23053     
23054     onClick : function(e)
23055     {   
23056         if(this.fireEvent('click', this, e) !== false){
23057             this.setChecked(!this.checked);
23058         }
23059         
23060     },
23061     
23062     setChecked : function(state,suppressEvent)
23063     {
23064         this.startValue = this.getValue();
23065
23066         if(this.inputType == 'radio'){
23067             
23068             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23069                 e.dom.checked = false;
23070             });
23071             
23072             this.inputEl().dom.checked = true;
23073             
23074             this.inputEl().dom.value = this.inputValue;
23075             
23076             if(suppressEvent !== true){
23077                 this.fireEvent('check', this, true);
23078             }
23079             
23080             this.validate();
23081             
23082             return;
23083         }
23084         
23085         this.checked = state;
23086         
23087         this.inputEl().dom.checked = state;
23088         
23089         
23090         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23091         
23092         if(suppressEvent !== true){
23093             this.fireEvent('check', this, state);
23094         }
23095         
23096         this.validate();
23097     },
23098     
23099     getValue : function()
23100     {
23101         if(this.inputType == 'radio'){
23102             return this.getGroupValue();
23103         }
23104         
23105         return this.hiddenEl().dom.value;
23106         
23107     },
23108     
23109     getGroupValue : function()
23110     {
23111         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23112             return '';
23113         }
23114         
23115         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23116     },
23117     
23118     setValue : function(v,suppressEvent)
23119     {
23120         if(this.inputType == 'radio'){
23121             this.setGroupValue(v, suppressEvent);
23122             return;
23123         }
23124         
23125         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23126         
23127         this.validate();
23128     },
23129     
23130     setGroupValue : function(v, suppressEvent)
23131     {
23132         this.startValue = this.getValue();
23133         
23134         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23135             e.dom.checked = false;
23136             
23137             if(e.dom.value == v){
23138                 e.dom.checked = true;
23139             }
23140         });
23141         
23142         if(suppressEvent !== true){
23143             this.fireEvent('check', this, true);
23144         }
23145
23146         this.validate();
23147         
23148         return;
23149     },
23150     
23151     validate : function()
23152     {
23153         if(this.getVisibilityEl().hasClass('hidden')){
23154             return true;
23155         }
23156         
23157         if(
23158                 this.disabled || 
23159                 (this.inputType == 'radio' && this.validateRadio()) ||
23160                 (this.inputType == 'checkbox' && this.validateCheckbox())
23161         ){
23162             this.markValid();
23163             return true;
23164         }
23165         
23166         this.markInvalid();
23167         return false;
23168     },
23169     
23170     validateRadio : function()
23171     {
23172         if(this.getVisibilityEl().hasClass('hidden')){
23173             return true;
23174         }
23175         
23176         if(this.allowBlank){
23177             return true;
23178         }
23179         
23180         var valid = false;
23181         
23182         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23183             if(!e.dom.checked){
23184                 return;
23185             }
23186             
23187             valid = true;
23188             
23189             return false;
23190         });
23191         
23192         return valid;
23193     },
23194     
23195     validateCheckbox : function()
23196     {
23197         if(!this.groupId){
23198             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23199             //return (this.getValue() == this.inputValue) ? true : false;
23200         }
23201         
23202         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23203         
23204         if(!group){
23205             return false;
23206         }
23207         
23208         var r = false;
23209         
23210         for(var i in group){
23211             if(group[i].el.isVisible(true)){
23212                 r = false;
23213                 break;
23214             }
23215             
23216             r = true;
23217         }
23218         
23219         for(var i in group){
23220             if(r){
23221                 break;
23222             }
23223             
23224             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23225         }
23226         
23227         return r;
23228     },
23229     
23230     /**
23231      * Mark this field as valid
23232      */
23233     markValid : function()
23234     {
23235         var _this = this;
23236         
23237         this.fireEvent('valid', this);
23238         
23239         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23240         
23241         if(this.groupId){
23242             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23243         }
23244         
23245         if(label){
23246             label.markValid();
23247         }
23248
23249         if(this.inputType == 'radio'){
23250             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23251                 var fg = e.findParent('.form-group', false, true);
23252                 if (Roo.bootstrap.version == 3) {
23253                     fg.removeClass([_this.invalidClass, _this.validClass]);
23254                     fg.addClass(_this.validClass);
23255                 } else {
23256                     fg.removeClass(['is-valid', 'is-invalid']);
23257                     fg.addClass('is-valid');
23258                 }
23259             });
23260             
23261             return;
23262         }
23263
23264         if(!this.groupId){
23265             var fg = this.el.findParent('.form-group', false, true);
23266             if (Roo.bootstrap.version == 3) {
23267                 fg.removeClass([this.invalidClass, this.validClass]);
23268                 fg.addClass(this.validClass);
23269             } else {
23270                 fg.removeClass(['is-valid', 'is-invalid']);
23271                 fg.addClass('is-valid');
23272             }
23273             return;
23274         }
23275         
23276         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23277         
23278         if(!group){
23279             return;
23280         }
23281         
23282         for(var i in group){
23283             var fg = group[i].el.findParent('.form-group', false, true);
23284             if (Roo.bootstrap.version == 3) {
23285                 fg.removeClass([this.invalidClass, this.validClass]);
23286                 fg.addClass(this.validClass);
23287             } else {
23288                 fg.removeClass(['is-valid', 'is-invalid']);
23289                 fg.addClass('is-valid');
23290             }
23291         }
23292     },
23293     
23294      /**
23295      * Mark this field as invalid
23296      * @param {String} msg The validation message
23297      */
23298     markInvalid : function(msg)
23299     {
23300         if(this.allowBlank){
23301             return;
23302         }
23303         
23304         var _this = this;
23305         
23306         this.fireEvent('invalid', this, msg);
23307         
23308         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23309         
23310         if(this.groupId){
23311             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23312         }
23313         
23314         if(label){
23315             label.markInvalid();
23316         }
23317             
23318         if(this.inputType == 'radio'){
23319             
23320             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23321                 var fg = e.findParent('.form-group', false, true);
23322                 if (Roo.bootstrap.version == 3) {
23323                     fg.removeClass([_this.invalidClass, _this.validClass]);
23324                     fg.addClass(_this.invalidClass);
23325                 } else {
23326                     fg.removeClass(['is-invalid', 'is-valid']);
23327                     fg.addClass('is-invalid');
23328                 }
23329             });
23330             
23331             return;
23332         }
23333         
23334         if(!this.groupId){
23335             var fg = this.el.findParent('.form-group', false, true);
23336             if (Roo.bootstrap.version == 3) {
23337                 fg.removeClass([_this.invalidClass, _this.validClass]);
23338                 fg.addClass(_this.invalidClass);
23339             } else {
23340                 fg.removeClass(['is-invalid', 'is-valid']);
23341                 fg.addClass('is-invalid');
23342             }
23343             return;
23344         }
23345         
23346         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23347         
23348         if(!group){
23349             return;
23350         }
23351         
23352         for(var i in group){
23353             var fg = group[i].el.findParent('.form-group', false, true);
23354             if (Roo.bootstrap.version == 3) {
23355                 fg.removeClass([_this.invalidClass, _this.validClass]);
23356                 fg.addClass(_this.invalidClass);
23357             } else {
23358                 fg.removeClass(['is-invalid', 'is-valid']);
23359                 fg.addClass('is-invalid');
23360             }
23361         }
23362         
23363     },
23364     
23365     clearInvalid : function()
23366     {
23367         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23368         
23369         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23370         
23371         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23372         
23373         if (label && label.iconEl) {
23374             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23375             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23376         }
23377     },
23378     
23379     disable : function()
23380     {
23381         if(this.inputType != 'radio'){
23382             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23383             return;
23384         }
23385         
23386         var _this = this;
23387         
23388         if(this.rendered){
23389             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23390                 _this.getActionEl().addClass(this.disabledClass);
23391                 e.dom.disabled = true;
23392             });
23393         }
23394         
23395         this.disabled = true;
23396         this.fireEvent("disable", this);
23397         return this;
23398     },
23399
23400     enable : function()
23401     {
23402         if(this.inputType != 'radio'){
23403             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23404             return;
23405         }
23406         
23407         var _this = this;
23408         
23409         if(this.rendered){
23410             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23411                 _this.getActionEl().removeClass(this.disabledClass);
23412                 e.dom.disabled = false;
23413             });
23414         }
23415         
23416         this.disabled = false;
23417         this.fireEvent("enable", this);
23418         return this;
23419     },
23420     
23421     setBoxLabel : function(v)
23422     {
23423         this.boxLabel = v;
23424         
23425         if(this.rendered){
23426             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23427         }
23428     }
23429
23430 });
23431
23432 Roo.apply(Roo.bootstrap.CheckBox, {
23433     
23434     groups: {},
23435     
23436      /**
23437     * register a CheckBox Group
23438     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23439     */
23440     register : function(checkbox)
23441     {
23442         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23443             this.groups[checkbox.groupId] = {};
23444         }
23445         
23446         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23447             return;
23448         }
23449         
23450         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23451         
23452     },
23453     /**
23454     * fetch a CheckBox Group based on the group ID
23455     * @param {string} the group ID
23456     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23457     */
23458     get: function(groupId) {
23459         if (typeof(this.groups[groupId]) == 'undefined') {
23460             return false;
23461         }
23462         
23463         return this.groups[groupId] ;
23464     }
23465     
23466     
23467 });
23468 /*
23469  * - LGPL
23470  *
23471  * RadioItem
23472  * 
23473  */
23474
23475 /**
23476  * @class Roo.bootstrap.Radio
23477  * @extends Roo.bootstrap.Component
23478  * Bootstrap Radio class
23479  * @cfg {String} boxLabel - the label associated
23480  * @cfg {String} value - the value of radio
23481  * 
23482  * @constructor
23483  * Create a new Radio
23484  * @param {Object} config The config object
23485  */
23486 Roo.bootstrap.Radio = function(config){
23487     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23488     
23489 };
23490
23491 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23492     
23493     boxLabel : '',
23494     
23495     value : '',
23496     
23497     getAutoCreate : function()
23498     {
23499         var cfg = {
23500             tag : 'div',
23501             cls : 'form-group radio',
23502             cn : [
23503                 {
23504                     tag : 'label',
23505                     cls : 'box-label',
23506                     html : this.boxLabel
23507                 }
23508             ]
23509         };
23510         
23511         return cfg;
23512     },
23513     
23514     initEvents : function() 
23515     {
23516         this.parent().register(this);
23517         
23518         this.el.on('click', this.onClick, this);
23519         
23520     },
23521     
23522     onClick : function(e)
23523     {
23524         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23525             this.setChecked(true);
23526         }
23527     },
23528     
23529     setChecked : function(state, suppressEvent)
23530     {
23531         this.parent().setValue(this.value, suppressEvent);
23532         
23533     },
23534     
23535     setBoxLabel : function(v)
23536     {
23537         this.boxLabel = v;
23538         
23539         if(this.rendered){
23540             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23541         }
23542     }
23543     
23544 });
23545  
23546
23547  /*
23548  * - LGPL
23549  *
23550  * Input
23551  * 
23552  */
23553
23554 /**
23555  * @class Roo.bootstrap.SecurePass
23556  * @extends Roo.bootstrap.Input
23557  * Bootstrap SecurePass class
23558  *
23559  * 
23560  * @constructor
23561  * Create a new SecurePass
23562  * @param {Object} config The config object
23563  */
23564  
23565 Roo.bootstrap.SecurePass = function (config) {
23566     // these go here, so the translation tool can replace them..
23567     this.errors = {
23568         PwdEmpty: "Please type a password, and then retype it to confirm.",
23569         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23570         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23571         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23572         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23573         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23574         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23575         TooWeak: "Your password is Too Weak."
23576     },
23577     this.meterLabel = "Password strength:";
23578     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23579     this.meterClass = [
23580         "roo-password-meter-tooweak", 
23581         "roo-password-meter-weak", 
23582         "roo-password-meter-medium", 
23583         "roo-password-meter-strong", 
23584         "roo-password-meter-grey"
23585     ];
23586     
23587     this.errors = {};
23588     
23589     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23590 }
23591
23592 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23593     /**
23594      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23595      * {
23596      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23597      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23598      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23599      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23600      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23601      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23602      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23603      * })
23604      */
23605     // private
23606     
23607     meterWidth: 300,
23608     errorMsg :'',    
23609     errors: false,
23610     imageRoot: '/',
23611     /**
23612      * @cfg {String/Object} Label for the strength meter (defaults to
23613      * 'Password strength:')
23614      */
23615     // private
23616     meterLabel: '',
23617     /**
23618      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23619      * ['Weak', 'Medium', 'Strong'])
23620      */
23621     // private    
23622     pwdStrengths: false,    
23623     // private
23624     strength: 0,
23625     // private
23626     _lastPwd: null,
23627     // private
23628     kCapitalLetter: 0,
23629     kSmallLetter: 1,
23630     kDigit: 2,
23631     kPunctuation: 3,
23632     
23633     insecure: false,
23634     // private
23635     initEvents: function ()
23636     {
23637         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23638
23639         if (this.el.is('input[type=password]') && Roo.isSafari) {
23640             this.el.on('keydown', this.SafariOnKeyDown, this);
23641         }
23642
23643         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23644     },
23645     // private
23646     onRender: function (ct, position)
23647     {
23648         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23649         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23650         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23651
23652         this.trigger.createChild({
23653                    cn: [
23654                     {
23655                     //id: 'PwdMeter',
23656                     tag: 'div',
23657                     cls: 'roo-password-meter-grey col-xs-12',
23658                     style: {
23659                         //width: 0,
23660                         //width: this.meterWidth + 'px'                                                
23661                         }
23662                     },
23663                     {                            
23664                          cls: 'roo-password-meter-text'                          
23665                     }
23666                 ]            
23667         });
23668
23669          
23670         if (this.hideTrigger) {
23671             this.trigger.setDisplayed(false);
23672         }
23673         this.setSize(this.width || '', this.height || '');
23674     },
23675     // private
23676     onDestroy: function ()
23677     {
23678         if (this.trigger) {
23679             this.trigger.removeAllListeners();
23680             this.trigger.remove();
23681         }
23682         if (this.wrap) {
23683             this.wrap.remove();
23684         }
23685         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23686     },
23687     // private
23688     checkStrength: function ()
23689     {
23690         var pwd = this.inputEl().getValue();
23691         if (pwd == this._lastPwd) {
23692             return;
23693         }
23694
23695         var strength;
23696         if (this.ClientSideStrongPassword(pwd)) {
23697             strength = 3;
23698         } else if (this.ClientSideMediumPassword(pwd)) {
23699             strength = 2;
23700         } else if (this.ClientSideWeakPassword(pwd)) {
23701             strength = 1;
23702         } else {
23703             strength = 0;
23704         }
23705         
23706         Roo.log('strength1: ' + strength);
23707         
23708         //var pm = this.trigger.child('div/div/div').dom;
23709         var pm = this.trigger.child('div/div');
23710         pm.removeClass(this.meterClass);
23711         pm.addClass(this.meterClass[strength]);
23712                 
23713         
23714         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23715                 
23716         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23717         
23718         this._lastPwd = pwd;
23719     },
23720     reset: function ()
23721     {
23722         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23723         
23724         this._lastPwd = '';
23725         
23726         var pm = this.trigger.child('div/div');
23727         pm.removeClass(this.meterClass);
23728         pm.addClass('roo-password-meter-grey');        
23729         
23730         
23731         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23732         
23733         pt.innerHTML = '';
23734         this.inputEl().dom.type='password';
23735     },
23736     // private
23737     validateValue: function (value)
23738     {
23739         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23740             return false;
23741         }
23742         if (value.length == 0) {
23743             if (this.allowBlank) {
23744                 this.clearInvalid();
23745                 return true;
23746             }
23747
23748             this.markInvalid(this.errors.PwdEmpty);
23749             this.errorMsg = this.errors.PwdEmpty;
23750             return false;
23751         }
23752         
23753         if(this.insecure){
23754             return true;
23755         }
23756         
23757         if (!value.match(/[\x21-\x7e]+/)) {
23758             this.markInvalid(this.errors.PwdBadChar);
23759             this.errorMsg = this.errors.PwdBadChar;
23760             return false;
23761         }
23762         if (value.length < 6) {
23763             this.markInvalid(this.errors.PwdShort);
23764             this.errorMsg = this.errors.PwdShort;
23765             return false;
23766         }
23767         if (value.length > 16) {
23768             this.markInvalid(this.errors.PwdLong);
23769             this.errorMsg = this.errors.PwdLong;
23770             return false;
23771         }
23772         var strength;
23773         if (this.ClientSideStrongPassword(value)) {
23774             strength = 3;
23775         } else if (this.ClientSideMediumPassword(value)) {
23776             strength = 2;
23777         } else if (this.ClientSideWeakPassword(value)) {
23778             strength = 1;
23779         } else {
23780             strength = 0;
23781         }
23782
23783         
23784         if (strength < 2) {
23785             //this.markInvalid(this.errors.TooWeak);
23786             this.errorMsg = this.errors.TooWeak;
23787             //return false;
23788         }
23789         
23790         
23791         console.log('strength2: ' + strength);
23792         
23793         //var pm = this.trigger.child('div/div/div').dom;
23794         
23795         var pm = this.trigger.child('div/div');
23796         pm.removeClass(this.meterClass);
23797         pm.addClass(this.meterClass[strength]);
23798                 
23799         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23800                 
23801         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23802         
23803         this.errorMsg = ''; 
23804         return true;
23805     },
23806     // private
23807     CharacterSetChecks: function (type)
23808     {
23809         this.type = type;
23810         this.fResult = false;
23811     },
23812     // private
23813     isctype: function (character, type)
23814     {
23815         switch (type) {  
23816             case this.kCapitalLetter:
23817                 if (character >= 'A' && character <= 'Z') {
23818                     return true;
23819                 }
23820                 break;
23821             
23822             case this.kSmallLetter:
23823                 if (character >= 'a' && character <= 'z') {
23824                     return true;
23825                 }
23826                 break;
23827             
23828             case this.kDigit:
23829                 if (character >= '0' && character <= '9') {
23830                     return true;
23831                 }
23832                 break;
23833             
23834             case this.kPunctuation:
23835                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23836                     return true;
23837                 }
23838                 break;
23839             
23840             default:
23841                 return false;
23842         }
23843
23844     },
23845     // private
23846     IsLongEnough: function (pwd, size)
23847     {
23848         return !(pwd == null || isNaN(size) || pwd.length < size);
23849     },
23850     // private
23851     SpansEnoughCharacterSets: function (word, nb)
23852     {
23853         if (!this.IsLongEnough(word, nb))
23854         {
23855             return false;
23856         }
23857
23858         var characterSetChecks = new Array(
23859             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23860             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23861         );
23862         
23863         for (var index = 0; index < word.length; ++index) {
23864             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23865                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23866                     characterSetChecks[nCharSet].fResult = true;
23867                     break;
23868                 }
23869             }
23870         }
23871
23872         var nCharSets = 0;
23873         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23874             if (characterSetChecks[nCharSet].fResult) {
23875                 ++nCharSets;
23876             }
23877         }
23878
23879         if (nCharSets < nb) {
23880             return false;
23881         }
23882         return true;
23883     },
23884     // private
23885     ClientSideStrongPassword: function (pwd)
23886     {
23887         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23888     },
23889     // private
23890     ClientSideMediumPassword: function (pwd)
23891     {
23892         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23893     },
23894     // private
23895     ClientSideWeakPassword: function (pwd)
23896     {
23897         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23898     }
23899           
23900 })//<script type="text/javascript">
23901
23902 /*
23903  * Based  Ext JS Library 1.1.1
23904  * Copyright(c) 2006-2007, Ext JS, LLC.
23905  * LGPL
23906  *
23907  */
23908  
23909 /**
23910  * @class Roo.HtmlEditorCore
23911  * @extends Roo.Component
23912  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23913  *
23914  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23915  */
23916
23917 Roo.HtmlEditorCore = function(config){
23918     
23919     
23920     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23921     
23922     
23923     this.addEvents({
23924         /**
23925          * @event initialize
23926          * Fires when the editor is fully initialized (including the iframe)
23927          * @param {Roo.HtmlEditorCore} this
23928          */
23929         initialize: true,
23930         /**
23931          * @event activate
23932          * Fires when the editor is first receives the focus. Any insertion must wait
23933          * until after this event.
23934          * @param {Roo.HtmlEditorCore} this
23935          */
23936         activate: true,
23937          /**
23938          * @event beforesync
23939          * Fires before the textarea is updated with content from the editor iframe. Return false
23940          * to cancel the sync.
23941          * @param {Roo.HtmlEditorCore} this
23942          * @param {String} html
23943          */
23944         beforesync: true,
23945          /**
23946          * @event beforepush
23947          * Fires before the iframe editor is updated with content from the textarea. Return false
23948          * to cancel the push.
23949          * @param {Roo.HtmlEditorCore} this
23950          * @param {String} html
23951          */
23952         beforepush: true,
23953          /**
23954          * @event sync
23955          * Fires when the textarea is updated with content from the editor iframe.
23956          * @param {Roo.HtmlEditorCore} this
23957          * @param {String} html
23958          */
23959         sync: true,
23960          /**
23961          * @event push
23962          * Fires when the iframe editor is updated with content from the textarea.
23963          * @param {Roo.HtmlEditorCore} this
23964          * @param {String} html
23965          */
23966         push: true,
23967         
23968         /**
23969          * @event editorevent
23970          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23971          * @param {Roo.HtmlEditorCore} this
23972          */
23973         editorevent: true
23974         
23975     });
23976     
23977     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23978     
23979     // defaults : white / black...
23980     this.applyBlacklists();
23981     
23982     
23983     
23984 };
23985
23986
23987 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23988
23989
23990      /**
23991      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23992      */
23993     
23994     owner : false,
23995     
23996      /**
23997      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23998      *                        Roo.resizable.
23999      */
24000     resizable : false,
24001      /**
24002      * @cfg {Number} height (in pixels)
24003      */   
24004     height: 300,
24005    /**
24006      * @cfg {Number} width (in pixels)
24007      */   
24008     width: 500,
24009     
24010     /**
24011      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24012      * 
24013      */
24014     stylesheets: false,
24015     
24016     // id of frame..
24017     frameId: false,
24018     
24019     // private properties
24020     validationEvent : false,
24021     deferHeight: true,
24022     initialized : false,
24023     activated : false,
24024     sourceEditMode : false,
24025     onFocus : Roo.emptyFn,
24026     iframePad:3,
24027     hideMode:'offsets',
24028     
24029     clearUp: true,
24030     
24031     // blacklist + whitelisted elements..
24032     black: false,
24033     white: false,
24034      
24035     bodyCls : '',
24036
24037     /**
24038      * Protected method that will not generally be called directly. It
24039      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24040      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24041      */
24042     getDocMarkup : function(){
24043         // body styles..
24044         var st = '';
24045         
24046         // inherit styels from page...?? 
24047         if (this.stylesheets === false) {
24048             
24049             Roo.get(document.head).select('style').each(function(node) {
24050                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24051             });
24052             
24053             Roo.get(document.head).select('link').each(function(node) { 
24054                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24055             });
24056             
24057         } else if (!this.stylesheets.length) {
24058                 // simple..
24059                 st = '<style type="text/css">' +
24060                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24061                    '</style>';
24062         } else {
24063             for (var i in this.stylesheets) { 
24064                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24065             }
24066             
24067         }
24068         
24069         st +=  '<style type="text/css">' +
24070             'IMG { cursor: pointer } ' +
24071         '</style>';
24072
24073         var cls = 'roo-htmleditor-body';
24074         
24075         if(this.bodyCls.length){
24076             cls += ' ' + this.bodyCls;
24077         }
24078         
24079         return '<html><head>' + st  +
24080             //<style type="text/css">' +
24081             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24082             //'</style>' +
24083             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24084     },
24085
24086     // private
24087     onRender : function(ct, position)
24088     {
24089         var _t = this;
24090         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24091         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24092         
24093         
24094         this.el.dom.style.border = '0 none';
24095         this.el.dom.setAttribute('tabIndex', -1);
24096         this.el.addClass('x-hidden hide');
24097         
24098         
24099         
24100         if(Roo.isIE){ // fix IE 1px bogus margin
24101             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24102         }
24103        
24104         
24105         this.frameId = Roo.id();
24106         
24107          
24108         
24109         var iframe = this.owner.wrap.createChild({
24110             tag: 'iframe',
24111             cls: 'form-control', // bootstrap..
24112             id: this.frameId,
24113             name: this.frameId,
24114             frameBorder : 'no',
24115             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24116         }, this.el
24117         );
24118         
24119         
24120         this.iframe = iframe.dom;
24121
24122          this.assignDocWin();
24123         
24124         this.doc.designMode = 'on';
24125        
24126         this.doc.open();
24127         this.doc.write(this.getDocMarkup());
24128         this.doc.close();
24129
24130         
24131         var task = { // must defer to wait for browser to be ready
24132             run : function(){
24133                 //console.log("run task?" + this.doc.readyState);
24134                 this.assignDocWin();
24135                 if(this.doc.body || this.doc.readyState == 'complete'){
24136                     try {
24137                         this.doc.designMode="on";
24138                     } catch (e) {
24139                         return;
24140                     }
24141                     Roo.TaskMgr.stop(task);
24142                     this.initEditor.defer(10, this);
24143                 }
24144             },
24145             interval : 10,
24146             duration: 10000,
24147             scope: this
24148         };
24149         Roo.TaskMgr.start(task);
24150
24151     },
24152
24153     // private
24154     onResize : function(w, h)
24155     {
24156          Roo.log('resize: ' +w + ',' + h );
24157         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24158         if(!this.iframe){
24159             return;
24160         }
24161         if(typeof w == 'number'){
24162             
24163             this.iframe.style.width = w + 'px';
24164         }
24165         if(typeof h == 'number'){
24166             
24167             this.iframe.style.height = h + 'px';
24168             if(this.doc){
24169                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24170             }
24171         }
24172         
24173     },
24174
24175     /**
24176      * Toggles the editor between standard and source edit mode.
24177      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24178      */
24179     toggleSourceEdit : function(sourceEditMode){
24180         
24181         this.sourceEditMode = sourceEditMode === true;
24182         
24183         if(this.sourceEditMode){
24184  
24185             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24186             
24187         }else{
24188             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24189             //this.iframe.className = '';
24190             this.deferFocus();
24191         }
24192         //this.setSize(this.owner.wrap.getSize());
24193         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24194     },
24195
24196     
24197   
24198
24199     /**
24200      * Protected method that will not generally be called directly. If you need/want
24201      * custom HTML cleanup, this is the method you should override.
24202      * @param {String} html The HTML to be cleaned
24203      * return {String} The cleaned HTML
24204      */
24205     cleanHtml : function(html){
24206         html = String(html);
24207         if(html.length > 5){
24208             if(Roo.isSafari){ // strip safari nonsense
24209                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24210             }
24211         }
24212         if(html == '&nbsp;'){
24213             html = '';
24214         }
24215         return html;
24216     },
24217
24218     /**
24219      * HTML Editor -> Textarea
24220      * Protected method that will not generally be called directly. Syncs the contents
24221      * of the editor iframe with the textarea.
24222      */
24223     syncValue : function(){
24224         if(this.initialized){
24225             var bd = (this.doc.body || this.doc.documentElement);
24226             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24227             var html = bd.innerHTML;
24228             if(Roo.isSafari){
24229                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24230                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24231                 if(m && m[1]){
24232                     html = '<div style="'+m[0]+'">' + html + '</div>';
24233                 }
24234             }
24235             html = this.cleanHtml(html);
24236             // fix up the special chars.. normaly like back quotes in word...
24237             // however we do not want to do this with chinese..
24238             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24239                 
24240                 var cc = match.charCodeAt();
24241
24242                 // Get the character value, handling surrogate pairs
24243                 if (match.length == 2) {
24244                     // It's a surrogate pair, calculate the Unicode code point
24245                     var high = match.charCodeAt(0) - 0xD800;
24246                     var low  = match.charCodeAt(1) - 0xDC00;
24247                     cc = (high * 0x400) + low + 0x10000;
24248                 }  else if (
24249                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24250                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24251                     (cc >= 0xf900 && cc < 0xfb00 )
24252                 ) {
24253                         return match;
24254                 }  
24255          
24256                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24257                 return "&#" + cc + ";";
24258                 
24259                 
24260             });
24261             
24262             
24263              
24264             if(this.owner.fireEvent('beforesync', this, html) !== false){
24265                 this.el.dom.value = html;
24266                 this.owner.fireEvent('sync', this, html);
24267             }
24268         }
24269     },
24270
24271     /**
24272      * Protected method that will not generally be called directly. Pushes the value of the textarea
24273      * into the iframe editor.
24274      */
24275     pushValue : function(){
24276         if(this.initialized){
24277             var v = this.el.dom.value.trim();
24278             
24279 //            if(v.length < 1){
24280 //                v = '&#160;';
24281 //            }
24282             
24283             if(this.owner.fireEvent('beforepush', this, v) !== false){
24284                 var d = (this.doc.body || this.doc.documentElement);
24285                 d.innerHTML = v;
24286                 this.cleanUpPaste();
24287                 this.el.dom.value = d.innerHTML;
24288                 this.owner.fireEvent('push', this, v);
24289             }
24290         }
24291     },
24292
24293     // private
24294     deferFocus : function(){
24295         this.focus.defer(10, this);
24296     },
24297
24298     // doc'ed in Field
24299     focus : function(){
24300         if(this.win && !this.sourceEditMode){
24301             this.win.focus();
24302         }else{
24303             this.el.focus();
24304         }
24305     },
24306     
24307     assignDocWin: function()
24308     {
24309         var iframe = this.iframe;
24310         
24311          if(Roo.isIE){
24312             this.doc = iframe.contentWindow.document;
24313             this.win = iframe.contentWindow;
24314         } else {
24315 //            if (!Roo.get(this.frameId)) {
24316 //                return;
24317 //            }
24318 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24319 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24320             
24321             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24322                 return;
24323             }
24324             
24325             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24326             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24327         }
24328     },
24329     
24330     // private
24331     initEditor : function(){
24332         //console.log("INIT EDITOR");
24333         this.assignDocWin();
24334         
24335         
24336         
24337         this.doc.designMode="on";
24338         this.doc.open();
24339         this.doc.write(this.getDocMarkup());
24340         this.doc.close();
24341         
24342         var dbody = (this.doc.body || this.doc.documentElement);
24343         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24344         // this copies styles from the containing element into thsi one..
24345         // not sure why we need all of this..
24346         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24347         
24348         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24349         //ss['background-attachment'] = 'fixed'; // w3c
24350         dbody.bgProperties = 'fixed'; // ie
24351         //Roo.DomHelper.applyStyles(dbody, ss);
24352         Roo.EventManager.on(this.doc, {
24353             //'mousedown': this.onEditorEvent,
24354             'mouseup': this.onEditorEvent,
24355             'dblclick': this.onEditorEvent,
24356             'click': this.onEditorEvent,
24357             'keyup': this.onEditorEvent,
24358             buffer:100,
24359             scope: this
24360         });
24361         if(Roo.isGecko){
24362             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24363         }
24364         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24365             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24366         }
24367         this.initialized = true;
24368
24369         this.owner.fireEvent('initialize', this);
24370         this.pushValue();
24371     },
24372
24373     // private
24374     onDestroy : function(){
24375         
24376         
24377         
24378         if(this.rendered){
24379             
24380             //for (var i =0; i < this.toolbars.length;i++) {
24381             //    // fixme - ask toolbars for heights?
24382             //    this.toolbars[i].onDestroy();
24383            // }
24384             
24385             //this.wrap.dom.innerHTML = '';
24386             //this.wrap.remove();
24387         }
24388     },
24389
24390     // private
24391     onFirstFocus : function(){
24392         
24393         this.assignDocWin();
24394         
24395         
24396         this.activated = true;
24397          
24398     
24399         if(Roo.isGecko){ // prevent silly gecko errors
24400             this.win.focus();
24401             var s = this.win.getSelection();
24402             if(!s.focusNode || s.focusNode.nodeType != 3){
24403                 var r = s.getRangeAt(0);
24404                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24405                 r.collapse(true);
24406                 this.deferFocus();
24407             }
24408             try{
24409                 this.execCmd('useCSS', true);
24410                 this.execCmd('styleWithCSS', false);
24411             }catch(e){}
24412         }
24413         this.owner.fireEvent('activate', this);
24414     },
24415
24416     // private
24417     adjustFont: function(btn){
24418         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24419         //if(Roo.isSafari){ // safari
24420         //    adjust *= 2;
24421        // }
24422         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24423         if(Roo.isSafari){ // safari
24424             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24425             v =  (v < 10) ? 10 : v;
24426             v =  (v > 48) ? 48 : v;
24427             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24428             
24429         }
24430         
24431         
24432         v = Math.max(1, v+adjust);
24433         
24434         this.execCmd('FontSize', v  );
24435     },
24436
24437     onEditorEvent : function(e)
24438     {
24439         this.owner.fireEvent('editorevent', this, e);
24440       //  this.updateToolbar();
24441         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24442     },
24443
24444     insertTag : function(tg)
24445     {
24446         // could be a bit smarter... -> wrap the current selected tRoo..
24447         if (tg.toLowerCase() == 'span' ||
24448             tg.toLowerCase() == 'code' ||
24449             tg.toLowerCase() == 'sup' ||
24450             tg.toLowerCase() == 'sub' 
24451             ) {
24452             
24453             range = this.createRange(this.getSelection());
24454             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24455             wrappingNode.appendChild(range.extractContents());
24456             range.insertNode(wrappingNode);
24457
24458             return;
24459             
24460             
24461             
24462         }
24463         this.execCmd("formatblock",   tg);
24464         
24465     },
24466     
24467     insertText : function(txt)
24468     {
24469         
24470         
24471         var range = this.createRange();
24472         range.deleteContents();
24473                //alert(Sender.getAttribute('label'));
24474                
24475         range.insertNode(this.doc.createTextNode(txt));
24476     } ,
24477     
24478      
24479
24480     /**
24481      * Executes a Midas editor command on the editor document and performs necessary focus and
24482      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24483      * @param {String} cmd The Midas command
24484      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24485      */
24486     relayCmd : function(cmd, value){
24487         this.win.focus();
24488         this.execCmd(cmd, value);
24489         this.owner.fireEvent('editorevent', this);
24490         //this.updateToolbar();
24491         this.owner.deferFocus();
24492     },
24493
24494     /**
24495      * Executes a Midas editor command directly on the editor document.
24496      * For visual commands, you should use {@link #relayCmd} instead.
24497      * <b>This should only be called after the editor is initialized.</b>
24498      * @param {String} cmd The Midas command
24499      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24500      */
24501     execCmd : function(cmd, value){
24502         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24503         this.syncValue();
24504     },
24505  
24506  
24507    
24508     /**
24509      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24510      * to insert tRoo.
24511      * @param {String} text | dom node.. 
24512      */
24513     insertAtCursor : function(text)
24514     {
24515         
24516         if(!this.activated){
24517             return;
24518         }
24519         /*
24520         if(Roo.isIE){
24521             this.win.focus();
24522             var r = this.doc.selection.createRange();
24523             if(r){
24524                 r.collapse(true);
24525                 r.pasteHTML(text);
24526                 this.syncValue();
24527                 this.deferFocus();
24528             
24529             }
24530             return;
24531         }
24532         */
24533         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24534             this.win.focus();
24535             
24536             
24537             // from jquery ui (MIT licenced)
24538             var range, node;
24539             var win = this.win;
24540             
24541             if (win.getSelection && win.getSelection().getRangeAt) {
24542                 range = win.getSelection().getRangeAt(0);
24543                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24544                 range.insertNode(node);
24545             } else if (win.document.selection && win.document.selection.createRange) {
24546                 // no firefox support
24547                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24548                 win.document.selection.createRange().pasteHTML(txt);
24549             } else {
24550                 // no firefox support
24551                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24552                 this.execCmd('InsertHTML', txt);
24553             } 
24554             
24555             this.syncValue();
24556             
24557             this.deferFocus();
24558         }
24559     },
24560  // private
24561     mozKeyPress : function(e){
24562         if(e.ctrlKey){
24563             var c = e.getCharCode(), cmd;
24564           
24565             if(c > 0){
24566                 c = String.fromCharCode(c).toLowerCase();
24567                 switch(c){
24568                     case 'b':
24569                         cmd = 'bold';
24570                         break;
24571                     case 'i':
24572                         cmd = 'italic';
24573                         break;
24574                     
24575                     case 'u':
24576                         cmd = 'underline';
24577                         break;
24578                     
24579                     case 'v':
24580                         this.cleanUpPaste.defer(100, this);
24581                         return;
24582                         
24583                 }
24584                 if(cmd){
24585                     this.win.focus();
24586                     this.execCmd(cmd);
24587                     this.deferFocus();
24588                     e.preventDefault();
24589                 }
24590                 
24591             }
24592         }
24593     },
24594
24595     // private
24596     fixKeys : function(){ // load time branching for fastest keydown performance
24597         if(Roo.isIE){
24598             return function(e){
24599                 var k = e.getKey(), r;
24600                 if(k == e.TAB){
24601                     e.stopEvent();
24602                     r = this.doc.selection.createRange();
24603                     if(r){
24604                         r.collapse(true);
24605                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24606                         this.deferFocus();
24607                     }
24608                     return;
24609                 }
24610                 
24611                 if(k == e.ENTER){
24612                     r = this.doc.selection.createRange();
24613                     if(r){
24614                         var target = r.parentElement();
24615                         if(!target || target.tagName.toLowerCase() != 'li'){
24616                             e.stopEvent();
24617                             r.pasteHTML('<br />');
24618                             r.collapse(false);
24619                             r.select();
24620                         }
24621                     }
24622                 }
24623                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24624                     this.cleanUpPaste.defer(100, this);
24625                     return;
24626                 }
24627                 
24628                 
24629             };
24630         }else if(Roo.isOpera){
24631             return function(e){
24632                 var k = e.getKey();
24633                 if(k == e.TAB){
24634                     e.stopEvent();
24635                     this.win.focus();
24636                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24637                     this.deferFocus();
24638                 }
24639                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24640                     this.cleanUpPaste.defer(100, this);
24641                     return;
24642                 }
24643                 
24644             };
24645         }else if(Roo.isSafari){
24646             return function(e){
24647                 var k = e.getKey();
24648                 
24649                 if(k == e.TAB){
24650                     e.stopEvent();
24651                     this.execCmd('InsertText','\t');
24652                     this.deferFocus();
24653                     return;
24654                 }
24655                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24656                     this.cleanUpPaste.defer(100, this);
24657                     return;
24658                 }
24659                 
24660              };
24661         }
24662     }(),
24663     
24664     getAllAncestors: function()
24665     {
24666         var p = this.getSelectedNode();
24667         var a = [];
24668         if (!p) {
24669             a.push(p); // push blank onto stack..
24670             p = this.getParentElement();
24671         }
24672         
24673         
24674         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24675             a.push(p);
24676             p = p.parentNode;
24677         }
24678         a.push(this.doc.body);
24679         return a;
24680     },
24681     lastSel : false,
24682     lastSelNode : false,
24683     
24684     
24685     getSelection : function() 
24686     {
24687         this.assignDocWin();
24688         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24689     },
24690     
24691     getSelectedNode: function() 
24692     {
24693         // this may only work on Gecko!!!
24694         
24695         // should we cache this!!!!
24696         
24697         
24698         
24699          
24700         var range = this.createRange(this.getSelection()).cloneRange();
24701         
24702         if (Roo.isIE) {
24703             var parent = range.parentElement();
24704             while (true) {
24705                 var testRange = range.duplicate();
24706                 testRange.moveToElementText(parent);
24707                 if (testRange.inRange(range)) {
24708                     break;
24709                 }
24710                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24711                     break;
24712                 }
24713                 parent = parent.parentElement;
24714             }
24715             return parent;
24716         }
24717         
24718         // is ancestor a text element.
24719         var ac =  range.commonAncestorContainer;
24720         if (ac.nodeType == 3) {
24721             ac = ac.parentNode;
24722         }
24723         
24724         var ar = ac.childNodes;
24725          
24726         var nodes = [];
24727         var other_nodes = [];
24728         var has_other_nodes = false;
24729         for (var i=0;i<ar.length;i++) {
24730             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24731                 continue;
24732             }
24733             // fullly contained node.
24734             
24735             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24736                 nodes.push(ar[i]);
24737                 continue;
24738             }
24739             
24740             // probably selected..
24741             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24742                 other_nodes.push(ar[i]);
24743                 continue;
24744             }
24745             // outer..
24746             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24747                 continue;
24748             }
24749             
24750             
24751             has_other_nodes = true;
24752         }
24753         if (!nodes.length && other_nodes.length) {
24754             nodes= other_nodes;
24755         }
24756         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24757             return false;
24758         }
24759         
24760         return nodes[0];
24761     },
24762     createRange: function(sel)
24763     {
24764         // this has strange effects when using with 
24765         // top toolbar - not sure if it's a great idea.
24766         //this.editor.contentWindow.focus();
24767         if (typeof sel != "undefined") {
24768             try {
24769                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24770             } catch(e) {
24771                 return this.doc.createRange();
24772             }
24773         } else {
24774             return this.doc.createRange();
24775         }
24776     },
24777     getParentElement: function()
24778     {
24779         
24780         this.assignDocWin();
24781         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24782         
24783         var range = this.createRange(sel);
24784          
24785         try {
24786             var p = range.commonAncestorContainer;
24787             while (p.nodeType == 3) { // text node
24788                 p = p.parentNode;
24789             }
24790             return p;
24791         } catch (e) {
24792             return null;
24793         }
24794     
24795     },
24796     /***
24797      *
24798      * Range intersection.. the hard stuff...
24799      *  '-1' = before
24800      *  '0' = hits..
24801      *  '1' = after.
24802      *         [ -- selected range --- ]
24803      *   [fail]                        [fail]
24804      *
24805      *    basically..
24806      *      if end is before start or  hits it. fail.
24807      *      if start is after end or hits it fail.
24808      *
24809      *   if either hits (but other is outside. - then it's not 
24810      *   
24811      *    
24812      **/
24813     
24814     
24815     // @see http://www.thismuchiknow.co.uk/?p=64.
24816     rangeIntersectsNode : function(range, node)
24817     {
24818         var nodeRange = node.ownerDocument.createRange();
24819         try {
24820             nodeRange.selectNode(node);
24821         } catch (e) {
24822             nodeRange.selectNodeContents(node);
24823         }
24824     
24825         var rangeStartRange = range.cloneRange();
24826         rangeStartRange.collapse(true);
24827     
24828         var rangeEndRange = range.cloneRange();
24829         rangeEndRange.collapse(false);
24830     
24831         var nodeStartRange = nodeRange.cloneRange();
24832         nodeStartRange.collapse(true);
24833     
24834         var nodeEndRange = nodeRange.cloneRange();
24835         nodeEndRange.collapse(false);
24836     
24837         return rangeStartRange.compareBoundaryPoints(
24838                  Range.START_TO_START, nodeEndRange) == -1 &&
24839                rangeEndRange.compareBoundaryPoints(
24840                  Range.START_TO_START, nodeStartRange) == 1;
24841         
24842          
24843     },
24844     rangeCompareNode : function(range, node)
24845     {
24846         var nodeRange = node.ownerDocument.createRange();
24847         try {
24848             nodeRange.selectNode(node);
24849         } catch (e) {
24850             nodeRange.selectNodeContents(node);
24851         }
24852         
24853         
24854         range.collapse(true);
24855     
24856         nodeRange.collapse(true);
24857      
24858         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24859         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24860          
24861         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24862         
24863         var nodeIsBefore   =  ss == 1;
24864         var nodeIsAfter    = ee == -1;
24865         
24866         if (nodeIsBefore && nodeIsAfter) {
24867             return 0; // outer
24868         }
24869         if (!nodeIsBefore && nodeIsAfter) {
24870             return 1; //right trailed.
24871         }
24872         
24873         if (nodeIsBefore && !nodeIsAfter) {
24874             return 2;  // left trailed.
24875         }
24876         // fully contined.
24877         return 3;
24878     },
24879
24880     // private? - in a new class?
24881     cleanUpPaste :  function()
24882     {
24883         // cleans up the whole document..
24884         Roo.log('cleanuppaste');
24885         
24886         this.cleanUpChildren(this.doc.body);
24887         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24888         if (clean != this.doc.body.innerHTML) {
24889             this.doc.body.innerHTML = clean;
24890         }
24891         
24892     },
24893     
24894     cleanWordChars : function(input) {// change the chars to hex code
24895         var he = Roo.HtmlEditorCore;
24896         
24897         var output = input;
24898         Roo.each(he.swapCodes, function(sw) { 
24899             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24900             
24901             output = output.replace(swapper, sw[1]);
24902         });
24903         
24904         return output;
24905     },
24906     
24907     
24908     cleanUpChildren : function (n)
24909     {
24910         if (!n.childNodes.length) {
24911             return;
24912         }
24913         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24914            this.cleanUpChild(n.childNodes[i]);
24915         }
24916     },
24917     
24918     
24919         
24920     
24921     cleanUpChild : function (node)
24922     {
24923         var ed = this;
24924         //console.log(node);
24925         if (node.nodeName == "#text") {
24926             // clean up silly Windows -- stuff?
24927             return; 
24928         }
24929         if (node.nodeName == "#comment") {
24930             node.parentNode.removeChild(node);
24931             // clean up silly Windows -- stuff?
24932             return; 
24933         }
24934         var lcname = node.tagName.toLowerCase();
24935         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24936         // whitelist of tags..
24937         
24938         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24939             // remove node.
24940             node.parentNode.removeChild(node);
24941             return;
24942             
24943         }
24944         
24945         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24946         
24947         // spans with no attributes - just remove them..
24948         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24949             remove_keep_children = true;
24950         }
24951         
24952         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24953         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24954         
24955         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24956         //    remove_keep_children = true;
24957         //}
24958         
24959         if (remove_keep_children) {
24960             this.cleanUpChildren(node);
24961             // inserts everything just before this node...
24962             while (node.childNodes.length) {
24963                 var cn = node.childNodes[0];
24964                 node.removeChild(cn);
24965                 node.parentNode.insertBefore(cn, node);
24966             }
24967             node.parentNode.removeChild(node);
24968             return;
24969         }
24970         
24971         if (!node.attributes || !node.attributes.length) {
24972             
24973           
24974             
24975             
24976             this.cleanUpChildren(node);
24977             return;
24978         }
24979         
24980         function cleanAttr(n,v)
24981         {
24982             
24983             if (v.match(/^\./) || v.match(/^\//)) {
24984                 return;
24985             }
24986             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24987                 return;
24988             }
24989             if (v.match(/^#/)) {
24990                 return;
24991             }
24992             if (v.match(/^\{/)) { // allow template editing.
24993                 return;
24994             }
24995 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24996             node.removeAttribute(n);
24997             
24998         }
24999         
25000         var cwhite = this.cwhite;
25001         var cblack = this.cblack;
25002             
25003         function cleanStyle(n,v)
25004         {
25005             if (v.match(/expression/)) { //XSS?? should we even bother..
25006                 node.removeAttribute(n);
25007                 return;
25008             }
25009             
25010             var parts = v.split(/;/);
25011             var clean = [];
25012             
25013             Roo.each(parts, function(p) {
25014                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25015                 if (!p.length) {
25016                     return true;
25017                 }
25018                 var l = p.split(':').shift().replace(/\s+/g,'');
25019                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25020                 
25021                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25022 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25023                     //node.removeAttribute(n);
25024                     return true;
25025                 }
25026                 //Roo.log()
25027                 // only allow 'c whitelisted system attributes'
25028                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25029 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25030                     //node.removeAttribute(n);
25031                     return true;
25032                 }
25033                 
25034                 
25035                  
25036                 
25037                 clean.push(p);
25038                 return true;
25039             });
25040             if (clean.length) { 
25041                 node.setAttribute(n, clean.join(';'));
25042             } else {
25043                 node.removeAttribute(n);
25044             }
25045             
25046         }
25047         
25048         
25049         for (var i = node.attributes.length-1; i > -1 ; i--) {
25050             var a = node.attributes[i];
25051             //console.log(a);
25052             
25053             if (a.name.toLowerCase().substr(0,2)=='on')  {
25054                 node.removeAttribute(a.name);
25055                 continue;
25056             }
25057             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25058                 node.removeAttribute(a.name);
25059                 continue;
25060             }
25061             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25062                 cleanAttr(a.name,a.value); // fixme..
25063                 continue;
25064             }
25065             if (a.name == 'style') {
25066                 cleanStyle(a.name,a.value);
25067                 continue;
25068             }
25069             /// clean up MS crap..
25070             // tecnically this should be a list of valid class'es..
25071             
25072             
25073             if (a.name == 'class') {
25074                 if (a.value.match(/^Mso/)) {
25075                     node.removeAttribute('class');
25076                 }
25077                 
25078                 if (a.value.match(/^body$/)) {
25079                     node.removeAttribute('class');
25080                 }
25081                 continue;
25082             }
25083             
25084             // style cleanup!?
25085             // class cleanup?
25086             
25087         }
25088         
25089         
25090         this.cleanUpChildren(node);
25091         
25092         
25093     },
25094     
25095     /**
25096      * Clean up MS wordisms...
25097      */
25098     cleanWord : function(node)
25099     {
25100         if (!node) {
25101             this.cleanWord(this.doc.body);
25102             return;
25103         }
25104         
25105         if(
25106                 node.nodeName == 'SPAN' &&
25107                 !node.hasAttributes() &&
25108                 node.childNodes.length == 1 &&
25109                 node.firstChild.nodeName == "#text"  
25110         ) {
25111             var textNode = node.firstChild;
25112             node.removeChild(textNode);
25113             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25114                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25115             }
25116             node.parentNode.insertBefore(textNode, node);
25117             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25118                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25119             }
25120             node.parentNode.removeChild(node);
25121         }
25122         
25123         if (node.nodeName == "#text") {
25124             // clean up silly Windows -- stuff?
25125             return; 
25126         }
25127         if (node.nodeName == "#comment") {
25128             node.parentNode.removeChild(node);
25129             // clean up silly Windows -- stuff?
25130             return; 
25131         }
25132         
25133         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25134             node.parentNode.removeChild(node);
25135             return;
25136         }
25137         //Roo.log(node.tagName);
25138         // remove - but keep children..
25139         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25140             //Roo.log('-- removed');
25141             while (node.childNodes.length) {
25142                 var cn = node.childNodes[0];
25143                 node.removeChild(cn);
25144                 node.parentNode.insertBefore(cn, node);
25145                 // move node to parent - and clean it..
25146                 this.cleanWord(cn);
25147             }
25148             node.parentNode.removeChild(node);
25149             /// no need to iterate chidlren = it's got none..
25150             //this.iterateChildren(node, this.cleanWord);
25151             return;
25152         }
25153         // clean styles
25154         if (node.className.length) {
25155             
25156             var cn = node.className.split(/\W+/);
25157             var cna = [];
25158             Roo.each(cn, function(cls) {
25159                 if (cls.match(/Mso[a-zA-Z]+/)) {
25160                     return;
25161                 }
25162                 cna.push(cls);
25163             });
25164             node.className = cna.length ? cna.join(' ') : '';
25165             if (!cna.length) {
25166                 node.removeAttribute("class");
25167             }
25168         }
25169         
25170         if (node.hasAttribute("lang")) {
25171             node.removeAttribute("lang");
25172         }
25173         
25174         if (node.hasAttribute("style")) {
25175             
25176             var styles = node.getAttribute("style").split(";");
25177             var nstyle = [];
25178             Roo.each(styles, function(s) {
25179                 if (!s.match(/:/)) {
25180                     return;
25181                 }
25182                 var kv = s.split(":");
25183                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25184                     return;
25185                 }
25186                 // what ever is left... we allow.
25187                 nstyle.push(s);
25188             });
25189             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25190             if (!nstyle.length) {
25191                 node.removeAttribute('style');
25192             }
25193         }
25194         this.iterateChildren(node, this.cleanWord);
25195         
25196         
25197         
25198     },
25199     /**
25200      * iterateChildren of a Node, calling fn each time, using this as the scole..
25201      * @param {DomNode} node node to iterate children of.
25202      * @param {Function} fn method of this class to call on each item.
25203      */
25204     iterateChildren : function(node, fn)
25205     {
25206         if (!node.childNodes.length) {
25207                 return;
25208         }
25209         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25210            fn.call(this, node.childNodes[i])
25211         }
25212     },
25213     
25214     
25215     /**
25216      * cleanTableWidths.
25217      *
25218      * Quite often pasting from word etc.. results in tables with column and widths.
25219      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25220      *
25221      */
25222     cleanTableWidths : function(node)
25223     {
25224          
25225          
25226         if (!node) {
25227             this.cleanTableWidths(this.doc.body);
25228             return;
25229         }
25230         
25231         // ignore list...
25232         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25233             return; 
25234         }
25235         Roo.log(node.tagName);
25236         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25237             this.iterateChildren(node, this.cleanTableWidths);
25238             return;
25239         }
25240         if (node.hasAttribute('width')) {
25241             node.removeAttribute('width');
25242         }
25243         
25244          
25245         if (node.hasAttribute("style")) {
25246             // pretty basic...
25247             
25248             var styles = node.getAttribute("style").split(";");
25249             var nstyle = [];
25250             Roo.each(styles, function(s) {
25251                 if (!s.match(/:/)) {
25252                     return;
25253                 }
25254                 var kv = s.split(":");
25255                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25256                     return;
25257                 }
25258                 // what ever is left... we allow.
25259                 nstyle.push(s);
25260             });
25261             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25262             if (!nstyle.length) {
25263                 node.removeAttribute('style');
25264             }
25265         }
25266         
25267         this.iterateChildren(node, this.cleanTableWidths);
25268         
25269         
25270     },
25271     
25272     
25273     
25274     
25275     domToHTML : function(currentElement, depth, nopadtext) {
25276         
25277         depth = depth || 0;
25278         nopadtext = nopadtext || false;
25279     
25280         if (!currentElement) {
25281             return this.domToHTML(this.doc.body);
25282         }
25283         
25284         //Roo.log(currentElement);
25285         var j;
25286         var allText = false;
25287         var nodeName = currentElement.nodeName;
25288         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25289         
25290         if  (nodeName == '#text') {
25291             
25292             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25293         }
25294         
25295         
25296         var ret = '';
25297         if (nodeName != 'BODY') {
25298              
25299             var i = 0;
25300             // Prints the node tagName, such as <A>, <IMG>, etc
25301             if (tagName) {
25302                 var attr = [];
25303                 for(i = 0; i < currentElement.attributes.length;i++) {
25304                     // quoting?
25305                     var aname = currentElement.attributes.item(i).name;
25306                     if (!currentElement.attributes.item(i).value.length) {
25307                         continue;
25308                     }
25309                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25310                 }
25311                 
25312                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25313             } 
25314             else {
25315                 
25316                 // eack
25317             }
25318         } else {
25319             tagName = false;
25320         }
25321         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25322             return ret;
25323         }
25324         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25325             nopadtext = true;
25326         }
25327         
25328         
25329         // Traverse the tree
25330         i = 0;
25331         var currentElementChild = currentElement.childNodes.item(i);
25332         var allText = true;
25333         var innerHTML  = '';
25334         lastnode = '';
25335         while (currentElementChild) {
25336             // Formatting code (indent the tree so it looks nice on the screen)
25337             var nopad = nopadtext;
25338             if (lastnode == 'SPAN') {
25339                 nopad  = true;
25340             }
25341             // text
25342             if  (currentElementChild.nodeName == '#text') {
25343                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25344                 toadd = nopadtext ? toadd : toadd.trim();
25345                 if (!nopad && toadd.length > 80) {
25346                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25347                 }
25348                 innerHTML  += toadd;
25349                 
25350                 i++;
25351                 currentElementChild = currentElement.childNodes.item(i);
25352                 lastNode = '';
25353                 continue;
25354             }
25355             allText = false;
25356             
25357             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25358                 
25359             // Recursively traverse the tree structure of the child node
25360             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25361             lastnode = currentElementChild.nodeName;
25362             i++;
25363             currentElementChild=currentElement.childNodes.item(i);
25364         }
25365         
25366         ret += innerHTML;
25367         
25368         if (!allText) {
25369                 // The remaining code is mostly for formatting the tree
25370             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25371         }
25372         
25373         
25374         if (tagName) {
25375             ret+= "</"+tagName+">";
25376         }
25377         return ret;
25378         
25379     },
25380         
25381     applyBlacklists : function()
25382     {
25383         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25384         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25385         
25386         this.white = [];
25387         this.black = [];
25388         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25389             if (b.indexOf(tag) > -1) {
25390                 return;
25391             }
25392             this.white.push(tag);
25393             
25394         }, this);
25395         
25396         Roo.each(w, function(tag) {
25397             if (b.indexOf(tag) > -1) {
25398                 return;
25399             }
25400             if (this.white.indexOf(tag) > -1) {
25401                 return;
25402             }
25403             this.white.push(tag);
25404             
25405         }, this);
25406         
25407         
25408         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25409             if (w.indexOf(tag) > -1) {
25410                 return;
25411             }
25412             this.black.push(tag);
25413             
25414         }, this);
25415         
25416         Roo.each(b, function(tag) {
25417             if (w.indexOf(tag) > -1) {
25418                 return;
25419             }
25420             if (this.black.indexOf(tag) > -1) {
25421                 return;
25422             }
25423             this.black.push(tag);
25424             
25425         }, this);
25426         
25427         
25428         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25429         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25430         
25431         this.cwhite = [];
25432         this.cblack = [];
25433         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25434             if (b.indexOf(tag) > -1) {
25435                 return;
25436             }
25437             this.cwhite.push(tag);
25438             
25439         }, this);
25440         
25441         Roo.each(w, function(tag) {
25442             if (b.indexOf(tag) > -1) {
25443                 return;
25444             }
25445             if (this.cwhite.indexOf(tag) > -1) {
25446                 return;
25447             }
25448             this.cwhite.push(tag);
25449             
25450         }, this);
25451         
25452         
25453         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25454             if (w.indexOf(tag) > -1) {
25455                 return;
25456             }
25457             this.cblack.push(tag);
25458             
25459         }, this);
25460         
25461         Roo.each(b, function(tag) {
25462             if (w.indexOf(tag) > -1) {
25463                 return;
25464             }
25465             if (this.cblack.indexOf(tag) > -1) {
25466                 return;
25467             }
25468             this.cblack.push(tag);
25469             
25470         }, this);
25471     },
25472     
25473     setStylesheets : function(stylesheets)
25474     {
25475         if(typeof(stylesheets) == 'string'){
25476             Roo.get(this.iframe.contentDocument.head).createChild({
25477                 tag : 'link',
25478                 rel : 'stylesheet',
25479                 type : 'text/css',
25480                 href : stylesheets
25481             });
25482             
25483             return;
25484         }
25485         var _this = this;
25486      
25487         Roo.each(stylesheets, function(s) {
25488             if(!s.length){
25489                 return;
25490             }
25491             
25492             Roo.get(_this.iframe.contentDocument.head).createChild({
25493                 tag : 'link',
25494                 rel : 'stylesheet',
25495                 type : 'text/css',
25496                 href : s
25497             });
25498         });
25499
25500         
25501     },
25502     
25503     removeStylesheets : function()
25504     {
25505         var _this = this;
25506         
25507         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25508             s.remove();
25509         });
25510     },
25511     
25512     setStyle : function(style)
25513     {
25514         Roo.get(this.iframe.contentDocument.head).createChild({
25515             tag : 'style',
25516             type : 'text/css',
25517             html : style
25518         });
25519
25520         return;
25521     }
25522     
25523     // hide stuff that is not compatible
25524     /**
25525      * @event blur
25526      * @hide
25527      */
25528     /**
25529      * @event change
25530      * @hide
25531      */
25532     /**
25533      * @event focus
25534      * @hide
25535      */
25536     /**
25537      * @event specialkey
25538      * @hide
25539      */
25540     /**
25541      * @cfg {String} fieldClass @hide
25542      */
25543     /**
25544      * @cfg {String} focusClass @hide
25545      */
25546     /**
25547      * @cfg {String} autoCreate @hide
25548      */
25549     /**
25550      * @cfg {String} inputType @hide
25551      */
25552     /**
25553      * @cfg {String} invalidClass @hide
25554      */
25555     /**
25556      * @cfg {String} invalidText @hide
25557      */
25558     /**
25559      * @cfg {String} msgFx @hide
25560      */
25561     /**
25562      * @cfg {String} validateOnBlur @hide
25563      */
25564 });
25565
25566 Roo.HtmlEditorCore.white = [
25567         'area', 'br', 'img', 'input', 'hr', 'wbr',
25568         
25569        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25570        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25571        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25572        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25573        'table',   'ul',         'xmp', 
25574        
25575        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25576       'thead',   'tr', 
25577      
25578       'dir', 'menu', 'ol', 'ul', 'dl',
25579        
25580       'embed',  'object'
25581 ];
25582
25583
25584 Roo.HtmlEditorCore.black = [
25585     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25586         'applet', // 
25587         'base',   'basefont', 'bgsound', 'blink',  'body', 
25588         'frame',  'frameset', 'head',    'html',   'ilayer', 
25589         'iframe', 'layer',  'link',     'meta',    'object',   
25590         'script', 'style' ,'title',  'xml' // clean later..
25591 ];
25592 Roo.HtmlEditorCore.clean = [
25593     'script', 'style', 'title', 'xml'
25594 ];
25595 Roo.HtmlEditorCore.remove = [
25596     'font'
25597 ];
25598 // attributes..
25599
25600 Roo.HtmlEditorCore.ablack = [
25601     'on'
25602 ];
25603     
25604 Roo.HtmlEditorCore.aclean = [ 
25605     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25606 ];
25607
25608 // protocols..
25609 Roo.HtmlEditorCore.pwhite= [
25610         'http',  'https',  'mailto'
25611 ];
25612
25613 // white listed style attributes.
25614 Roo.HtmlEditorCore.cwhite= [
25615       //  'text-align', /// default is to allow most things..
25616       
25617          
25618 //        'font-size'//??
25619 ];
25620
25621 // black listed style attributes.
25622 Roo.HtmlEditorCore.cblack= [
25623       //  'font-size' -- this can be set by the project 
25624 ];
25625
25626
25627 Roo.HtmlEditorCore.swapCodes   =[ 
25628     [    8211, "--" ], 
25629     [    8212, "--" ], 
25630     [    8216,  "'" ],  
25631     [    8217, "'" ],  
25632     [    8220, '"' ],  
25633     [    8221, '"' ],  
25634     [    8226, "*" ],  
25635     [    8230, "..." ]
25636 ]; 
25637
25638     /*
25639  * - LGPL
25640  *
25641  * HtmlEditor
25642  * 
25643  */
25644
25645 /**
25646  * @class Roo.bootstrap.HtmlEditor
25647  * @extends Roo.bootstrap.TextArea
25648  * Bootstrap HtmlEditor class
25649
25650  * @constructor
25651  * Create a new HtmlEditor
25652  * @param {Object} config The config object
25653  */
25654
25655 Roo.bootstrap.HtmlEditor = function(config){
25656     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25657     if (!this.toolbars) {
25658         this.toolbars = [];
25659     }
25660     
25661     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25662     this.addEvents({
25663             /**
25664              * @event initialize
25665              * Fires when the editor is fully initialized (including the iframe)
25666              * @param {HtmlEditor} this
25667              */
25668             initialize: true,
25669             /**
25670              * @event activate
25671              * Fires when the editor is first receives the focus. Any insertion must wait
25672              * until after this event.
25673              * @param {HtmlEditor} this
25674              */
25675             activate: true,
25676              /**
25677              * @event beforesync
25678              * Fires before the textarea is updated with content from the editor iframe. Return false
25679              * to cancel the sync.
25680              * @param {HtmlEditor} this
25681              * @param {String} html
25682              */
25683             beforesync: true,
25684              /**
25685              * @event beforepush
25686              * Fires before the iframe editor is updated with content from the textarea. Return false
25687              * to cancel the push.
25688              * @param {HtmlEditor} this
25689              * @param {String} html
25690              */
25691             beforepush: true,
25692              /**
25693              * @event sync
25694              * Fires when the textarea is updated with content from the editor iframe.
25695              * @param {HtmlEditor} this
25696              * @param {String} html
25697              */
25698             sync: true,
25699              /**
25700              * @event push
25701              * Fires when the iframe editor is updated with content from the textarea.
25702              * @param {HtmlEditor} this
25703              * @param {String} html
25704              */
25705             push: true,
25706              /**
25707              * @event editmodechange
25708              * Fires when the editor switches edit modes
25709              * @param {HtmlEditor} this
25710              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25711              */
25712             editmodechange: true,
25713             /**
25714              * @event editorevent
25715              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25716              * @param {HtmlEditor} this
25717              */
25718             editorevent: true,
25719             /**
25720              * @event firstfocus
25721              * Fires when on first focus - needed by toolbars..
25722              * @param {HtmlEditor} this
25723              */
25724             firstfocus: true,
25725             /**
25726              * @event autosave
25727              * Auto save the htmlEditor value as a file into Events
25728              * @param {HtmlEditor} this
25729              */
25730             autosave: true,
25731             /**
25732              * @event savedpreview
25733              * preview the saved version of htmlEditor
25734              * @param {HtmlEditor} this
25735              */
25736             savedpreview: true
25737         });
25738 };
25739
25740
25741 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25742     
25743     
25744       /**
25745      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25746      */
25747     toolbars : false,
25748     
25749      /**
25750     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25751     */
25752     btns : [],
25753    
25754      /**
25755      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25756      *                        Roo.resizable.
25757      */
25758     resizable : false,
25759      /**
25760      * @cfg {Number} height (in pixels)
25761      */   
25762     height: 300,
25763    /**
25764      * @cfg {Number} width (in pixels)
25765      */   
25766     width: false,
25767     
25768     /**
25769      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25770      * 
25771      */
25772     stylesheets: false,
25773     
25774     // id of frame..
25775     frameId: false,
25776     
25777     // private properties
25778     validationEvent : false,
25779     deferHeight: true,
25780     initialized : false,
25781     activated : false,
25782     
25783     onFocus : Roo.emptyFn,
25784     iframePad:3,
25785     hideMode:'offsets',
25786     
25787     tbContainer : false,
25788     
25789     bodyCls : '',
25790     
25791     toolbarContainer :function() {
25792         return this.wrap.select('.x-html-editor-tb',true).first();
25793     },
25794
25795     /**
25796      * Protected method that will not generally be called directly. It
25797      * is called when the editor creates its toolbar. Override this method if you need to
25798      * add custom toolbar buttons.
25799      * @param {HtmlEditor} editor
25800      */
25801     createToolbar : function(){
25802         Roo.log('renewing');
25803         Roo.log("create toolbars");
25804         
25805         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25806         this.toolbars[0].render(this.toolbarContainer());
25807         
25808         return;
25809         
25810 //        if (!editor.toolbars || !editor.toolbars.length) {
25811 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25812 //        }
25813 //        
25814 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25815 //            editor.toolbars[i] = Roo.factory(
25816 //                    typeof(editor.toolbars[i]) == 'string' ?
25817 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25818 //                Roo.bootstrap.HtmlEditor);
25819 //            editor.toolbars[i].init(editor);
25820 //        }
25821     },
25822
25823      
25824     // private
25825     onRender : function(ct, position)
25826     {
25827        // Roo.log("Call onRender: " + this.xtype);
25828         var _t = this;
25829         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25830       
25831         this.wrap = this.inputEl().wrap({
25832             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25833         });
25834         
25835         this.editorcore.onRender(ct, position);
25836          
25837         if (this.resizable) {
25838             this.resizeEl = new Roo.Resizable(this.wrap, {
25839                 pinned : true,
25840                 wrap: true,
25841                 dynamic : true,
25842                 minHeight : this.height,
25843                 height: this.height,
25844                 handles : this.resizable,
25845                 width: this.width,
25846                 listeners : {
25847                     resize : function(r, w, h) {
25848                         _t.onResize(w,h); // -something
25849                     }
25850                 }
25851             });
25852             
25853         }
25854         this.createToolbar(this);
25855        
25856         
25857         if(!this.width && this.resizable){
25858             this.setSize(this.wrap.getSize());
25859         }
25860         if (this.resizeEl) {
25861             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25862             // should trigger onReize..
25863         }
25864         
25865     },
25866
25867     // private
25868     onResize : function(w, h)
25869     {
25870         Roo.log('resize: ' +w + ',' + h );
25871         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25872         var ew = false;
25873         var eh = false;
25874         
25875         if(this.inputEl() ){
25876             if(typeof w == 'number'){
25877                 var aw = w - this.wrap.getFrameWidth('lr');
25878                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25879                 ew = aw;
25880             }
25881             if(typeof h == 'number'){
25882                  var tbh = -11;  // fixme it needs to tool bar size!
25883                 for (var i =0; i < this.toolbars.length;i++) {
25884                     // fixme - ask toolbars for heights?
25885                     tbh += this.toolbars[i].el.getHeight();
25886                     //if (this.toolbars[i].footer) {
25887                     //    tbh += this.toolbars[i].footer.el.getHeight();
25888                     //}
25889                 }
25890               
25891                 
25892                 
25893                 
25894                 
25895                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25896                 ah -= 5; // knock a few pixes off for look..
25897                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25898                 var eh = ah;
25899             }
25900         }
25901         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25902         this.editorcore.onResize(ew,eh);
25903         
25904     },
25905
25906     /**
25907      * Toggles the editor between standard and source edit mode.
25908      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25909      */
25910     toggleSourceEdit : function(sourceEditMode)
25911     {
25912         this.editorcore.toggleSourceEdit(sourceEditMode);
25913         
25914         if(this.editorcore.sourceEditMode){
25915             Roo.log('editor - showing textarea');
25916             
25917 //            Roo.log('in');
25918 //            Roo.log(this.syncValue());
25919             this.syncValue();
25920             this.inputEl().removeClass(['hide', 'x-hidden']);
25921             this.inputEl().dom.removeAttribute('tabIndex');
25922             this.inputEl().focus();
25923         }else{
25924             Roo.log('editor - hiding textarea');
25925 //            Roo.log('out')
25926 //            Roo.log(this.pushValue()); 
25927             this.pushValue();
25928             
25929             this.inputEl().addClass(['hide', 'x-hidden']);
25930             this.inputEl().dom.setAttribute('tabIndex', -1);
25931             //this.deferFocus();
25932         }
25933          
25934         if(this.resizable){
25935             this.setSize(this.wrap.getSize());
25936         }
25937         
25938         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25939     },
25940  
25941     // private (for BoxComponent)
25942     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25943
25944     // private (for BoxComponent)
25945     getResizeEl : function(){
25946         return this.wrap;
25947     },
25948
25949     // private (for BoxComponent)
25950     getPositionEl : function(){
25951         return this.wrap;
25952     },
25953
25954     // private
25955     initEvents : function(){
25956         this.originalValue = this.getValue();
25957     },
25958
25959 //    /**
25960 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25961 //     * @method
25962 //     */
25963 //    markInvalid : Roo.emptyFn,
25964 //    /**
25965 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25966 //     * @method
25967 //     */
25968 //    clearInvalid : Roo.emptyFn,
25969
25970     setValue : function(v){
25971         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25972         this.editorcore.pushValue();
25973     },
25974
25975      
25976     // private
25977     deferFocus : function(){
25978         this.focus.defer(10, this);
25979     },
25980
25981     // doc'ed in Field
25982     focus : function(){
25983         this.editorcore.focus();
25984         
25985     },
25986       
25987
25988     // private
25989     onDestroy : function(){
25990         
25991         
25992         
25993         if(this.rendered){
25994             
25995             for (var i =0; i < this.toolbars.length;i++) {
25996                 // fixme - ask toolbars for heights?
25997                 this.toolbars[i].onDestroy();
25998             }
25999             
26000             this.wrap.dom.innerHTML = '';
26001             this.wrap.remove();
26002         }
26003     },
26004
26005     // private
26006     onFirstFocus : function(){
26007         //Roo.log("onFirstFocus");
26008         this.editorcore.onFirstFocus();
26009          for (var i =0; i < this.toolbars.length;i++) {
26010             this.toolbars[i].onFirstFocus();
26011         }
26012         
26013     },
26014     
26015     // private
26016     syncValue : function()
26017     {   
26018         this.editorcore.syncValue();
26019     },
26020     
26021     pushValue : function()
26022     {   
26023         this.editorcore.pushValue();
26024     }
26025      
26026     
26027     // hide stuff that is not compatible
26028     /**
26029      * @event blur
26030      * @hide
26031      */
26032     /**
26033      * @event change
26034      * @hide
26035      */
26036     /**
26037      * @event focus
26038      * @hide
26039      */
26040     /**
26041      * @event specialkey
26042      * @hide
26043      */
26044     /**
26045      * @cfg {String} fieldClass @hide
26046      */
26047     /**
26048      * @cfg {String} focusClass @hide
26049      */
26050     /**
26051      * @cfg {String} autoCreate @hide
26052      */
26053     /**
26054      * @cfg {String} inputType @hide
26055      */
26056      
26057     /**
26058      * @cfg {String} invalidText @hide
26059      */
26060     /**
26061      * @cfg {String} msgFx @hide
26062      */
26063     /**
26064      * @cfg {String} validateOnBlur @hide
26065      */
26066 });
26067  
26068     
26069    
26070    
26071    
26072       
26073 Roo.namespace('Roo.bootstrap.htmleditor');
26074 /**
26075  * @class Roo.bootstrap.HtmlEditorToolbar1
26076  * Basic Toolbar
26077  * 
26078  * @example
26079  * Usage:
26080  *
26081  new Roo.bootstrap.HtmlEditor({
26082     ....
26083     toolbars : [
26084         new Roo.bootstrap.HtmlEditorToolbar1({
26085             disable : { fonts: 1 , format: 1, ..., ... , ...],
26086             btns : [ .... ]
26087         })
26088     }
26089      
26090  * 
26091  * @cfg {Object} disable List of elements to disable..
26092  * @cfg {Array} btns List of additional buttons.
26093  * 
26094  * 
26095  * NEEDS Extra CSS? 
26096  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26097  */
26098  
26099 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26100 {
26101     
26102     Roo.apply(this, config);
26103     
26104     // default disabled, based on 'good practice'..
26105     this.disable = this.disable || {};
26106     Roo.applyIf(this.disable, {
26107         fontSize : true,
26108         colors : true,
26109         specialElements : true
26110     });
26111     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26112     
26113     this.editor = config.editor;
26114     this.editorcore = config.editor.editorcore;
26115     
26116     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26117     
26118     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26119     // dont call parent... till later.
26120 }
26121 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26122      
26123     bar : true,
26124     
26125     editor : false,
26126     editorcore : false,
26127     
26128     
26129     formats : [
26130         "p" ,  
26131         "h1","h2","h3","h4","h5","h6", 
26132         "pre", "code", 
26133         "abbr", "acronym", "address", "cite", "samp", "var",
26134         'div','span'
26135     ],
26136     
26137     onRender : function(ct, position)
26138     {
26139        // Roo.log("Call onRender: " + this.xtype);
26140         
26141        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26142        Roo.log(this.el);
26143        this.el.dom.style.marginBottom = '0';
26144        var _this = this;
26145        var editorcore = this.editorcore;
26146        var editor= this.editor;
26147        
26148        var children = [];
26149        var btn = function(id,cmd , toggle, handler, html){
26150        
26151             var  event = toggle ? 'toggle' : 'click';
26152        
26153             var a = {
26154                 size : 'sm',
26155                 xtype: 'Button',
26156                 xns: Roo.bootstrap,
26157                 //glyphicon : id,
26158                 fa: id,
26159                 cmd : id || cmd,
26160                 enableToggle:toggle !== false,
26161                 html : html || '',
26162                 pressed : toggle ? false : null,
26163                 listeners : {}
26164             };
26165             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26166                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26167             };
26168             children.push(a);
26169             return a;
26170        }
26171        
26172     //    var cb_box = function...
26173         
26174         var style = {
26175                 xtype: 'Button',
26176                 size : 'sm',
26177                 xns: Roo.bootstrap,
26178                 fa : 'font',
26179                 //html : 'submit'
26180                 menu : {
26181                     xtype: 'Menu',
26182                     xns: Roo.bootstrap,
26183                     items:  []
26184                 }
26185         };
26186         Roo.each(this.formats, function(f) {
26187             style.menu.items.push({
26188                 xtype :'MenuItem',
26189                 xns: Roo.bootstrap,
26190                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26191                 tagname : f,
26192                 listeners : {
26193                     click : function()
26194                     {
26195                         editorcore.insertTag(this.tagname);
26196                         editor.focus();
26197                     }
26198                 }
26199                 
26200             });
26201         });
26202         children.push(style);   
26203         
26204         btn('bold',false,true);
26205         btn('italic',false,true);
26206         btn('align-left', 'justifyleft',true);
26207         btn('align-center', 'justifycenter',true);
26208         btn('align-right' , 'justifyright',true);
26209         btn('link', false, false, function(btn) {
26210             //Roo.log("create link?");
26211             var url = prompt(this.createLinkText, this.defaultLinkValue);
26212             if(url && url != 'http:/'+'/'){
26213                 this.editorcore.relayCmd('createlink', url);
26214             }
26215         }),
26216         btn('list','insertunorderedlist',true);
26217         btn('pencil', false,true, function(btn){
26218                 Roo.log(this);
26219                 this.toggleSourceEdit(btn.pressed);
26220         });
26221         
26222         if (this.editor.btns.length > 0) {
26223             for (var i = 0; i<this.editor.btns.length; i++) {
26224                 children.push(this.editor.btns[i]);
26225             }
26226         }
26227         
26228         /*
26229         var cog = {
26230                 xtype: 'Button',
26231                 size : 'sm',
26232                 xns: Roo.bootstrap,
26233                 glyphicon : 'cog',
26234                 //html : 'submit'
26235                 menu : {
26236                     xtype: 'Menu',
26237                     xns: Roo.bootstrap,
26238                     items:  []
26239                 }
26240         };
26241         
26242         cog.menu.items.push({
26243             xtype :'MenuItem',
26244             xns: Roo.bootstrap,
26245             html : Clean styles,
26246             tagname : f,
26247             listeners : {
26248                 click : function()
26249                 {
26250                     editorcore.insertTag(this.tagname);
26251                     editor.focus();
26252                 }
26253             }
26254             
26255         });
26256        */
26257         
26258          
26259        this.xtype = 'NavSimplebar';
26260         
26261         for(var i=0;i< children.length;i++) {
26262             
26263             this.buttons.add(this.addxtypeChild(children[i]));
26264             
26265         }
26266         
26267         editor.on('editorevent', this.updateToolbar, this);
26268     },
26269     onBtnClick : function(id)
26270     {
26271        this.editorcore.relayCmd(id);
26272        this.editorcore.focus();
26273     },
26274     
26275     /**
26276      * Protected method that will not generally be called directly. It triggers
26277      * a toolbar update by reading the markup state of the current selection in the editor.
26278      */
26279     updateToolbar: function(){
26280
26281         if(!this.editorcore.activated){
26282             this.editor.onFirstFocus(); // is this neeed?
26283             return;
26284         }
26285
26286         var btns = this.buttons; 
26287         var doc = this.editorcore.doc;
26288         btns.get('bold').setActive(doc.queryCommandState('bold'));
26289         btns.get('italic').setActive(doc.queryCommandState('italic'));
26290         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26291         
26292         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26293         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26294         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26295         
26296         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26297         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26298          /*
26299         
26300         var ans = this.editorcore.getAllAncestors();
26301         if (this.formatCombo) {
26302             
26303             
26304             var store = this.formatCombo.store;
26305             this.formatCombo.setValue("");
26306             for (var i =0; i < ans.length;i++) {
26307                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26308                     // select it..
26309                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26310                     break;
26311                 }
26312             }
26313         }
26314         
26315         
26316         
26317         // hides menus... - so this cant be on a menu...
26318         Roo.bootstrap.MenuMgr.hideAll();
26319         */
26320         Roo.bootstrap.MenuMgr.hideAll();
26321         //this.editorsyncValue();
26322     },
26323     onFirstFocus: function() {
26324         this.buttons.each(function(item){
26325            item.enable();
26326         });
26327     },
26328     toggleSourceEdit : function(sourceEditMode){
26329         
26330           
26331         if(sourceEditMode){
26332             Roo.log("disabling buttons");
26333            this.buttons.each( function(item){
26334                 if(item.cmd != 'pencil'){
26335                     item.disable();
26336                 }
26337             });
26338           
26339         }else{
26340             Roo.log("enabling buttons");
26341             if(this.editorcore.initialized){
26342                 this.buttons.each( function(item){
26343                     item.enable();
26344                 });
26345             }
26346             
26347         }
26348         Roo.log("calling toggole on editor");
26349         // tell the editor that it's been pressed..
26350         this.editor.toggleSourceEdit(sourceEditMode);
26351        
26352     }
26353 });
26354
26355
26356
26357
26358  
26359 /*
26360  * - LGPL
26361  */
26362
26363 /**
26364  * @class Roo.bootstrap.Markdown
26365  * @extends Roo.bootstrap.TextArea
26366  * Bootstrap Showdown editable area
26367  * @cfg {string} content
26368  * 
26369  * @constructor
26370  * Create a new Showdown
26371  */
26372
26373 Roo.bootstrap.Markdown = function(config){
26374     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26375    
26376 };
26377
26378 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26379     
26380     editing :false,
26381     
26382     initEvents : function()
26383     {
26384         
26385         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26386         this.markdownEl = this.el.createChild({
26387             cls : 'roo-markdown-area'
26388         });
26389         this.inputEl().addClass('d-none');
26390         if (this.getValue() == '') {
26391             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26392             
26393         } else {
26394             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26395         }
26396         this.markdownEl.on('click', this.toggleTextEdit, this);
26397         this.on('blur', this.toggleTextEdit, this);
26398         this.on('specialkey', this.resizeTextArea, this);
26399     },
26400     
26401     toggleTextEdit : function()
26402     {
26403         var sh = this.markdownEl.getHeight();
26404         this.inputEl().addClass('d-none');
26405         this.markdownEl.addClass('d-none');
26406         if (!this.editing) {
26407             // show editor?
26408             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26409             this.inputEl().removeClass('d-none');
26410             this.inputEl().focus();
26411             this.editing = true;
26412             return;
26413         }
26414         // show showdown...
26415         this.updateMarkdown();
26416         this.markdownEl.removeClass('d-none');
26417         this.editing = false;
26418         return;
26419     },
26420     updateMarkdown : function()
26421     {
26422         if (this.getValue() == '') {
26423             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26424             return;
26425         }
26426  
26427         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26428     },
26429     
26430     resizeTextArea: function () {
26431         
26432         var sh = 100;
26433         Roo.log([sh, this.getValue().split("\n").length * 30]);
26434         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26435     },
26436     setValue : function(val)
26437     {
26438         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26439         if (!this.editing) {
26440             this.updateMarkdown();
26441         }
26442         
26443     },
26444     focus : function()
26445     {
26446         if (!this.editing) {
26447             this.toggleTextEdit();
26448         }
26449         
26450     }
26451
26452
26453 });
26454 /**
26455  * @class Roo.bootstrap.Table.AbstractSelectionModel
26456  * @extends Roo.util.Observable
26457  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26458  * implemented by descendant classes.  This class should not be directly instantiated.
26459  * @constructor
26460  */
26461 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26462     this.locked = false;
26463     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26464 };
26465
26466
26467 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26468     /** @ignore Called by the grid automatically. Do not call directly. */
26469     init : function(grid){
26470         this.grid = grid;
26471         this.initEvents();
26472     },
26473
26474     /**
26475      * Locks the selections.
26476      */
26477     lock : function(){
26478         this.locked = true;
26479     },
26480
26481     /**
26482      * Unlocks the selections.
26483      */
26484     unlock : function(){
26485         this.locked = false;
26486     },
26487
26488     /**
26489      * Returns true if the selections are locked.
26490      * @return {Boolean}
26491      */
26492     isLocked : function(){
26493         return this.locked;
26494     },
26495     
26496     
26497     initEvents : function ()
26498     {
26499         
26500     }
26501 });
26502 /**
26503  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26504  * @class Roo.bootstrap.Table.RowSelectionModel
26505  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26506  * It supports multiple selections and keyboard selection/navigation. 
26507  * @constructor
26508  * @param {Object} config
26509  */
26510
26511 Roo.bootstrap.Table.RowSelectionModel = function(config){
26512     Roo.apply(this, config);
26513     this.selections = new Roo.util.MixedCollection(false, function(o){
26514         return o.id;
26515     });
26516
26517     this.last = false;
26518     this.lastActive = false;
26519
26520     this.addEvents({
26521         /**
26522              * @event selectionchange
26523              * Fires when the selection changes
26524              * @param {SelectionModel} this
26525              */
26526             "selectionchange" : true,
26527         /**
26528              * @event afterselectionchange
26529              * Fires after the selection changes (eg. by key press or clicking)
26530              * @param {SelectionModel} this
26531              */
26532             "afterselectionchange" : true,
26533         /**
26534              * @event beforerowselect
26535              * Fires when a row is selected being selected, return false to cancel.
26536              * @param {SelectionModel} this
26537              * @param {Number} rowIndex The selected index
26538              * @param {Boolean} keepExisting False if other selections will be cleared
26539              */
26540             "beforerowselect" : true,
26541         /**
26542              * @event rowselect
26543              * Fires when a row is selected.
26544              * @param {SelectionModel} this
26545              * @param {Number} rowIndex The selected index
26546              * @param {Roo.data.Record} r The record
26547              */
26548             "rowselect" : true,
26549         /**
26550              * @event rowdeselect
26551              * Fires when a row is deselected.
26552              * @param {SelectionModel} this
26553              * @param {Number} rowIndex The selected index
26554              */
26555         "rowdeselect" : true
26556     });
26557     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26558     this.locked = false;
26559  };
26560
26561 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26562     /**
26563      * @cfg {Boolean} singleSelect
26564      * True to allow selection of only one row at a time (defaults to false)
26565      */
26566     singleSelect : false,
26567
26568     // private
26569     initEvents : function()
26570     {
26571
26572         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26573         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26574         //}else{ // allow click to work like normal
26575          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26576         //}
26577         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26578         this.grid.on("rowclick", this.handleMouseDown, this);
26579         
26580         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26581             "up" : function(e){
26582                 if(!e.shiftKey){
26583                     this.selectPrevious(e.shiftKey);
26584                 }else if(this.last !== false && this.lastActive !== false){
26585                     var last = this.last;
26586                     this.selectRange(this.last,  this.lastActive-1);
26587                     this.grid.getView().focusRow(this.lastActive);
26588                     if(last !== false){
26589                         this.last = last;
26590                     }
26591                 }else{
26592                     this.selectFirstRow();
26593                 }
26594                 this.fireEvent("afterselectionchange", this);
26595             },
26596             "down" : function(e){
26597                 if(!e.shiftKey){
26598                     this.selectNext(e.shiftKey);
26599                 }else if(this.last !== false && this.lastActive !== false){
26600                     var last = this.last;
26601                     this.selectRange(this.last,  this.lastActive+1);
26602                     this.grid.getView().focusRow(this.lastActive);
26603                     if(last !== false){
26604                         this.last = last;
26605                     }
26606                 }else{
26607                     this.selectFirstRow();
26608                 }
26609                 this.fireEvent("afterselectionchange", this);
26610             },
26611             scope: this
26612         });
26613         this.grid.store.on('load', function(){
26614             this.selections.clear();
26615         },this);
26616         /*
26617         var view = this.grid.view;
26618         view.on("refresh", this.onRefresh, this);
26619         view.on("rowupdated", this.onRowUpdated, this);
26620         view.on("rowremoved", this.onRemove, this);
26621         */
26622     },
26623
26624     // private
26625     onRefresh : function()
26626     {
26627         var ds = this.grid.store, i, v = this.grid.view;
26628         var s = this.selections;
26629         s.each(function(r){
26630             if((i = ds.indexOfId(r.id)) != -1){
26631                 v.onRowSelect(i);
26632             }else{
26633                 s.remove(r);
26634             }
26635         });
26636     },
26637
26638     // private
26639     onRemove : function(v, index, r){
26640         this.selections.remove(r);
26641     },
26642
26643     // private
26644     onRowUpdated : function(v, index, r){
26645         if(this.isSelected(r)){
26646             v.onRowSelect(index);
26647         }
26648     },
26649
26650     /**
26651      * Select records.
26652      * @param {Array} records The records to select
26653      * @param {Boolean} keepExisting (optional) True to keep existing selections
26654      */
26655     selectRecords : function(records, keepExisting)
26656     {
26657         if(!keepExisting){
26658             this.clearSelections();
26659         }
26660             var ds = this.grid.store;
26661         for(var i = 0, len = records.length; i < len; i++){
26662             this.selectRow(ds.indexOf(records[i]), true);
26663         }
26664     },
26665
26666     /**
26667      * Gets the number of selected rows.
26668      * @return {Number}
26669      */
26670     getCount : function(){
26671         return this.selections.length;
26672     },
26673
26674     /**
26675      * Selects the first row in the grid.
26676      */
26677     selectFirstRow : function(){
26678         this.selectRow(0);
26679     },
26680
26681     /**
26682      * Select the last row.
26683      * @param {Boolean} keepExisting (optional) True to keep existing selections
26684      */
26685     selectLastRow : function(keepExisting){
26686         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26687         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26688     },
26689
26690     /**
26691      * Selects the row immediately following the last selected row.
26692      * @param {Boolean} keepExisting (optional) True to keep existing selections
26693      */
26694     selectNext : function(keepExisting)
26695     {
26696             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26697             this.selectRow(this.last+1, keepExisting);
26698             this.grid.getView().focusRow(this.last);
26699         }
26700     },
26701
26702     /**
26703      * Selects the row that precedes the last selected row.
26704      * @param {Boolean} keepExisting (optional) True to keep existing selections
26705      */
26706     selectPrevious : function(keepExisting){
26707         if(this.last){
26708             this.selectRow(this.last-1, keepExisting);
26709             this.grid.getView().focusRow(this.last);
26710         }
26711     },
26712
26713     /**
26714      * Returns the selected records
26715      * @return {Array} Array of selected records
26716      */
26717     getSelections : function(){
26718         return [].concat(this.selections.items);
26719     },
26720
26721     /**
26722      * Returns the first selected record.
26723      * @return {Record}
26724      */
26725     getSelected : function(){
26726         return this.selections.itemAt(0);
26727     },
26728
26729
26730     /**
26731      * Clears all selections.
26732      */
26733     clearSelections : function(fast)
26734     {
26735         if(this.locked) {
26736             return;
26737         }
26738         if(fast !== true){
26739                 var ds = this.grid.store;
26740             var s = this.selections;
26741             s.each(function(r){
26742                 this.deselectRow(ds.indexOfId(r.id));
26743             }, this);
26744             s.clear();
26745         }else{
26746             this.selections.clear();
26747         }
26748         this.last = false;
26749     },
26750
26751
26752     /**
26753      * Selects all rows.
26754      */
26755     selectAll : function(){
26756         if(this.locked) {
26757             return;
26758         }
26759         this.selections.clear();
26760         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26761             this.selectRow(i, true);
26762         }
26763     },
26764
26765     /**
26766      * Returns True if there is a selection.
26767      * @return {Boolean}
26768      */
26769     hasSelection : function(){
26770         return this.selections.length > 0;
26771     },
26772
26773     /**
26774      * Returns True if the specified row is selected.
26775      * @param {Number/Record} record The record or index of the record to check
26776      * @return {Boolean}
26777      */
26778     isSelected : function(index){
26779             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26780         return (r && this.selections.key(r.id) ? true : false);
26781     },
26782
26783     /**
26784      * Returns True if the specified record id is selected.
26785      * @param {String} id The id of record to check
26786      * @return {Boolean}
26787      */
26788     isIdSelected : function(id){
26789         return (this.selections.key(id) ? true : false);
26790     },
26791
26792
26793     // private
26794     handleMouseDBClick : function(e, t){
26795         
26796     },
26797     // private
26798     handleMouseDown : function(e, t)
26799     {
26800             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26801         if(this.isLocked() || rowIndex < 0 ){
26802             return;
26803         };
26804         if(e.shiftKey && this.last !== false){
26805             var last = this.last;
26806             this.selectRange(last, rowIndex, e.ctrlKey);
26807             this.last = last; // reset the last
26808             t.focus();
26809     
26810         }else{
26811             var isSelected = this.isSelected(rowIndex);
26812             //Roo.log("select row:" + rowIndex);
26813             if(isSelected){
26814                 this.deselectRow(rowIndex);
26815             } else {
26816                         this.selectRow(rowIndex, true);
26817             }
26818     
26819             /*
26820                 if(e.button !== 0 && isSelected){
26821                 alert('rowIndex 2: ' + rowIndex);
26822                     view.focusRow(rowIndex);
26823                 }else if(e.ctrlKey && isSelected){
26824                     this.deselectRow(rowIndex);
26825                 }else if(!isSelected){
26826                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26827                     view.focusRow(rowIndex);
26828                 }
26829             */
26830         }
26831         this.fireEvent("afterselectionchange", this);
26832     },
26833     // private
26834     handleDragableRowClick :  function(grid, rowIndex, e) 
26835     {
26836         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26837             this.selectRow(rowIndex, false);
26838             grid.view.focusRow(rowIndex);
26839              this.fireEvent("afterselectionchange", this);
26840         }
26841     },
26842     
26843     /**
26844      * Selects multiple rows.
26845      * @param {Array} rows Array of the indexes of the row to select
26846      * @param {Boolean} keepExisting (optional) True to keep existing selections
26847      */
26848     selectRows : function(rows, keepExisting){
26849         if(!keepExisting){
26850             this.clearSelections();
26851         }
26852         for(var i = 0, len = rows.length; i < len; i++){
26853             this.selectRow(rows[i], true);
26854         }
26855     },
26856
26857     /**
26858      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26859      * @param {Number} startRow The index of the first row in the range
26860      * @param {Number} endRow The index of the last row in the range
26861      * @param {Boolean} keepExisting (optional) True to retain existing selections
26862      */
26863     selectRange : function(startRow, endRow, keepExisting){
26864         if(this.locked) {
26865             return;
26866         }
26867         if(!keepExisting){
26868             this.clearSelections();
26869         }
26870         if(startRow <= endRow){
26871             for(var i = startRow; i <= endRow; i++){
26872                 this.selectRow(i, true);
26873             }
26874         }else{
26875             for(var i = startRow; i >= endRow; i--){
26876                 this.selectRow(i, true);
26877             }
26878         }
26879     },
26880
26881     /**
26882      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26883      * @param {Number} startRow The index of the first row in the range
26884      * @param {Number} endRow The index of the last row in the range
26885      */
26886     deselectRange : function(startRow, endRow, preventViewNotify){
26887         if(this.locked) {
26888             return;
26889         }
26890         for(var i = startRow; i <= endRow; i++){
26891             this.deselectRow(i, preventViewNotify);
26892         }
26893     },
26894
26895     /**
26896      * Selects a row.
26897      * @param {Number} row The index of the row to select
26898      * @param {Boolean} keepExisting (optional) True to keep existing selections
26899      */
26900     selectRow : function(index, keepExisting, preventViewNotify)
26901     {
26902             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26903             return;
26904         }
26905         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26906             if(!keepExisting || this.singleSelect){
26907                 this.clearSelections();
26908             }
26909             
26910             var r = this.grid.store.getAt(index);
26911             //console.log('selectRow - record id :' + r.id);
26912             
26913             this.selections.add(r);
26914             this.last = this.lastActive = index;
26915             if(!preventViewNotify){
26916                 var proxy = new Roo.Element(
26917                                 this.grid.getRowDom(index)
26918                 );
26919                 proxy.addClass('bg-info info');
26920             }
26921             this.fireEvent("rowselect", this, index, r);
26922             this.fireEvent("selectionchange", this);
26923         }
26924     },
26925
26926     /**
26927      * Deselects a row.
26928      * @param {Number} row The index of the row to deselect
26929      */
26930     deselectRow : function(index, preventViewNotify)
26931     {
26932         if(this.locked) {
26933             return;
26934         }
26935         if(this.last == index){
26936             this.last = false;
26937         }
26938         if(this.lastActive == index){
26939             this.lastActive = false;
26940         }
26941         
26942         var r = this.grid.store.getAt(index);
26943         if (!r) {
26944             return;
26945         }
26946         
26947         this.selections.remove(r);
26948         //.console.log('deselectRow - record id :' + r.id);
26949         if(!preventViewNotify){
26950         
26951             var proxy = new Roo.Element(
26952                 this.grid.getRowDom(index)
26953             );
26954             proxy.removeClass('bg-info info');
26955         }
26956         this.fireEvent("rowdeselect", this, index);
26957         this.fireEvent("selectionchange", this);
26958     },
26959
26960     // private
26961     restoreLast : function(){
26962         if(this._last){
26963             this.last = this._last;
26964         }
26965     },
26966
26967     // private
26968     acceptsNav : function(row, col, cm){
26969         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26970     },
26971
26972     // private
26973     onEditorKey : function(field, e){
26974         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26975         if(k == e.TAB){
26976             e.stopEvent();
26977             ed.completeEdit();
26978             if(e.shiftKey){
26979                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26980             }else{
26981                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26982             }
26983         }else if(k == e.ENTER && !e.ctrlKey){
26984             e.stopEvent();
26985             ed.completeEdit();
26986             if(e.shiftKey){
26987                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26988             }else{
26989                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26990             }
26991         }else if(k == e.ESC){
26992             ed.cancelEdit();
26993         }
26994         if(newCell){
26995             g.startEditing(newCell[0], newCell[1]);
26996         }
26997     }
26998 });
26999 /*
27000  * Based on:
27001  * Ext JS Library 1.1.1
27002  * Copyright(c) 2006-2007, Ext JS, LLC.
27003  *
27004  * Originally Released Under LGPL - original licence link has changed is not relivant.
27005  *
27006  * Fork - LGPL
27007  * <script type="text/javascript">
27008  */
27009  
27010 /**
27011  * @class Roo.bootstrap.PagingToolbar
27012  * @extends Roo.bootstrap.NavSimplebar
27013  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27014  * @constructor
27015  * Create a new PagingToolbar
27016  * @param {Object} config The config object
27017  * @param {Roo.data.Store} store
27018  */
27019 Roo.bootstrap.PagingToolbar = function(config)
27020 {
27021     // old args format still supported... - xtype is prefered..
27022         // created from xtype...
27023     
27024     this.ds = config.dataSource;
27025     
27026     if (config.store && !this.ds) {
27027         this.store= Roo.factory(config.store, Roo.data);
27028         this.ds = this.store;
27029         this.ds.xmodule = this.xmodule || false;
27030     }
27031     
27032     this.toolbarItems = [];
27033     if (config.items) {
27034         this.toolbarItems = config.items;
27035     }
27036     
27037     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27038     
27039     this.cursor = 0;
27040     
27041     if (this.ds) { 
27042         this.bind(this.ds);
27043     }
27044     
27045     if (Roo.bootstrap.version == 4) {
27046         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27047     } else {
27048         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27049     }
27050     
27051 };
27052
27053 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27054     /**
27055      * @cfg {Roo.data.Store} dataSource
27056      * The underlying data store providing the paged data
27057      */
27058     /**
27059      * @cfg {String/HTMLElement/Element} container
27060      * container The id or element that will contain the toolbar
27061      */
27062     /**
27063      * @cfg {Boolean} displayInfo
27064      * True to display the displayMsg (defaults to false)
27065      */
27066     /**
27067      * @cfg {Number} pageSize
27068      * The number of records to display per page (defaults to 20)
27069      */
27070     pageSize: 20,
27071     /**
27072      * @cfg {String} displayMsg
27073      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27074      */
27075     displayMsg : 'Displaying {0} - {1} of {2}',
27076     /**
27077      * @cfg {String} emptyMsg
27078      * The message to display when no records are found (defaults to "No data to display")
27079      */
27080     emptyMsg : 'No data to display',
27081     /**
27082      * Customizable piece of the default paging text (defaults to "Page")
27083      * @type String
27084      */
27085     beforePageText : "Page",
27086     /**
27087      * Customizable piece of the default paging text (defaults to "of %0")
27088      * @type String
27089      */
27090     afterPageText : "of {0}",
27091     /**
27092      * Customizable piece of the default paging text (defaults to "First Page")
27093      * @type String
27094      */
27095     firstText : "First Page",
27096     /**
27097      * Customizable piece of the default paging text (defaults to "Previous Page")
27098      * @type String
27099      */
27100     prevText : "Previous Page",
27101     /**
27102      * Customizable piece of the default paging text (defaults to "Next Page")
27103      * @type String
27104      */
27105     nextText : "Next Page",
27106     /**
27107      * Customizable piece of the default paging text (defaults to "Last Page")
27108      * @type String
27109      */
27110     lastText : "Last Page",
27111     /**
27112      * Customizable piece of the default paging text (defaults to "Refresh")
27113      * @type String
27114      */
27115     refreshText : "Refresh",
27116
27117     buttons : false,
27118     // private
27119     onRender : function(ct, position) 
27120     {
27121         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27122         this.navgroup.parentId = this.id;
27123         this.navgroup.onRender(this.el, null);
27124         // add the buttons to the navgroup
27125         
27126         if(this.displayInfo){
27127             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27128             this.displayEl = this.el.select('.x-paging-info', true).first();
27129 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27130 //            this.displayEl = navel.el.select('span',true).first();
27131         }
27132         
27133         var _this = this;
27134         
27135         if(this.buttons){
27136             Roo.each(_this.buttons, function(e){ // this might need to use render????
27137                Roo.factory(e).render(_this.el);
27138             });
27139         }
27140             
27141         Roo.each(_this.toolbarItems, function(e) {
27142             _this.navgroup.addItem(e);
27143         });
27144         
27145         
27146         this.first = this.navgroup.addItem({
27147             tooltip: this.firstText,
27148             cls: "prev btn-outline-secondary",
27149             html : ' <i class="fa fa-step-backward"></i>',
27150             disabled: true,
27151             preventDefault: true,
27152             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27153         });
27154         
27155         this.prev =  this.navgroup.addItem({
27156             tooltip: this.prevText,
27157             cls: "prev btn-outline-secondary",
27158             html : ' <i class="fa fa-backward"></i>',
27159             disabled: true,
27160             preventDefault: true,
27161             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27162         });
27163     //this.addSeparator();
27164         
27165         
27166         var field = this.navgroup.addItem( {
27167             tagtype : 'span',
27168             cls : 'x-paging-position  btn-outline-secondary',
27169              disabled: true,
27170             html : this.beforePageText  +
27171                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27172                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27173          } ); //?? escaped?
27174         
27175         this.field = field.el.select('input', true).first();
27176         this.field.on("keydown", this.onPagingKeydown, this);
27177         this.field.on("focus", function(){this.dom.select();});
27178     
27179     
27180         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27181         //this.field.setHeight(18);
27182         //this.addSeparator();
27183         this.next = this.navgroup.addItem({
27184             tooltip: this.nextText,
27185             cls: "next btn-outline-secondary",
27186             html : ' <i class="fa fa-forward"></i>',
27187             disabled: true,
27188             preventDefault: true,
27189             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27190         });
27191         this.last = this.navgroup.addItem({
27192             tooltip: this.lastText,
27193             html : ' <i class="fa fa-step-forward"></i>',
27194             cls: "next btn-outline-secondary",
27195             disabled: true,
27196             preventDefault: true,
27197             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27198         });
27199     //this.addSeparator();
27200         this.loading = this.navgroup.addItem({
27201             tooltip: this.refreshText,
27202             cls: "btn-outline-secondary",
27203             html : ' <i class="fa fa-refresh"></i>',
27204             preventDefault: true,
27205             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27206         });
27207         
27208     },
27209
27210     // private
27211     updateInfo : function(){
27212         if(this.displayEl){
27213             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27214             var msg = count == 0 ?
27215                 this.emptyMsg :
27216                 String.format(
27217                     this.displayMsg,
27218                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27219                 );
27220             this.displayEl.update(msg);
27221         }
27222     },
27223
27224     // private
27225     onLoad : function(ds, r, o)
27226     {
27227         this.cursor = o.params.start ? o.params.start : 0;
27228         
27229         var d = this.getPageData(),
27230             ap = d.activePage,
27231             ps = d.pages;
27232         
27233         
27234         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27235         this.field.dom.value = ap;
27236         this.first.setDisabled(ap == 1);
27237         this.prev.setDisabled(ap == 1);
27238         this.next.setDisabled(ap == ps);
27239         this.last.setDisabled(ap == ps);
27240         this.loading.enable();
27241         this.updateInfo();
27242     },
27243
27244     // private
27245     getPageData : function(){
27246         var total = this.ds.getTotalCount();
27247         return {
27248             total : total,
27249             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27250             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27251         };
27252     },
27253
27254     // private
27255     onLoadError : function(){
27256         this.loading.enable();
27257     },
27258
27259     // private
27260     onPagingKeydown : function(e){
27261         var k = e.getKey();
27262         var d = this.getPageData();
27263         if(k == e.RETURN){
27264             var v = this.field.dom.value, pageNum;
27265             if(!v || isNaN(pageNum = parseInt(v, 10))){
27266                 this.field.dom.value = d.activePage;
27267                 return;
27268             }
27269             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27270             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27271             e.stopEvent();
27272         }
27273         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))
27274         {
27275           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27276           this.field.dom.value = pageNum;
27277           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27278           e.stopEvent();
27279         }
27280         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27281         {
27282           var v = this.field.dom.value, pageNum; 
27283           var increment = (e.shiftKey) ? 10 : 1;
27284           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27285                 increment *= -1;
27286           }
27287           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27288             this.field.dom.value = d.activePage;
27289             return;
27290           }
27291           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27292           {
27293             this.field.dom.value = parseInt(v, 10) + increment;
27294             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27295             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27296           }
27297           e.stopEvent();
27298         }
27299     },
27300
27301     // private
27302     beforeLoad : function(){
27303         if(this.loading){
27304             this.loading.disable();
27305         }
27306     },
27307
27308     // private
27309     onClick : function(which){
27310         
27311         var ds = this.ds;
27312         if (!ds) {
27313             return;
27314         }
27315         
27316         switch(which){
27317             case "first":
27318                 ds.load({params:{start: 0, limit: this.pageSize}});
27319             break;
27320             case "prev":
27321                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27322             break;
27323             case "next":
27324                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27325             break;
27326             case "last":
27327                 var total = ds.getTotalCount();
27328                 var extra = total % this.pageSize;
27329                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27330                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27331             break;
27332             case "refresh":
27333                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27334             break;
27335         }
27336     },
27337
27338     /**
27339      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27340      * @param {Roo.data.Store} store The data store to unbind
27341      */
27342     unbind : function(ds){
27343         ds.un("beforeload", this.beforeLoad, this);
27344         ds.un("load", this.onLoad, this);
27345         ds.un("loadexception", this.onLoadError, this);
27346         ds.un("remove", this.updateInfo, this);
27347         ds.un("add", this.updateInfo, this);
27348         this.ds = undefined;
27349     },
27350
27351     /**
27352      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27353      * @param {Roo.data.Store} store The data store to bind
27354      */
27355     bind : function(ds){
27356         ds.on("beforeload", this.beforeLoad, this);
27357         ds.on("load", this.onLoad, this);
27358         ds.on("loadexception", this.onLoadError, this);
27359         ds.on("remove", this.updateInfo, this);
27360         ds.on("add", this.updateInfo, this);
27361         this.ds = ds;
27362     }
27363 });/*
27364  * - LGPL
27365  *
27366  * element
27367  * 
27368  */
27369
27370 /**
27371  * @class Roo.bootstrap.MessageBar
27372  * @extends Roo.bootstrap.Component
27373  * Bootstrap MessageBar class
27374  * @cfg {String} html contents of the MessageBar
27375  * @cfg {String} weight (info | success | warning | danger) default info
27376  * @cfg {String} beforeClass insert the bar before the given class
27377  * @cfg {Boolean} closable (true | false) default false
27378  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27379  * 
27380  * @constructor
27381  * Create a new Element
27382  * @param {Object} config The config object
27383  */
27384
27385 Roo.bootstrap.MessageBar = function(config){
27386     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27387 };
27388
27389 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27390     
27391     html: '',
27392     weight: 'info',
27393     closable: false,
27394     fixed: false,
27395     beforeClass: 'bootstrap-sticky-wrap',
27396     
27397     getAutoCreate : function(){
27398         
27399         var cfg = {
27400             tag: 'div',
27401             cls: 'alert alert-dismissable alert-' + this.weight,
27402             cn: [
27403                 {
27404                     tag: 'span',
27405                     cls: 'message',
27406                     html: this.html || ''
27407                 }
27408             ]
27409         };
27410         
27411         if(this.fixed){
27412             cfg.cls += ' alert-messages-fixed';
27413         }
27414         
27415         if(this.closable){
27416             cfg.cn.push({
27417                 tag: 'button',
27418                 cls: 'close',
27419                 html: 'x'
27420             });
27421         }
27422         
27423         return cfg;
27424     },
27425     
27426     onRender : function(ct, position)
27427     {
27428         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27429         
27430         if(!this.el){
27431             var cfg = Roo.apply({},  this.getAutoCreate());
27432             cfg.id = Roo.id();
27433             
27434             if (this.cls) {
27435                 cfg.cls += ' ' + this.cls;
27436             }
27437             if (this.style) {
27438                 cfg.style = this.style;
27439             }
27440             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27441             
27442             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27443         }
27444         
27445         this.el.select('>button.close').on('click', this.hide, this);
27446         
27447     },
27448     
27449     show : function()
27450     {
27451         if (!this.rendered) {
27452             this.render();
27453         }
27454         
27455         this.el.show();
27456         
27457         this.fireEvent('show', this);
27458         
27459     },
27460     
27461     hide : function()
27462     {
27463         if (!this.rendered) {
27464             this.render();
27465         }
27466         
27467         this.el.hide();
27468         
27469         this.fireEvent('hide', this);
27470     },
27471     
27472     update : function()
27473     {
27474 //        var e = this.el.dom.firstChild;
27475 //        
27476 //        if(this.closable){
27477 //            e = e.nextSibling;
27478 //        }
27479 //        
27480 //        e.data = this.html || '';
27481
27482         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27483     }
27484    
27485 });
27486
27487  
27488
27489      /*
27490  * - LGPL
27491  *
27492  * Graph
27493  * 
27494  */
27495
27496
27497 /**
27498  * @class Roo.bootstrap.Graph
27499  * @extends Roo.bootstrap.Component
27500  * Bootstrap Graph class
27501 > Prameters
27502  -sm {number} sm 4
27503  -md {number} md 5
27504  @cfg {String} graphtype  bar | vbar | pie
27505  @cfg {number} g_x coodinator | centre x (pie)
27506  @cfg {number} g_y coodinator | centre y (pie)
27507  @cfg {number} g_r radius (pie)
27508  @cfg {number} g_height height of the chart (respected by all elements in the set)
27509  @cfg {number} g_width width of the chart (respected by all elements in the set)
27510  @cfg {Object} title The title of the chart
27511     
27512  -{Array}  values
27513  -opts (object) options for the chart 
27514      o {
27515      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27516      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27517      o vgutter (number)
27518      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.
27519      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27520      o to
27521      o stretch (boolean)
27522      o }
27523  -opts (object) options for the pie
27524      o{
27525      o cut
27526      o startAngle (number)
27527      o endAngle (number)
27528      } 
27529  *
27530  * @constructor
27531  * Create a new Input
27532  * @param {Object} config The config object
27533  */
27534
27535 Roo.bootstrap.Graph = function(config){
27536     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27537     
27538     this.addEvents({
27539         // img events
27540         /**
27541          * @event click
27542          * The img click event for the img.
27543          * @param {Roo.EventObject} e
27544          */
27545         "click" : true
27546     });
27547 };
27548
27549 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27550     
27551     sm: 4,
27552     md: 5,
27553     graphtype: 'bar',
27554     g_height: 250,
27555     g_width: 400,
27556     g_x: 50,
27557     g_y: 50,
27558     g_r: 30,
27559     opts:{
27560         //g_colors: this.colors,
27561         g_type: 'soft',
27562         g_gutter: '20%'
27563
27564     },
27565     title : false,
27566
27567     getAutoCreate : function(){
27568         
27569         var cfg = {
27570             tag: 'div',
27571             html : null
27572         };
27573         
27574         
27575         return  cfg;
27576     },
27577
27578     onRender : function(ct,position){
27579         
27580         
27581         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27582         
27583         if (typeof(Raphael) == 'undefined') {
27584             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27585             return;
27586         }
27587         
27588         this.raphael = Raphael(this.el.dom);
27589         
27590                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27591                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27592                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27593                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27594                 /*
27595                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27596                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27597                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27598                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27599                 
27600                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27601                 r.barchart(330, 10, 300, 220, data1);
27602                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27603                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27604                 */
27605                 
27606                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27607                 // r.barchart(30, 30, 560, 250,  xdata, {
27608                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27609                 //     axis : "0 0 1 1",
27610                 //     axisxlabels :  xdata
27611                 //     //yvalues : cols,
27612                    
27613                 // });
27614 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27615 //        
27616 //        this.load(null,xdata,{
27617 //                axis : "0 0 1 1",
27618 //                axisxlabels :  xdata
27619 //                });
27620
27621     },
27622
27623     load : function(graphtype,xdata,opts)
27624     {
27625         this.raphael.clear();
27626         if(!graphtype) {
27627             graphtype = this.graphtype;
27628         }
27629         if(!opts){
27630             opts = this.opts;
27631         }
27632         var r = this.raphael,
27633             fin = function () {
27634                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27635             },
27636             fout = function () {
27637                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27638             },
27639             pfin = function() {
27640                 this.sector.stop();
27641                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27642
27643                 if (this.label) {
27644                     this.label[0].stop();
27645                     this.label[0].attr({ r: 7.5 });
27646                     this.label[1].attr({ "font-weight": 800 });
27647                 }
27648             },
27649             pfout = function() {
27650                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27651
27652                 if (this.label) {
27653                     this.label[0].animate({ r: 5 }, 500, "bounce");
27654                     this.label[1].attr({ "font-weight": 400 });
27655                 }
27656             };
27657
27658         switch(graphtype){
27659             case 'bar':
27660                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27661                 break;
27662             case 'hbar':
27663                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27664                 break;
27665             case 'pie':
27666 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27667 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27668 //            
27669                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27670                 
27671                 break;
27672
27673         }
27674         
27675         if(this.title){
27676             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27677         }
27678         
27679     },
27680     
27681     setTitle: function(o)
27682     {
27683         this.title = o;
27684     },
27685     
27686     initEvents: function() {
27687         
27688         if(!this.href){
27689             this.el.on('click', this.onClick, this);
27690         }
27691     },
27692     
27693     onClick : function(e)
27694     {
27695         Roo.log('img onclick');
27696         this.fireEvent('click', this, e);
27697     }
27698    
27699 });
27700
27701  
27702 /*
27703  * - LGPL
27704  *
27705  * numberBox
27706  * 
27707  */
27708 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27709
27710 /**
27711  * @class Roo.bootstrap.dash.NumberBox
27712  * @extends Roo.bootstrap.Component
27713  * Bootstrap NumberBox class
27714  * @cfg {String} headline Box headline
27715  * @cfg {String} content Box content
27716  * @cfg {String} icon Box icon
27717  * @cfg {String} footer Footer text
27718  * @cfg {String} fhref Footer href
27719  * 
27720  * @constructor
27721  * Create a new NumberBox
27722  * @param {Object} config The config object
27723  */
27724
27725
27726 Roo.bootstrap.dash.NumberBox = function(config){
27727     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27728     
27729 };
27730
27731 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27732     
27733     headline : '',
27734     content : '',
27735     icon : '',
27736     footer : '',
27737     fhref : '',
27738     ficon : '',
27739     
27740     getAutoCreate : function(){
27741         
27742         var cfg = {
27743             tag : 'div',
27744             cls : 'small-box ',
27745             cn : [
27746                 {
27747                     tag : 'div',
27748                     cls : 'inner',
27749                     cn :[
27750                         {
27751                             tag : 'h3',
27752                             cls : 'roo-headline',
27753                             html : this.headline
27754                         },
27755                         {
27756                             tag : 'p',
27757                             cls : 'roo-content',
27758                             html : this.content
27759                         }
27760                     ]
27761                 }
27762             ]
27763         };
27764         
27765         if(this.icon){
27766             cfg.cn.push({
27767                 tag : 'div',
27768                 cls : 'icon',
27769                 cn :[
27770                     {
27771                         tag : 'i',
27772                         cls : 'ion ' + this.icon
27773                     }
27774                 ]
27775             });
27776         }
27777         
27778         if(this.footer){
27779             var footer = {
27780                 tag : 'a',
27781                 cls : 'small-box-footer',
27782                 href : this.fhref || '#',
27783                 html : this.footer
27784             };
27785             
27786             cfg.cn.push(footer);
27787             
27788         }
27789         
27790         return  cfg;
27791     },
27792
27793     onRender : function(ct,position){
27794         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27795
27796
27797        
27798                 
27799     },
27800
27801     setHeadline: function (value)
27802     {
27803         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27804     },
27805     
27806     setFooter: function (value, href)
27807     {
27808         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27809         
27810         if(href){
27811             this.el.select('a.small-box-footer',true).first().attr('href', href);
27812         }
27813         
27814     },
27815
27816     setContent: function (value)
27817     {
27818         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27819     },
27820
27821     initEvents: function() 
27822     {   
27823         
27824     }
27825     
27826 });
27827
27828  
27829 /*
27830  * - LGPL
27831  *
27832  * TabBox
27833  * 
27834  */
27835 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27836
27837 /**
27838  * @class Roo.bootstrap.dash.TabBox
27839  * @extends Roo.bootstrap.Component
27840  * Bootstrap TabBox class
27841  * @cfg {String} title Title of the TabBox
27842  * @cfg {String} icon Icon of the TabBox
27843  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27844  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27845  * 
27846  * @constructor
27847  * Create a new TabBox
27848  * @param {Object} config The config object
27849  */
27850
27851
27852 Roo.bootstrap.dash.TabBox = function(config){
27853     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27854     this.addEvents({
27855         // raw events
27856         /**
27857          * @event addpane
27858          * When a pane is added
27859          * @param {Roo.bootstrap.dash.TabPane} pane
27860          */
27861         "addpane" : true,
27862         /**
27863          * @event activatepane
27864          * When a pane is activated
27865          * @param {Roo.bootstrap.dash.TabPane} pane
27866          */
27867         "activatepane" : true
27868         
27869          
27870     });
27871     
27872     this.panes = [];
27873 };
27874
27875 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27876
27877     title : '',
27878     icon : false,
27879     showtabs : true,
27880     tabScrollable : false,
27881     
27882     getChildContainer : function()
27883     {
27884         return this.el.select('.tab-content', true).first();
27885     },
27886     
27887     getAutoCreate : function(){
27888         
27889         var header = {
27890             tag: 'li',
27891             cls: 'pull-left header',
27892             html: this.title,
27893             cn : []
27894         };
27895         
27896         if(this.icon){
27897             header.cn.push({
27898                 tag: 'i',
27899                 cls: 'fa ' + this.icon
27900             });
27901         }
27902         
27903         var h = {
27904             tag: 'ul',
27905             cls: 'nav nav-tabs pull-right',
27906             cn: [
27907                 header
27908             ]
27909         };
27910         
27911         if(this.tabScrollable){
27912             h = {
27913                 tag: 'div',
27914                 cls: 'tab-header',
27915                 cn: [
27916                     {
27917                         tag: 'ul',
27918                         cls: 'nav nav-tabs pull-right',
27919                         cn: [
27920                             header
27921                         ]
27922                     }
27923                 ]
27924             };
27925         }
27926         
27927         var cfg = {
27928             tag: 'div',
27929             cls: 'nav-tabs-custom',
27930             cn: [
27931                 h,
27932                 {
27933                     tag: 'div',
27934                     cls: 'tab-content no-padding',
27935                     cn: []
27936                 }
27937             ]
27938         };
27939
27940         return  cfg;
27941     },
27942     initEvents : function()
27943     {
27944         //Roo.log('add add pane handler');
27945         this.on('addpane', this.onAddPane, this);
27946     },
27947      /**
27948      * Updates the box title
27949      * @param {String} html to set the title to.
27950      */
27951     setTitle : function(value)
27952     {
27953         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27954     },
27955     onAddPane : function(pane)
27956     {
27957         this.panes.push(pane);
27958         //Roo.log('addpane');
27959         //Roo.log(pane);
27960         // tabs are rendere left to right..
27961         if(!this.showtabs){
27962             return;
27963         }
27964         
27965         var ctr = this.el.select('.nav-tabs', true).first();
27966          
27967          
27968         var existing = ctr.select('.nav-tab',true);
27969         var qty = existing.getCount();;
27970         
27971         
27972         var tab = ctr.createChild({
27973             tag : 'li',
27974             cls : 'nav-tab' + (qty ? '' : ' active'),
27975             cn : [
27976                 {
27977                     tag : 'a',
27978                     href:'#',
27979                     html : pane.title
27980                 }
27981             ]
27982         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27983         pane.tab = tab;
27984         
27985         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27986         if (!qty) {
27987             pane.el.addClass('active');
27988         }
27989         
27990                 
27991     },
27992     onTabClick : function(ev,un,ob,pane)
27993     {
27994         //Roo.log('tab - prev default');
27995         ev.preventDefault();
27996         
27997         
27998         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27999         pane.tab.addClass('active');
28000         //Roo.log(pane.title);
28001         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28002         // technically we should have a deactivate event.. but maybe add later.
28003         // and it should not de-activate the selected tab...
28004         this.fireEvent('activatepane', pane);
28005         pane.el.addClass('active');
28006         pane.fireEvent('activate');
28007         
28008         
28009     },
28010     
28011     getActivePane : function()
28012     {
28013         var r = false;
28014         Roo.each(this.panes, function(p) {
28015             if(p.el.hasClass('active')){
28016                 r = p;
28017                 return false;
28018             }
28019             
28020             return;
28021         });
28022         
28023         return r;
28024     }
28025     
28026     
28027 });
28028
28029  
28030 /*
28031  * - LGPL
28032  *
28033  * Tab pane
28034  * 
28035  */
28036 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28037 /**
28038  * @class Roo.bootstrap.TabPane
28039  * @extends Roo.bootstrap.Component
28040  * Bootstrap TabPane class
28041  * @cfg {Boolean} active (false | true) Default false
28042  * @cfg {String} title title of panel
28043
28044  * 
28045  * @constructor
28046  * Create a new TabPane
28047  * @param {Object} config The config object
28048  */
28049
28050 Roo.bootstrap.dash.TabPane = function(config){
28051     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28052     
28053     this.addEvents({
28054         // raw events
28055         /**
28056          * @event activate
28057          * When a pane is activated
28058          * @param {Roo.bootstrap.dash.TabPane} pane
28059          */
28060         "activate" : true
28061          
28062     });
28063 };
28064
28065 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28066     
28067     active : false,
28068     title : '',
28069     
28070     // the tabBox that this is attached to.
28071     tab : false,
28072      
28073     getAutoCreate : function() 
28074     {
28075         var cfg = {
28076             tag: 'div',
28077             cls: 'tab-pane'
28078         };
28079         
28080         if(this.active){
28081             cfg.cls += ' active';
28082         }
28083         
28084         return cfg;
28085     },
28086     initEvents  : function()
28087     {
28088         //Roo.log('trigger add pane handler');
28089         this.parent().fireEvent('addpane', this)
28090     },
28091     
28092      /**
28093      * Updates the tab title 
28094      * @param {String} html to set the title to.
28095      */
28096     setTitle: function(str)
28097     {
28098         if (!this.tab) {
28099             return;
28100         }
28101         this.title = str;
28102         this.tab.select('a', true).first().dom.innerHTML = str;
28103         
28104     }
28105     
28106     
28107     
28108 });
28109
28110  
28111
28112
28113  /*
28114  * - LGPL
28115  *
28116  * menu
28117  * 
28118  */
28119 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28120
28121 /**
28122  * @class Roo.bootstrap.menu.Menu
28123  * @extends Roo.bootstrap.Component
28124  * Bootstrap Menu class - container for Menu
28125  * @cfg {String} html Text of the menu
28126  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28127  * @cfg {String} icon Font awesome icon
28128  * @cfg {String} pos Menu align to (top | bottom) default bottom
28129  * 
28130  * 
28131  * @constructor
28132  * Create a new Menu
28133  * @param {Object} config The config object
28134  */
28135
28136
28137 Roo.bootstrap.menu.Menu = function(config){
28138     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28139     
28140     this.addEvents({
28141         /**
28142          * @event beforeshow
28143          * Fires before this menu is displayed
28144          * @param {Roo.bootstrap.menu.Menu} this
28145          */
28146         beforeshow : true,
28147         /**
28148          * @event beforehide
28149          * Fires before this menu is hidden
28150          * @param {Roo.bootstrap.menu.Menu} this
28151          */
28152         beforehide : true,
28153         /**
28154          * @event show
28155          * Fires after this menu is displayed
28156          * @param {Roo.bootstrap.menu.Menu} this
28157          */
28158         show : true,
28159         /**
28160          * @event hide
28161          * Fires after this menu is hidden
28162          * @param {Roo.bootstrap.menu.Menu} this
28163          */
28164         hide : true,
28165         /**
28166          * @event click
28167          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28168          * @param {Roo.bootstrap.menu.Menu} this
28169          * @param {Roo.EventObject} e
28170          */
28171         click : true
28172     });
28173     
28174 };
28175
28176 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28177     
28178     submenu : false,
28179     html : '',
28180     weight : 'default',
28181     icon : false,
28182     pos : 'bottom',
28183     
28184     
28185     getChildContainer : function() {
28186         if(this.isSubMenu){
28187             return this.el;
28188         }
28189         
28190         return this.el.select('ul.dropdown-menu', true).first();  
28191     },
28192     
28193     getAutoCreate : function()
28194     {
28195         var text = [
28196             {
28197                 tag : 'span',
28198                 cls : 'roo-menu-text',
28199                 html : this.html
28200             }
28201         ];
28202         
28203         if(this.icon){
28204             text.unshift({
28205                 tag : 'i',
28206                 cls : 'fa ' + this.icon
28207             })
28208         }
28209         
28210         
28211         var cfg = {
28212             tag : 'div',
28213             cls : 'btn-group',
28214             cn : [
28215                 {
28216                     tag : 'button',
28217                     cls : 'dropdown-button btn btn-' + this.weight,
28218                     cn : text
28219                 },
28220                 {
28221                     tag : 'button',
28222                     cls : 'dropdown-toggle btn btn-' + this.weight,
28223                     cn : [
28224                         {
28225                             tag : 'span',
28226                             cls : 'caret'
28227                         }
28228                     ]
28229                 },
28230                 {
28231                     tag : 'ul',
28232                     cls : 'dropdown-menu'
28233                 }
28234             ]
28235             
28236         };
28237         
28238         if(this.pos == 'top'){
28239             cfg.cls += ' dropup';
28240         }
28241         
28242         if(this.isSubMenu){
28243             cfg = {
28244                 tag : 'ul',
28245                 cls : 'dropdown-menu'
28246             }
28247         }
28248         
28249         return cfg;
28250     },
28251     
28252     onRender : function(ct, position)
28253     {
28254         this.isSubMenu = ct.hasClass('dropdown-submenu');
28255         
28256         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28257     },
28258     
28259     initEvents : function() 
28260     {
28261         if(this.isSubMenu){
28262             return;
28263         }
28264         
28265         this.hidden = true;
28266         
28267         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28268         this.triggerEl.on('click', this.onTriggerPress, this);
28269         
28270         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28271         this.buttonEl.on('click', this.onClick, this);
28272         
28273     },
28274     
28275     list : function()
28276     {
28277         if(this.isSubMenu){
28278             return this.el;
28279         }
28280         
28281         return this.el.select('ul.dropdown-menu', true).first();
28282     },
28283     
28284     onClick : function(e)
28285     {
28286         this.fireEvent("click", this, e);
28287     },
28288     
28289     onTriggerPress  : function(e)
28290     {   
28291         if (this.isVisible()) {
28292             this.hide();
28293         } else {
28294             this.show();
28295         }
28296     },
28297     
28298     isVisible : function(){
28299         return !this.hidden;
28300     },
28301     
28302     show : function()
28303     {
28304         this.fireEvent("beforeshow", this);
28305         
28306         this.hidden = false;
28307         this.el.addClass('open');
28308         
28309         Roo.get(document).on("mouseup", this.onMouseUp, this);
28310         
28311         this.fireEvent("show", this);
28312         
28313         
28314     },
28315     
28316     hide : function()
28317     {
28318         this.fireEvent("beforehide", this);
28319         
28320         this.hidden = true;
28321         this.el.removeClass('open');
28322         
28323         Roo.get(document).un("mouseup", this.onMouseUp);
28324         
28325         this.fireEvent("hide", this);
28326     },
28327     
28328     onMouseUp : function()
28329     {
28330         this.hide();
28331     }
28332     
28333 });
28334
28335  
28336  /*
28337  * - LGPL
28338  *
28339  * menu item
28340  * 
28341  */
28342 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28343
28344 /**
28345  * @class Roo.bootstrap.menu.Item
28346  * @extends Roo.bootstrap.Component
28347  * Bootstrap MenuItem class
28348  * @cfg {Boolean} submenu (true | false) default false
28349  * @cfg {String} html text of the item
28350  * @cfg {String} href the link
28351  * @cfg {Boolean} disable (true | false) default false
28352  * @cfg {Boolean} preventDefault (true | false) default true
28353  * @cfg {String} icon Font awesome icon
28354  * @cfg {String} pos Submenu align to (left | right) default right 
28355  * 
28356  * 
28357  * @constructor
28358  * Create a new Item
28359  * @param {Object} config The config object
28360  */
28361
28362
28363 Roo.bootstrap.menu.Item = function(config){
28364     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28365     this.addEvents({
28366         /**
28367          * @event mouseover
28368          * Fires when the mouse is hovering over this menu
28369          * @param {Roo.bootstrap.menu.Item} this
28370          * @param {Roo.EventObject} e
28371          */
28372         mouseover : true,
28373         /**
28374          * @event mouseout
28375          * Fires when the mouse exits this menu
28376          * @param {Roo.bootstrap.menu.Item} this
28377          * @param {Roo.EventObject} e
28378          */
28379         mouseout : true,
28380         // raw events
28381         /**
28382          * @event click
28383          * The raw click event for the entire grid.
28384          * @param {Roo.EventObject} e
28385          */
28386         click : true
28387     });
28388 };
28389
28390 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28391     
28392     submenu : false,
28393     href : '',
28394     html : '',
28395     preventDefault: true,
28396     disable : false,
28397     icon : false,
28398     pos : 'right',
28399     
28400     getAutoCreate : function()
28401     {
28402         var text = [
28403             {
28404                 tag : 'span',
28405                 cls : 'roo-menu-item-text',
28406                 html : this.html
28407             }
28408         ];
28409         
28410         if(this.icon){
28411             text.unshift({
28412                 tag : 'i',
28413                 cls : 'fa ' + this.icon
28414             })
28415         }
28416         
28417         var cfg = {
28418             tag : 'li',
28419             cn : [
28420                 {
28421                     tag : 'a',
28422                     href : this.href || '#',
28423                     cn : text
28424                 }
28425             ]
28426         };
28427         
28428         if(this.disable){
28429             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28430         }
28431         
28432         if(this.submenu){
28433             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28434             
28435             if(this.pos == 'left'){
28436                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28437             }
28438         }
28439         
28440         return cfg;
28441     },
28442     
28443     initEvents : function() 
28444     {
28445         this.el.on('mouseover', this.onMouseOver, this);
28446         this.el.on('mouseout', this.onMouseOut, this);
28447         
28448         this.el.select('a', true).first().on('click', this.onClick, this);
28449         
28450     },
28451     
28452     onClick : function(e)
28453     {
28454         if(this.preventDefault){
28455             e.preventDefault();
28456         }
28457         
28458         this.fireEvent("click", this, e);
28459     },
28460     
28461     onMouseOver : function(e)
28462     {
28463         if(this.submenu && this.pos == 'left'){
28464             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28465         }
28466         
28467         this.fireEvent("mouseover", this, e);
28468     },
28469     
28470     onMouseOut : function(e)
28471     {
28472         this.fireEvent("mouseout", this, e);
28473     }
28474 });
28475
28476  
28477
28478  /*
28479  * - LGPL
28480  *
28481  * menu separator
28482  * 
28483  */
28484 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28485
28486 /**
28487  * @class Roo.bootstrap.menu.Separator
28488  * @extends Roo.bootstrap.Component
28489  * Bootstrap Separator class
28490  * 
28491  * @constructor
28492  * Create a new Separator
28493  * @param {Object} config The config object
28494  */
28495
28496
28497 Roo.bootstrap.menu.Separator = function(config){
28498     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28499 };
28500
28501 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28502     
28503     getAutoCreate : function(){
28504         var cfg = {
28505             tag : 'li',
28506             cls: 'divider'
28507         };
28508         
28509         return cfg;
28510     }
28511    
28512 });
28513
28514  
28515
28516  /*
28517  * - LGPL
28518  *
28519  * Tooltip
28520  * 
28521  */
28522
28523 /**
28524  * @class Roo.bootstrap.Tooltip
28525  * Bootstrap Tooltip class
28526  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28527  * to determine which dom element triggers the tooltip.
28528  * 
28529  * It needs to add support for additional attributes like tooltip-position
28530  * 
28531  * @constructor
28532  * Create a new Toolti
28533  * @param {Object} config The config object
28534  */
28535
28536 Roo.bootstrap.Tooltip = function(config){
28537     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28538     
28539     this.alignment = Roo.bootstrap.Tooltip.alignment;
28540     
28541     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28542         this.alignment = config.alignment;
28543     }
28544     
28545 };
28546
28547 Roo.apply(Roo.bootstrap.Tooltip, {
28548     /**
28549      * @function init initialize tooltip monitoring.
28550      * @static
28551      */
28552     currentEl : false,
28553     currentTip : false,
28554     currentRegion : false,
28555     
28556     //  init : delay?
28557     
28558     init : function()
28559     {
28560         Roo.get(document).on('mouseover', this.enter ,this);
28561         Roo.get(document).on('mouseout', this.leave, this);
28562          
28563         
28564         this.currentTip = new Roo.bootstrap.Tooltip();
28565     },
28566     
28567     enter : function(ev)
28568     {
28569         var dom = ev.getTarget();
28570         
28571         //Roo.log(['enter',dom]);
28572         var el = Roo.fly(dom);
28573         if (this.currentEl) {
28574             //Roo.log(dom);
28575             //Roo.log(this.currentEl);
28576             //Roo.log(this.currentEl.contains(dom));
28577             if (this.currentEl == el) {
28578                 return;
28579             }
28580             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28581                 return;
28582             }
28583
28584         }
28585         
28586         if (this.currentTip.el) {
28587             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28588         }    
28589         //Roo.log(ev);
28590         
28591         if(!el || el.dom == document){
28592             return;
28593         }
28594         
28595         var bindEl = el;
28596         
28597         // you can not look for children, as if el is the body.. then everythign is the child..
28598         if (!el.attr('tooltip')) { //
28599             if (!el.select("[tooltip]").elements.length) {
28600                 return;
28601             }
28602             // is the mouse over this child...?
28603             bindEl = el.select("[tooltip]").first();
28604             var xy = ev.getXY();
28605             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28606                 //Roo.log("not in region.");
28607                 return;
28608             }
28609             //Roo.log("child element over..");
28610             
28611         }
28612         this.currentEl = bindEl;
28613         this.currentTip.bind(bindEl);
28614         this.currentRegion = Roo.lib.Region.getRegion(dom);
28615         this.currentTip.enter();
28616         
28617     },
28618     leave : function(ev)
28619     {
28620         var dom = ev.getTarget();
28621         //Roo.log(['leave',dom]);
28622         if (!this.currentEl) {
28623             return;
28624         }
28625         
28626         
28627         if (dom != this.currentEl.dom) {
28628             return;
28629         }
28630         var xy = ev.getXY();
28631         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28632             return;
28633         }
28634         // only activate leave if mouse cursor is outside... bounding box..
28635         
28636         
28637         
28638         
28639         if (this.currentTip) {
28640             this.currentTip.leave();
28641         }
28642         //Roo.log('clear currentEl');
28643         this.currentEl = false;
28644         
28645         
28646     },
28647     alignment : {
28648         'left' : ['r-l', [-2,0], 'right'],
28649         'right' : ['l-r', [2,0], 'left'],
28650         'bottom' : ['t-b', [0,2], 'top'],
28651         'top' : [ 'b-t', [0,-2], 'bottom']
28652     }
28653     
28654 });
28655
28656
28657 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28658     
28659     
28660     bindEl : false,
28661     
28662     delay : null, // can be { show : 300 , hide: 500}
28663     
28664     timeout : null,
28665     
28666     hoverState : null, //???
28667     
28668     placement : 'bottom', 
28669     
28670     alignment : false,
28671     
28672     getAutoCreate : function(){
28673     
28674         var cfg = {
28675            cls : 'tooltip',   
28676            role : 'tooltip',
28677            cn : [
28678                 {
28679                     cls : 'tooltip-arrow arrow'
28680                 },
28681                 {
28682                     cls : 'tooltip-inner'
28683                 }
28684            ]
28685         };
28686         
28687         return cfg;
28688     },
28689     bind : function(el)
28690     {
28691         this.bindEl = el;
28692     },
28693     
28694     initEvents : function()
28695     {
28696         this.arrowEl = this.el.select('.arrow', true).first();
28697         this.innerEl = this.el.select('.tooltip-inner', true).first();
28698     },
28699     
28700     enter : function () {
28701        
28702         if (this.timeout != null) {
28703             clearTimeout(this.timeout);
28704         }
28705         
28706         this.hoverState = 'in';
28707          //Roo.log("enter - show");
28708         if (!this.delay || !this.delay.show) {
28709             this.show();
28710             return;
28711         }
28712         var _t = this;
28713         this.timeout = setTimeout(function () {
28714             if (_t.hoverState == 'in') {
28715                 _t.show();
28716             }
28717         }, this.delay.show);
28718     },
28719     leave : function()
28720     {
28721         clearTimeout(this.timeout);
28722     
28723         this.hoverState = 'out';
28724          if (!this.delay || !this.delay.hide) {
28725             this.hide();
28726             return;
28727         }
28728        
28729         var _t = this;
28730         this.timeout = setTimeout(function () {
28731             //Roo.log("leave - timeout");
28732             
28733             if (_t.hoverState == 'out') {
28734                 _t.hide();
28735                 Roo.bootstrap.Tooltip.currentEl = false;
28736             }
28737         }, delay);
28738     },
28739     
28740     show : function (msg)
28741     {
28742         if (!this.el) {
28743             this.render(document.body);
28744         }
28745         // set content.
28746         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28747         
28748         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28749         
28750         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28751         
28752         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28753                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28754         
28755         var placement = typeof this.placement == 'function' ?
28756             this.placement.call(this, this.el, on_el) :
28757             this.placement;
28758             
28759         var autoToken = /\s?auto?\s?/i;
28760         var autoPlace = autoToken.test(placement);
28761         if (autoPlace) {
28762             placement = placement.replace(autoToken, '') || 'top';
28763         }
28764         
28765         //this.el.detach()
28766         //this.el.setXY([0,0]);
28767         this.el.show();
28768         //this.el.dom.style.display='block';
28769         
28770         //this.el.appendTo(on_el);
28771         
28772         var p = this.getPosition();
28773         var box = this.el.getBox();
28774         
28775         if (autoPlace) {
28776             // fixme..
28777         }
28778         
28779         var align = this.alignment[placement];
28780         
28781         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28782         
28783         if(placement == 'top' || placement == 'bottom'){
28784             if(xy[0] < 0){
28785                 placement = 'right';
28786             }
28787             
28788             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28789                 placement = 'left';
28790             }
28791             
28792             var scroll = Roo.select('body', true).first().getScroll();
28793             
28794             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28795                 placement = 'top';
28796             }
28797             
28798             align = this.alignment[placement];
28799             
28800             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28801             
28802         }
28803         
28804         this.el.alignTo(this.bindEl, align[0],align[1]);
28805         //var arrow = this.el.select('.arrow',true).first();
28806         //arrow.set(align[2], 
28807         
28808         this.el.addClass(placement);
28809         this.el.addClass("bs-tooltip-"+ placement);
28810         
28811         this.el.addClass('in fade show');
28812         
28813         this.hoverState = null;
28814         
28815         if (this.el.hasClass('fade')) {
28816             // fade it?
28817         }
28818         
28819         
28820         
28821         
28822         
28823     },
28824     hide : function()
28825     {
28826          
28827         if (!this.el) {
28828             return;
28829         }
28830         //this.el.setXY([0,0]);
28831         this.el.removeClass(['show', 'in']);
28832         //this.el.hide();
28833         
28834     }
28835     
28836 });
28837  
28838
28839  /*
28840  * - LGPL
28841  *
28842  * Location Picker
28843  * 
28844  */
28845
28846 /**
28847  * @class Roo.bootstrap.LocationPicker
28848  * @extends Roo.bootstrap.Component
28849  * Bootstrap LocationPicker class
28850  * @cfg {Number} latitude Position when init default 0
28851  * @cfg {Number} longitude Position when init default 0
28852  * @cfg {Number} zoom default 15
28853  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28854  * @cfg {Boolean} mapTypeControl default false
28855  * @cfg {Boolean} disableDoubleClickZoom default false
28856  * @cfg {Boolean} scrollwheel default true
28857  * @cfg {Boolean} streetViewControl default false
28858  * @cfg {Number} radius default 0
28859  * @cfg {String} locationName
28860  * @cfg {Boolean} draggable default true
28861  * @cfg {Boolean} enableAutocomplete default false
28862  * @cfg {Boolean} enableReverseGeocode default true
28863  * @cfg {String} markerTitle
28864  * 
28865  * @constructor
28866  * Create a new LocationPicker
28867  * @param {Object} config The config object
28868  */
28869
28870
28871 Roo.bootstrap.LocationPicker = function(config){
28872     
28873     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28874     
28875     this.addEvents({
28876         /**
28877          * @event initial
28878          * Fires when the picker initialized.
28879          * @param {Roo.bootstrap.LocationPicker} this
28880          * @param {Google Location} location
28881          */
28882         initial : true,
28883         /**
28884          * @event positionchanged
28885          * Fires when the picker position changed.
28886          * @param {Roo.bootstrap.LocationPicker} this
28887          * @param {Google Location} location
28888          */
28889         positionchanged : true,
28890         /**
28891          * @event resize
28892          * Fires when the map resize.
28893          * @param {Roo.bootstrap.LocationPicker} this
28894          */
28895         resize : true,
28896         /**
28897          * @event show
28898          * Fires when the map show.
28899          * @param {Roo.bootstrap.LocationPicker} this
28900          */
28901         show : true,
28902         /**
28903          * @event hide
28904          * Fires when the map hide.
28905          * @param {Roo.bootstrap.LocationPicker} this
28906          */
28907         hide : true,
28908         /**
28909          * @event mapClick
28910          * Fires when click the map.
28911          * @param {Roo.bootstrap.LocationPicker} this
28912          * @param {Map event} e
28913          */
28914         mapClick : true,
28915         /**
28916          * @event mapRightClick
28917          * Fires when right click the map.
28918          * @param {Roo.bootstrap.LocationPicker} this
28919          * @param {Map event} e
28920          */
28921         mapRightClick : true,
28922         /**
28923          * @event markerClick
28924          * Fires when click the marker.
28925          * @param {Roo.bootstrap.LocationPicker} this
28926          * @param {Map event} e
28927          */
28928         markerClick : true,
28929         /**
28930          * @event markerRightClick
28931          * Fires when right click the marker.
28932          * @param {Roo.bootstrap.LocationPicker} this
28933          * @param {Map event} e
28934          */
28935         markerRightClick : true,
28936         /**
28937          * @event OverlayViewDraw
28938          * Fires when OverlayView Draw
28939          * @param {Roo.bootstrap.LocationPicker} this
28940          */
28941         OverlayViewDraw : true,
28942         /**
28943          * @event OverlayViewOnAdd
28944          * Fires when OverlayView Draw
28945          * @param {Roo.bootstrap.LocationPicker} this
28946          */
28947         OverlayViewOnAdd : true,
28948         /**
28949          * @event OverlayViewOnRemove
28950          * Fires when OverlayView Draw
28951          * @param {Roo.bootstrap.LocationPicker} this
28952          */
28953         OverlayViewOnRemove : true,
28954         /**
28955          * @event OverlayViewShow
28956          * Fires when OverlayView Draw
28957          * @param {Roo.bootstrap.LocationPicker} this
28958          * @param {Pixel} cpx
28959          */
28960         OverlayViewShow : true,
28961         /**
28962          * @event OverlayViewHide
28963          * Fires when OverlayView Draw
28964          * @param {Roo.bootstrap.LocationPicker} this
28965          */
28966         OverlayViewHide : true,
28967         /**
28968          * @event loadexception
28969          * Fires when load google lib failed.
28970          * @param {Roo.bootstrap.LocationPicker} this
28971          */
28972         loadexception : true
28973     });
28974         
28975 };
28976
28977 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28978     
28979     gMapContext: false,
28980     
28981     latitude: 0,
28982     longitude: 0,
28983     zoom: 15,
28984     mapTypeId: false,
28985     mapTypeControl: false,
28986     disableDoubleClickZoom: false,
28987     scrollwheel: true,
28988     streetViewControl: false,
28989     radius: 0,
28990     locationName: '',
28991     draggable: true,
28992     enableAutocomplete: false,
28993     enableReverseGeocode: true,
28994     markerTitle: '',
28995     
28996     getAutoCreate: function()
28997     {
28998
28999         var cfg = {
29000             tag: 'div',
29001             cls: 'roo-location-picker'
29002         };
29003         
29004         return cfg
29005     },
29006     
29007     initEvents: function(ct, position)
29008     {       
29009         if(!this.el.getWidth() || this.isApplied()){
29010             return;
29011         }
29012         
29013         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29014         
29015         this.initial();
29016     },
29017     
29018     initial: function()
29019     {
29020         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29021             this.fireEvent('loadexception', this);
29022             return;
29023         }
29024         
29025         if(!this.mapTypeId){
29026             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29027         }
29028         
29029         this.gMapContext = this.GMapContext();
29030         
29031         this.initOverlayView();
29032         
29033         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29034         
29035         var _this = this;
29036                 
29037         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29038             _this.setPosition(_this.gMapContext.marker.position);
29039         });
29040         
29041         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29042             _this.fireEvent('mapClick', this, event);
29043             
29044         });
29045
29046         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29047             _this.fireEvent('mapRightClick', this, event);
29048             
29049         });
29050         
29051         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29052             _this.fireEvent('markerClick', this, event);
29053             
29054         });
29055
29056         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29057             _this.fireEvent('markerRightClick', this, event);
29058             
29059         });
29060         
29061         this.setPosition(this.gMapContext.location);
29062         
29063         this.fireEvent('initial', this, this.gMapContext.location);
29064     },
29065     
29066     initOverlayView: function()
29067     {
29068         var _this = this;
29069         
29070         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29071             
29072             draw: function()
29073             {
29074                 _this.fireEvent('OverlayViewDraw', _this);
29075             },
29076             
29077             onAdd: function()
29078             {
29079                 _this.fireEvent('OverlayViewOnAdd', _this);
29080             },
29081             
29082             onRemove: function()
29083             {
29084                 _this.fireEvent('OverlayViewOnRemove', _this);
29085             },
29086             
29087             show: function(cpx)
29088             {
29089                 _this.fireEvent('OverlayViewShow', _this, cpx);
29090             },
29091             
29092             hide: function()
29093             {
29094                 _this.fireEvent('OverlayViewHide', _this);
29095             }
29096             
29097         });
29098     },
29099     
29100     fromLatLngToContainerPixel: function(event)
29101     {
29102         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29103     },
29104     
29105     isApplied: function() 
29106     {
29107         return this.getGmapContext() == false ? false : true;
29108     },
29109     
29110     getGmapContext: function() 
29111     {
29112         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29113     },
29114     
29115     GMapContext: function() 
29116     {
29117         var position = new google.maps.LatLng(this.latitude, this.longitude);
29118         
29119         var _map = new google.maps.Map(this.el.dom, {
29120             center: position,
29121             zoom: this.zoom,
29122             mapTypeId: this.mapTypeId,
29123             mapTypeControl: this.mapTypeControl,
29124             disableDoubleClickZoom: this.disableDoubleClickZoom,
29125             scrollwheel: this.scrollwheel,
29126             streetViewControl: this.streetViewControl,
29127             locationName: this.locationName,
29128             draggable: this.draggable,
29129             enableAutocomplete: this.enableAutocomplete,
29130             enableReverseGeocode: this.enableReverseGeocode
29131         });
29132         
29133         var _marker = new google.maps.Marker({
29134             position: position,
29135             map: _map,
29136             title: this.markerTitle,
29137             draggable: this.draggable
29138         });
29139         
29140         return {
29141             map: _map,
29142             marker: _marker,
29143             circle: null,
29144             location: position,
29145             radius: this.radius,
29146             locationName: this.locationName,
29147             addressComponents: {
29148                 formatted_address: null,
29149                 addressLine1: null,
29150                 addressLine2: null,
29151                 streetName: null,
29152                 streetNumber: null,
29153                 city: null,
29154                 district: null,
29155                 state: null,
29156                 stateOrProvince: null
29157             },
29158             settings: this,
29159             domContainer: this.el.dom,
29160             geodecoder: new google.maps.Geocoder()
29161         };
29162     },
29163     
29164     drawCircle: function(center, radius, options) 
29165     {
29166         if (this.gMapContext.circle != null) {
29167             this.gMapContext.circle.setMap(null);
29168         }
29169         if (radius > 0) {
29170             radius *= 1;
29171             options = Roo.apply({}, options, {
29172                 strokeColor: "#0000FF",
29173                 strokeOpacity: .35,
29174                 strokeWeight: 2,
29175                 fillColor: "#0000FF",
29176                 fillOpacity: .2
29177             });
29178             
29179             options.map = this.gMapContext.map;
29180             options.radius = radius;
29181             options.center = center;
29182             this.gMapContext.circle = new google.maps.Circle(options);
29183             return this.gMapContext.circle;
29184         }
29185         
29186         return null;
29187     },
29188     
29189     setPosition: function(location) 
29190     {
29191         this.gMapContext.location = location;
29192         this.gMapContext.marker.setPosition(location);
29193         this.gMapContext.map.panTo(location);
29194         this.drawCircle(location, this.gMapContext.radius, {});
29195         
29196         var _this = this;
29197         
29198         if (this.gMapContext.settings.enableReverseGeocode) {
29199             this.gMapContext.geodecoder.geocode({
29200                 latLng: this.gMapContext.location
29201             }, function(results, status) {
29202                 
29203                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29204                     _this.gMapContext.locationName = results[0].formatted_address;
29205                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29206                     
29207                     _this.fireEvent('positionchanged', this, location);
29208                 }
29209             });
29210             
29211             return;
29212         }
29213         
29214         this.fireEvent('positionchanged', this, location);
29215     },
29216     
29217     resize: function()
29218     {
29219         google.maps.event.trigger(this.gMapContext.map, "resize");
29220         
29221         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29222         
29223         this.fireEvent('resize', this);
29224     },
29225     
29226     setPositionByLatLng: function(latitude, longitude)
29227     {
29228         this.setPosition(new google.maps.LatLng(latitude, longitude));
29229     },
29230     
29231     getCurrentPosition: function() 
29232     {
29233         return {
29234             latitude: this.gMapContext.location.lat(),
29235             longitude: this.gMapContext.location.lng()
29236         };
29237     },
29238     
29239     getAddressName: function() 
29240     {
29241         return this.gMapContext.locationName;
29242     },
29243     
29244     getAddressComponents: function() 
29245     {
29246         return this.gMapContext.addressComponents;
29247     },
29248     
29249     address_component_from_google_geocode: function(address_components) 
29250     {
29251         var result = {};
29252         
29253         for (var i = 0; i < address_components.length; i++) {
29254             var component = address_components[i];
29255             if (component.types.indexOf("postal_code") >= 0) {
29256                 result.postalCode = component.short_name;
29257             } else if (component.types.indexOf("street_number") >= 0) {
29258                 result.streetNumber = component.short_name;
29259             } else if (component.types.indexOf("route") >= 0) {
29260                 result.streetName = component.short_name;
29261             } else if (component.types.indexOf("neighborhood") >= 0) {
29262                 result.city = component.short_name;
29263             } else if (component.types.indexOf("locality") >= 0) {
29264                 result.city = component.short_name;
29265             } else if (component.types.indexOf("sublocality") >= 0) {
29266                 result.district = component.short_name;
29267             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29268                 result.stateOrProvince = component.short_name;
29269             } else if (component.types.indexOf("country") >= 0) {
29270                 result.country = component.short_name;
29271             }
29272         }
29273         
29274         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29275         result.addressLine2 = "";
29276         return result;
29277     },
29278     
29279     setZoomLevel: function(zoom)
29280     {
29281         this.gMapContext.map.setZoom(zoom);
29282     },
29283     
29284     show: function()
29285     {
29286         if(!this.el){
29287             return;
29288         }
29289         
29290         this.el.show();
29291         
29292         this.resize();
29293         
29294         this.fireEvent('show', this);
29295     },
29296     
29297     hide: function()
29298     {
29299         if(!this.el){
29300             return;
29301         }
29302         
29303         this.el.hide();
29304         
29305         this.fireEvent('hide', this);
29306     }
29307     
29308 });
29309
29310 Roo.apply(Roo.bootstrap.LocationPicker, {
29311     
29312     OverlayView : function(map, options)
29313     {
29314         options = options || {};
29315         
29316         this.setMap(map);
29317     }
29318     
29319     
29320 });/**
29321  * @class Roo.bootstrap.Alert
29322  * @extends Roo.bootstrap.Component
29323  * Bootstrap Alert class - shows an alert area box
29324  * eg
29325  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29326   Enter a valid email address
29327 </div>
29328  * @licence LGPL
29329  * @cfg {String} title The title of alert
29330  * @cfg {String} html The content of alert
29331  * @cfg {String} weight (  success | info | warning | danger )
29332  * @cfg {String} faicon font-awesomeicon
29333  * 
29334  * @constructor
29335  * Create a new alert
29336  * @param {Object} config The config object
29337  */
29338
29339
29340 Roo.bootstrap.Alert = function(config){
29341     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29342     
29343 };
29344
29345 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29346     
29347     title: '',
29348     html: '',
29349     weight: false,
29350     faicon: false,
29351     
29352     getAutoCreate : function()
29353     {
29354         
29355         var cfg = {
29356             tag : 'div',
29357             cls : 'alert',
29358             cn : [
29359                 {
29360                     tag : 'i',
29361                     cls : 'roo-alert-icon'
29362                     
29363                 },
29364                 {
29365                     tag : 'b',
29366                     cls : 'roo-alert-title',
29367                     html : this.title
29368                 },
29369                 {
29370                     tag : 'span',
29371                     cls : 'roo-alert-text',
29372                     html : this.html
29373                 }
29374             ]
29375         };
29376         
29377         if(this.faicon){
29378             cfg.cn[0].cls += ' fa ' + this.faicon;
29379         }
29380         
29381         if(this.weight){
29382             cfg.cls += ' alert-' + this.weight;
29383         }
29384         
29385         return cfg;
29386     },
29387     
29388     initEvents: function() 
29389     {
29390         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29391     },
29392     
29393     setTitle : function(str)
29394     {
29395         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29396     },
29397     
29398     setText : function(str)
29399     {
29400         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29401     },
29402     
29403     setWeight : function(weight)
29404     {
29405         if(this.weight){
29406             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29407         }
29408         
29409         this.weight = weight;
29410         
29411         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29412     },
29413     
29414     setIcon : function(icon)
29415     {
29416         if(this.faicon){
29417             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29418         }
29419         
29420         this.faicon = icon;
29421         
29422         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29423     },
29424     
29425     hide: function() 
29426     {
29427         this.el.hide();   
29428     },
29429     
29430     show: function() 
29431     {  
29432         this.el.show();   
29433     }
29434     
29435 });
29436
29437  
29438 /*
29439 * Licence: LGPL
29440 */
29441
29442 /**
29443  * @class Roo.bootstrap.UploadCropbox
29444  * @extends Roo.bootstrap.Component
29445  * Bootstrap UploadCropbox class
29446  * @cfg {String} emptyText show when image has been loaded
29447  * @cfg {String} rotateNotify show when image too small to rotate
29448  * @cfg {Number} errorTimeout default 3000
29449  * @cfg {Number} minWidth default 300
29450  * @cfg {Number} minHeight default 300
29451  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29452  * @cfg {Boolean} isDocument (true|false) default false
29453  * @cfg {String} url action url
29454  * @cfg {String} paramName default 'imageUpload'
29455  * @cfg {String} method default POST
29456  * @cfg {Boolean} loadMask (true|false) default true
29457  * @cfg {Boolean} loadingText default 'Loading...'
29458  * 
29459  * @constructor
29460  * Create a new UploadCropbox
29461  * @param {Object} config The config object
29462  */
29463
29464 Roo.bootstrap.UploadCropbox = function(config){
29465     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29466     
29467     this.addEvents({
29468         /**
29469          * @event beforeselectfile
29470          * Fire before select file
29471          * @param {Roo.bootstrap.UploadCropbox} this
29472          */
29473         "beforeselectfile" : true,
29474         /**
29475          * @event initial
29476          * Fire after initEvent
29477          * @param {Roo.bootstrap.UploadCropbox} this
29478          */
29479         "initial" : true,
29480         /**
29481          * @event crop
29482          * Fire after initEvent
29483          * @param {Roo.bootstrap.UploadCropbox} this
29484          * @param {String} data
29485          */
29486         "crop" : true,
29487         /**
29488          * @event prepare
29489          * Fire when preparing the file data
29490          * @param {Roo.bootstrap.UploadCropbox} this
29491          * @param {Object} file
29492          */
29493         "prepare" : true,
29494         /**
29495          * @event exception
29496          * Fire when get exception
29497          * @param {Roo.bootstrap.UploadCropbox} this
29498          * @param {XMLHttpRequest} xhr
29499          */
29500         "exception" : true,
29501         /**
29502          * @event beforeloadcanvas
29503          * Fire before load the canvas
29504          * @param {Roo.bootstrap.UploadCropbox} this
29505          * @param {String} src
29506          */
29507         "beforeloadcanvas" : true,
29508         /**
29509          * @event trash
29510          * Fire when trash image
29511          * @param {Roo.bootstrap.UploadCropbox} this
29512          */
29513         "trash" : true,
29514         /**
29515          * @event download
29516          * Fire when download the image
29517          * @param {Roo.bootstrap.UploadCropbox} this
29518          */
29519         "download" : true,
29520         /**
29521          * @event footerbuttonclick
29522          * Fire when footerbuttonclick
29523          * @param {Roo.bootstrap.UploadCropbox} this
29524          * @param {String} type
29525          */
29526         "footerbuttonclick" : true,
29527         /**
29528          * @event resize
29529          * Fire when resize
29530          * @param {Roo.bootstrap.UploadCropbox} this
29531          */
29532         "resize" : true,
29533         /**
29534          * @event rotate
29535          * Fire when rotate the image
29536          * @param {Roo.bootstrap.UploadCropbox} this
29537          * @param {String} pos
29538          */
29539         "rotate" : true,
29540         /**
29541          * @event inspect
29542          * Fire when inspect the file
29543          * @param {Roo.bootstrap.UploadCropbox} this
29544          * @param {Object} file
29545          */
29546         "inspect" : true,
29547         /**
29548          * @event upload
29549          * Fire when xhr upload the file
29550          * @param {Roo.bootstrap.UploadCropbox} this
29551          * @param {Object} data
29552          */
29553         "upload" : true,
29554         /**
29555          * @event arrange
29556          * Fire when arrange the file data
29557          * @param {Roo.bootstrap.UploadCropbox} this
29558          * @param {Object} formData
29559          */
29560         "arrange" : true
29561     });
29562     
29563     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29564 };
29565
29566 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29567     
29568     emptyText : 'Click to upload image',
29569     rotateNotify : 'Image is too small to rotate',
29570     errorTimeout : 3000,
29571     scale : 0,
29572     baseScale : 1,
29573     rotate : 0,
29574     dragable : false,
29575     pinching : false,
29576     mouseX : 0,
29577     mouseY : 0,
29578     cropData : false,
29579     minWidth : 300,
29580     minHeight : 300,
29581     file : false,
29582     exif : {},
29583     baseRotate : 1,
29584     cropType : 'image/jpeg',
29585     buttons : false,
29586     canvasLoaded : false,
29587     isDocument : false,
29588     method : 'POST',
29589     paramName : 'imageUpload',
29590     loadMask : true,
29591     loadingText : 'Loading...',
29592     maskEl : false,
29593     
29594     getAutoCreate : function()
29595     {
29596         var cfg = {
29597             tag : 'div',
29598             cls : 'roo-upload-cropbox',
29599             cn : [
29600                 {
29601                     tag : 'input',
29602                     cls : 'roo-upload-cropbox-selector',
29603                     type : 'file'
29604                 },
29605                 {
29606                     tag : 'div',
29607                     cls : 'roo-upload-cropbox-body',
29608                     style : 'cursor:pointer',
29609                     cn : [
29610                         {
29611                             tag : 'div',
29612                             cls : 'roo-upload-cropbox-preview'
29613                         },
29614                         {
29615                             tag : 'div',
29616                             cls : 'roo-upload-cropbox-thumb'
29617                         },
29618                         {
29619                             tag : 'div',
29620                             cls : 'roo-upload-cropbox-empty-notify',
29621                             html : this.emptyText
29622                         },
29623                         {
29624                             tag : 'div',
29625                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29626                             html : this.rotateNotify
29627                         }
29628                     ]
29629                 },
29630                 {
29631                     tag : 'div',
29632                     cls : 'roo-upload-cropbox-footer',
29633                     cn : {
29634                         tag : 'div',
29635                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29636                         cn : []
29637                     }
29638                 }
29639             ]
29640         };
29641         
29642         return cfg;
29643     },
29644     
29645     onRender : function(ct, position)
29646     {
29647         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29648         
29649         if (this.buttons.length) {
29650             
29651             Roo.each(this.buttons, function(bb) {
29652                 
29653                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29654                 
29655                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29656                 
29657             }, this);
29658         }
29659         
29660         if(this.loadMask){
29661             this.maskEl = this.el;
29662         }
29663     },
29664     
29665     initEvents : function()
29666     {
29667         this.urlAPI = (window.createObjectURL && window) || 
29668                                 (window.URL && URL.revokeObjectURL && URL) || 
29669                                 (window.webkitURL && webkitURL);
29670                         
29671         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29672         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29673         
29674         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29675         this.selectorEl.hide();
29676         
29677         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29678         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29679         
29680         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29681         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29682         this.thumbEl.hide();
29683         
29684         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29685         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29686         
29687         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29688         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29689         this.errorEl.hide();
29690         
29691         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29692         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29693         this.footerEl.hide();
29694         
29695         this.setThumbBoxSize();
29696         
29697         this.bind();
29698         
29699         this.resize();
29700         
29701         this.fireEvent('initial', this);
29702     },
29703
29704     bind : function()
29705     {
29706         var _this = this;
29707         
29708         window.addEventListener("resize", function() { _this.resize(); } );
29709         
29710         this.bodyEl.on('click', this.beforeSelectFile, this);
29711         
29712         if(Roo.isTouch){
29713             this.bodyEl.on('touchstart', this.onTouchStart, this);
29714             this.bodyEl.on('touchmove', this.onTouchMove, this);
29715             this.bodyEl.on('touchend', this.onTouchEnd, this);
29716         }
29717         
29718         if(!Roo.isTouch){
29719             this.bodyEl.on('mousedown', this.onMouseDown, this);
29720             this.bodyEl.on('mousemove', this.onMouseMove, this);
29721             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29722             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29723             Roo.get(document).on('mouseup', this.onMouseUp, this);
29724         }
29725         
29726         this.selectorEl.on('change', this.onFileSelected, this);
29727     },
29728     
29729     reset : function()
29730     {    
29731         this.scale = 0;
29732         this.baseScale = 1;
29733         this.rotate = 0;
29734         this.baseRotate = 1;
29735         this.dragable = false;
29736         this.pinching = false;
29737         this.mouseX = 0;
29738         this.mouseY = 0;
29739         this.cropData = false;
29740         this.notifyEl.dom.innerHTML = this.emptyText;
29741         
29742         this.selectorEl.dom.value = '';
29743         
29744     },
29745     
29746     resize : function()
29747     {
29748         if(this.fireEvent('resize', this) != false){
29749             this.setThumbBoxPosition();
29750             this.setCanvasPosition();
29751         }
29752     },
29753     
29754     onFooterButtonClick : function(e, el, o, type)
29755     {
29756         switch (type) {
29757             case 'rotate-left' :
29758                 this.onRotateLeft(e);
29759                 break;
29760             case 'rotate-right' :
29761                 this.onRotateRight(e);
29762                 break;
29763             case 'picture' :
29764                 this.beforeSelectFile(e);
29765                 break;
29766             case 'trash' :
29767                 this.trash(e);
29768                 break;
29769             case 'crop' :
29770                 this.crop(e);
29771                 break;
29772             case 'download' :
29773                 this.download(e);
29774                 break;
29775             default :
29776                 break;
29777         }
29778         
29779         this.fireEvent('footerbuttonclick', this, type);
29780     },
29781     
29782     beforeSelectFile : function(e)
29783     {
29784         e.preventDefault();
29785         
29786         if(this.fireEvent('beforeselectfile', this) != false){
29787             this.selectorEl.dom.click();
29788         }
29789     },
29790     
29791     onFileSelected : function(e)
29792     {
29793         e.preventDefault();
29794         
29795         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29796             return;
29797         }
29798         
29799         var file = this.selectorEl.dom.files[0];
29800         
29801         if(this.fireEvent('inspect', this, file) != false){
29802             this.prepare(file);
29803         }
29804         
29805     },
29806     
29807     trash : function(e)
29808     {
29809         this.fireEvent('trash', this);
29810     },
29811     
29812     download : function(e)
29813     {
29814         this.fireEvent('download', this);
29815     },
29816     
29817     loadCanvas : function(src)
29818     {   
29819         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29820             
29821             this.reset();
29822             
29823             this.imageEl = document.createElement('img');
29824             
29825             var _this = this;
29826             
29827             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29828             
29829             this.imageEl.src = src;
29830         }
29831     },
29832     
29833     onLoadCanvas : function()
29834     {   
29835         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29836         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29837         
29838         this.bodyEl.un('click', this.beforeSelectFile, this);
29839         
29840         this.notifyEl.hide();
29841         this.thumbEl.show();
29842         this.footerEl.show();
29843         
29844         this.baseRotateLevel();
29845         
29846         if(this.isDocument){
29847             this.setThumbBoxSize();
29848         }
29849         
29850         this.setThumbBoxPosition();
29851         
29852         this.baseScaleLevel();
29853         
29854         this.draw();
29855         
29856         this.resize();
29857         
29858         this.canvasLoaded = true;
29859         
29860         if(this.loadMask){
29861             this.maskEl.unmask();
29862         }
29863         
29864     },
29865     
29866     setCanvasPosition : function()
29867     {   
29868         if(!this.canvasEl){
29869             return;
29870         }
29871         
29872         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29873         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29874         
29875         this.previewEl.setLeft(pw);
29876         this.previewEl.setTop(ph);
29877         
29878     },
29879     
29880     onMouseDown : function(e)
29881     {   
29882         e.stopEvent();
29883         
29884         this.dragable = true;
29885         this.pinching = false;
29886         
29887         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29888             this.dragable = false;
29889             return;
29890         }
29891         
29892         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29893         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29894         
29895     },
29896     
29897     onMouseMove : function(e)
29898     {   
29899         e.stopEvent();
29900         
29901         if(!this.canvasLoaded){
29902             return;
29903         }
29904         
29905         if (!this.dragable){
29906             return;
29907         }
29908         
29909         var minX = Math.ceil(this.thumbEl.getLeft(true));
29910         var minY = Math.ceil(this.thumbEl.getTop(true));
29911         
29912         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29913         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29914         
29915         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29916         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29917         
29918         x = x - this.mouseX;
29919         y = y - this.mouseY;
29920         
29921         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29922         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29923         
29924         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29925         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29926         
29927         this.previewEl.setLeft(bgX);
29928         this.previewEl.setTop(bgY);
29929         
29930         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29931         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29932     },
29933     
29934     onMouseUp : function(e)
29935     {   
29936         e.stopEvent();
29937         
29938         this.dragable = false;
29939     },
29940     
29941     onMouseWheel : function(e)
29942     {   
29943         e.stopEvent();
29944         
29945         this.startScale = this.scale;
29946         
29947         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29948         
29949         if(!this.zoomable()){
29950             this.scale = this.startScale;
29951             return;
29952         }
29953         
29954         this.draw();
29955         
29956         return;
29957     },
29958     
29959     zoomable : function()
29960     {
29961         var minScale = this.thumbEl.getWidth() / this.minWidth;
29962         
29963         if(this.minWidth < this.minHeight){
29964             minScale = this.thumbEl.getHeight() / this.minHeight;
29965         }
29966         
29967         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29968         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29969         
29970         if(
29971                 this.isDocument &&
29972                 (this.rotate == 0 || this.rotate == 180) && 
29973                 (
29974                     width > this.imageEl.OriginWidth || 
29975                     height > this.imageEl.OriginHeight ||
29976                     (width < this.minWidth && height < this.minHeight)
29977                 )
29978         ){
29979             return false;
29980         }
29981         
29982         if(
29983                 this.isDocument &&
29984                 (this.rotate == 90 || this.rotate == 270) && 
29985                 (
29986                     width > this.imageEl.OriginWidth || 
29987                     height > this.imageEl.OriginHeight ||
29988                     (width < this.minHeight && height < this.minWidth)
29989                 )
29990         ){
29991             return false;
29992         }
29993         
29994         if(
29995                 !this.isDocument &&
29996                 (this.rotate == 0 || this.rotate == 180) && 
29997                 (
29998                     width < this.minWidth || 
29999                     width > this.imageEl.OriginWidth || 
30000                     height < this.minHeight || 
30001                     height > this.imageEl.OriginHeight
30002                 )
30003         ){
30004             return false;
30005         }
30006         
30007         if(
30008                 !this.isDocument &&
30009                 (this.rotate == 90 || this.rotate == 270) && 
30010                 (
30011                     width < this.minHeight || 
30012                     width > this.imageEl.OriginWidth || 
30013                     height < this.minWidth || 
30014                     height > this.imageEl.OriginHeight
30015                 )
30016         ){
30017             return false;
30018         }
30019         
30020         return true;
30021         
30022     },
30023     
30024     onRotateLeft : function(e)
30025     {   
30026         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30027             
30028             var minScale = this.thumbEl.getWidth() / this.minWidth;
30029             
30030             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30031             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30032             
30033             this.startScale = this.scale;
30034             
30035             while (this.getScaleLevel() < minScale){
30036             
30037                 this.scale = this.scale + 1;
30038                 
30039                 if(!this.zoomable()){
30040                     break;
30041                 }
30042                 
30043                 if(
30044                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30045                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30046                 ){
30047                     continue;
30048                 }
30049                 
30050                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30051
30052                 this.draw();
30053                 
30054                 return;
30055             }
30056             
30057             this.scale = this.startScale;
30058             
30059             this.onRotateFail();
30060             
30061             return false;
30062         }
30063         
30064         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30065
30066         if(this.isDocument){
30067             this.setThumbBoxSize();
30068             this.setThumbBoxPosition();
30069             this.setCanvasPosition();
30070         }
30071         
30072         this.draw();
30073         
30074         this.fireEvent('rotate', this, 'left');
30075         
30076     },
30077     
30078     onRotateRight : function(e)
30079     {
30080         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30081             
30082             var minScale = this.thumbEl.getWidth() / this.minWidth;
30083         
30084             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30085             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30086             
30087             this.startScale = this.scale;
30088             
30089             while (this.getScaleLevel() < minScale){
30090             
30091                 this.scale = this.scale + 1;
30092                 
30093                 if(!this.zoomable()){
30094                     break;
30095                 }
30096                 
30097                 if(
30098                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30099                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30100                 ){
30101                     continue;
30102                 }
30103                 
30104                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30105
30106                 this.draw();
30107                 
30108                 return;
30109             }
30110             
30111             this.scale = this.startScale;
30112             
30113             this.onRotateFail();
30114             
30115             return false;
30116         }
30117         
30118         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30119
30120         if(this.isDocument){
30121             this.setThumbBoxSize();
30122             this.setThumbBoxPosition();
30123             this.setCanvasPosition();
30124         }
30125         
30126         this.draw();
30127         
30128         this.fireEvent('rotate', this, 'right');
30129     },
30130     
30131     onRotateFail : function()
30132     {
30133         this.errorEl.show(true);
30134         
30135         var _this = this;
30136         
30137         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30138     },
30139     
30140     draw : function()
30141     {
30142         this.previewEl.dom.innerHTML = '';
30143         
30144         var canvasEl = document.createElement("canvas");
30145         
30146         var contextEl = canvasEl.getContext("2d");
30147         
30148         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30149         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30150         var center = this.imageEl.OriginWidth / 2;
30151         
30152         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30153             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30154             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30155             center = this.imageEl.OriginHeight / 2;
30156         }
30157         
30158         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30159         
30160         contextEl.translate(center, center);
30161         contextEl.rotate(this.rotate * Math.PI / 180);
30162
30163         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30164         
30165         this.canvasEl = document.createElement("canvas");
30166         
30167         this.contextEl = this.canvasEl.getContext("2d");
30168         
30169         switch (this.rotate) {
30170             case 0 :
30171                 
30172                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30173                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30174                 
30175                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30176                 
30177                 break;
30178             case 90 : 
30179                 
30180                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30181                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30182                 
30183                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30184                     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);
30185                     break;
30186                 }
30187                 
30188                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30189                 
30190                 break;
30191             case 180 :
30192                 
30193                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30194                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30195                 
30196                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30197                     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);
30198                     break;
30199                 }
30200                 
30201                 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);
30202                 
30203                 break;
30204             case 270 :
30205                 
30206                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30207                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30208         
30209                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30210                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30211                     break;
30212                 }
30213                 
30214                 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);
30215                 
30216                 break;
30217             default : 
30218                 break;
30219         }
30220         
30221         this.previewEl.appendChild(this.canvasEl);
30222         
30223         this.setCanvasPosition();
30224     },
30225     
30226     crop : function()
30227     {
30228         if(!this.canvasLoaded){
30229             return;
30230         }
30231         
30232         var imageCanvas = document.createElement("canvas");
30233         
30234         var imageContext = imageCanvas.getContext("2d");
30235         
30236         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30237         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30238         
30239         var center = imageCanvas.width / 2;
30240         
30241         imageContext.translate(center, center);
30242         
30243         imageContext.rotate(this.rotate * Math.PI / 180);
30244         
30245         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30246         
30247         var canvas = document.createElement("canvas");
30248         
30249         var context = canvas.getContext("2d");
30250                 
30251         canvas.width = this.minWidth;
30252         canvas.height = this.minHeight;
30253
30254         switch (this.rotate) {
30255             case 0 :
30256                 
30257                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30258                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30259                 
30260                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30261                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30262                 
30263                 var targetWidth = this.minWidth - 2 * x;
30264                 var targetHeight = this.minHeight - 2 * y;
30265                 
30266                 var scale = 1;
30267                 
30268                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30269                     scale = targetWidth / width;
30270                 }
30271                 
30272                 if(x > 0 && y == 0){
30273                     scale = targetHeight / height;
30274                 }
30275                 
30276                 if(x > 0 && y > 0){
30277                     scale = targetWidth / width;
30278                     
30279                     if(width < height){
30280                         scale = targetHeight / height;
30281                     }
30282                 }
30283                 
30284                 context.scale(scale, scale);
30285                 
30286                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30287                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30288
30289                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30290                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30291
30292                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30293                 
30294                 break;
30295             case 90 : 
30296                 
30297                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30298                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30299                 
30300                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30301                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30302                 
30303                 var targetWidth = this.minWidth - 2 * x;
30304                 var targetHeight = this.minHeight - 2 * y;
30305                 
30306                 var scale = 1;
30307                 
30308                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30309                     scale = targetWidth / width;
30310                 }
30311                 
30312                 if(x > 0 && y == 0){
30313                     scale = targetHeight / height;
30314                 }
30315                 
30316                 if(x > 0 && y > 0){
30317                     scale = targetWidth / width;
30318                     
30319                     if(width < height){
30320                         scale = targetHeight / height;
30321                     }
30322                 }
30323                 
30324                 context.scale(scale, scale);
30325                 
30326                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30327                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30328
30329                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30330                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30331                 
30332                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30333                 
30334                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30335                 
30336                 break;
30337             case 180 :
30338                 
30339                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30340                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30341                 
30342                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30343                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30344                 
30345                 var targetWidth = this.minWidth - 2 * x;
30346                 var targetHeight = this.minHeight - 2 * y;
30347                 
30348                 var scale = 1;
30349                 
30350                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30351                     scale = targetWidth / width;
30352                 }
30353                 
30354                 if(x > 0 && y == 0){
30355                     scale = targetHeight / height;
30356                 }
30357                 
30358                 if(x > 0 && y > 0){
30359                     scale = targetWidth / width;
30360                     
30361                     if(width < height){
30362                         scale = targetHeight / height;
30363                     }
30364                 }
30365                 
30366                 context.scale(scale, scale);
30367                 
30368                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30369                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30370
30371                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30372                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30373
30374                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30375                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30376                 
30377                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30378                 
30379                 break;
30380             case 270 :
30381                 
30382                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30383                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30384                 
30385                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30386                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30387                 
30388                 var targetWidth = this.minWidth - 2 * x;
30389                 var targetHeight = this.minHeight - 2 * y;
30390                 
30391                 var scale = 1;
30392                 
30393                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30394                     scale = targetWidth / width;
30395                 }
30396                 
30397                 if(x > 0 && y == 0){
30398                     scale = targetHeight / height;
30399                 }
30400                 
30401                 if(x > 0 && y > 0){
30402                     scale = targetWidth / width;
30403                     
30404                     if(width < height){
30405                         scale = targetHeight / height;
30406                     }
30407                 }
30408                 
30409                 context.scale(scale, scale);
30410                 
30411                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30412                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30413
30414                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30415                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30416                 
30417                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30418                 
30419                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30420                 
30421                 break;
30422             default : 
30423                 break;
30424         }
30425         
30426         this.cropData = canvas.toDataURL(this.cropType);
30427         
30428         if(this.fireEvent('crop', this, this.cropData) !== false){
30429             this.process(this.file, this.cropData);
30430         }
30431         
30432         return;
30433         
30434     },
30435     
30436     setThumbBoxSize : function()
30437     {
30438         var width, height;
30439         
30440         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30441             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30442             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30443             
30444             this.minWidth = width;
30445             this.minHeight = height;
30446             
30447             if(this.rotate == 90 || this.rotate == 270){
30448                 this.minWidth = height;
30449                 this.minHeight = width;
30450             }
30451         }
30452         
30453         height = 300;
30454         width = Math.ceil(this.minWidth * height / this.minHeight);
30455         
30456         if(this.minWidth > this.minHeight){
30457             width = 300;
30458             height = Math.ceil(this.minHeight * width / this.minWidth);
30459         }
30460         
30461         this.thumbEl.setStyle({
30462             width : width + 'px',
30463             height : height + 'px'
30464         });
30465
30466         return;
30467             
30468     },
30469     
30470     setThumbBoxPosition : function()
30471     {
30472         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30473         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30474         
30475         this.thumbEl.setLeft(x);
30476         this.thumbEl.setTop(y);
30477         
30478     },
30479     
30480     baseRotateLevel : function()
30481     {
30482         this.baseRotate = 1;
30483         
30484         if(
30485                 typeof(this.exif) != 'undefined' &&
30486                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30487                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30488         ){
30489             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30490         }
30491         
30492         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30493         
30494     },
30495     
30496     baseScaleLevel : function()
30497     {
30498         var width, height;
30499         
30500         if(this.isDocument){
30501             
30502             if(this.baseRotate == 6 || this.baseRotate == 8){
30503             
30504                 height = this.thumbEl.getHeight();
30505                 this.baseScale = height / this.imageEl.OriginWidth;
30506
30507                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30508                     width = this.thumbEl.getWidth();
30509                     this.baseScale = width / this.imageEl.OriginHeight;
30510                 }
30511
30512                 return;
30513             }
30514
30515             height = this.thumbEl.getHeight();
30516             this.baseScale = height / this.imageEl.OriginHeight;
30517
30518             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30519                 width = this.thumbEl.getWidth();
30520                 this.baseScale = width / this.imageEl.OriginWidth;
30521             }
30522
30523             return;
30524         }
30525         
30526         if(this.baseRotate == 6 || this.baseRotate == 8){
30527             
30528             width = this.thumbEl.getHeight();
30529             this.baseScale = width / this.imageEl.OriginHeight;
30530             
30531             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30532                 height = this.thumbEl.getWidth();
30533                 this.baseScale = height / this.imageEl.OriginHeight;
30534             }
30535             
30536             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30537                 height = this.thumbEl.getWidth();
30538                 this.baseScale = height / this.imageEl.OriginHeight;
30539                 
30540                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30541                     width = this.thumbEl.getHeight();
30542                     this.baseScale = width / this.imageEl.OriginWidth;
30543                 }
30544             }
30545             
30546             return;
30547         }
30548         
30549         width = this.thumbEl.getWidth();
30550         this.baseScale = width / this.imageEl.OriginWidth;
30551         
30552         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30553             height = this.thumbEl.getHeight();
30554             this.baseScale = height / this.imageEl.OriginHeight;
30555         }
30556         
30557         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30558             
30559             height = this.thumbEl.getHeight();
30560             this.baseScale = height / this.imageEl.OriginHeight;
30561             
30562             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30563                 width = this.thumbEl.getWidth();
30564                 this.baseScale = width / this.imageEl.OriginWidth;
30565             }
30566             
30567         }
30568         
30569         return;
30570     },
30571     
30572     getScaleLevel : function()
30573     {
30574         return this.baseScale * Math.pow(1.1, this.scale);
30575     },
30576     
30577     onTouchStart : function(e)
30578     {
30579         if(!this.canvasLoaded){
30580             this.beforeSelectFile(e);
30581             return;
30582         }
30583         
30584         var touches = e.browserEvent.touches;
30585         
30586         if(!touches){
30587             return;
30588         }
30589         
30590         if(touches.length == 1){
30591             this.onMouseDown(e);
30592             return;
30593         }
30594         
30595         if(touches.length != 2){
30596             return;
30597         }
30598         
30599         var coords = [];
30600         
30601         for(var i = 0, finger; finger = touches[i]; i++){
30602             coords.push(finger.pageX, finger.pageY);
30603         }
30604         
30605         var x = Math.pow(coords[0] - coords[2], 2);
30606         var y = Math.pow(coords[1] - coords[3], 2);
30607         
30608         this.startDistance = Math.sqrt(x + y);
30609         
30610         this.startScale = this.scale;
30611         
30612         this.pinching = true;
30613         this.dragable = false;
30614         
30615     },
30616     
30617     onTouchMove : function(e)
30618     {
30619         if(!this.pinching && !this.dragable){
30620             return;
30621         }
30622         
30623         var touches = e.browserEvent.touches;
30624         
30625         if(!touches){
30626             return;
30627         }
30628         
30629         if(this.dragable){
30630             this.onMouseMove(e);
30631             return;
30632         }
30633         
30634         var coords = [];
30635         
30636         for(var i = 0, finger; finger = touches[i]; i++){
30637             coords.push(finger.pageX, finger.pageY);
30638         }
30639         
30640         var x = Math.pow(coords[0] - coords[2], 2);
30641         var y = Math.pow(coords[1] - coords[3], 2);
30642         
30643         this.endDistance = Math.sqrt(x + y);
30644         
30645         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30646         
30647         if(!this.zoomable()){
30648             this.scale = this.startScale;
30649             return;
30650         }
30651         
30652         this.draw();
30653         
30654     },
30655     
30656     onTouchEnd : function(e)
30657     {
30658         this.pinching = false;
30659         this.dragable = false;
30660         
30661     },
30662     
30663     process : function(file, crop)
30664     {
30665         if(this.loadMask){
30666             this.maskEl.mask(this.loadingText);
30667         }
30668         
30669         this.xhr = new XMLHttpRequest();
30670         
30671         file.xhr = this.xhr;
30672
30673         this.xhr.open(this.method, this.url, true);
30674         
30675         var headers = {
30676             "Accept": "application/json",
30677             "Cache-Control": "no-cache",
30678             "X-Requested-With": "XMLHttpRequest"
30679         };
30680         
30681         for (var headerName in headers) {
30682             var headerValue = headers[headerName];
30683             if (headerValue) {
30684                 this.xhr.setRequestHeader(headerName, headerValue);
30685             }
30686         }
30687         
30688         var _this = this;
30689         
30690         this.xhr.onload = function()
30691         {
30692             _this.xhrOnLoad(_this.xhr);
30693         }
30694         
30695         this.xhr.onerror = function()
30696         {
30697             _this.xhrOnError(_this.xhr);
30698         }
30699         
30700         var formData = new FormData();
30701
30702         formData.append('returnHTML', 'NO');
30703         
30704         if(crop){
30705             formData.append('crop', crop);
30706         }
30707         
30708         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30709             formData.append(this.paramName, file, file.name);
30710         }
30711         
30712         if(typeof(file.filename) != 'undefined'){
30713             formData.append('filename', file.filename);
30714         }
30715         
30716         if(typeof(file.mimetype) != 'undefined'){
30717             formData.append('mimetype', file.mimetype);
30718         }
30719         
30720         if(this.fireEvent('arrange', this, formData) != false){
30721             this.xhr.send(formData);
30722         };
30723     },
30724     
30725     xhrOnLoad : function(xhr)
30726     {
30727         if(this.loadMask){
30728             this.maskEl.unmask();
30729         }
30730         
30731         if (xhr.readyState !== 4) {
30732             this.fireEvent('exception', this, xhr);
30733             return;
30734         }
30735
30736         var response = Roo.decode(xhr.responseText);
30737         
30738         if(!response.success){
30739             this.fireEvent('exception', this, xhr);
30740             return;
30741         }
30742         
30743         var response = Roo.decode(xhr.responseText);
30744         
30745         this.fireEvent('upload', this, response);
30746         
30747     },
30748     
30749     xhrOnError : function()
30750     {
30751         if(this.loadMask){
30752             this.maskEl.unmask();
30753         }
30754         
30755         Roo.log('xhr on error');
30756         
30757         var response = Roo.decode(xhr.responseText);
30758           
30759         Roo.log(response);
30760         
30761     },
30762     
30763     prepare : function(file)
30764     {   
30765         if(this.loadMask){
30766             this.maskEl.mask(this.loadingText);
30767         }
30768         
30769         this.file = false;
30770         this.exif = {};
30771         
30772         if(typeof(file) === 'string'){
30773             this.loadCanvas(file);
30774             return;
30775         }
30776         
30777         if(!file || !this.urlAPI){
30778             return;
30779         }
30780         
30781         this.file = file;
30782         this.cropType = file.type;
30783         
30784         var _this = this;
30785         
30786         if(this.fireEvent('prepare', this, this.file) != false){
30787             
30788             var reader = new FileReader();
30789             
30790             reader.onload = function (e) {
30791                 if (e.target.error) {
30792                     Roo.log(e.target.error);
30793                     return;
30794                 }
30795                 
30796                 var buffer = e.target.result,
30797                     dataView = new DataView(buffer),
30798                     offset = 2,
30799                     maxOffset = dataView.byteLength - 4,
30800                     markerBytes,
30801                     markerLength;
30802                 
30803                 if (dataView.getUint16(0) === 0xffd8) {
30804                     while (offset < maxOffset) {
30805                         markerBytes = dataView.getUint16(offset);
30806                         
30807                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30808                             markerLength = dataView.getUint16(offset + 2) + 2;
30809                             if (offset + markerLength > dataView.byteLength) {
30810                                 Roo.log('Invalid meta data: Invalid segment size.');
30811                                 break;
30812                             }
30813                             
30814                             if(markerBytes == 0xffe1){
30815                                 _this.parseExifData(
30816                                     dataView,
30817                                     offset,
30818                                     markerLength
30819                                 );
30820                             }
30821                             
30822                             offset += markerLength;
30823                             
30824                             continue;
30825                         }
30826                         
30827                         break;
30828                     }
30829                     
30830                 }
30831                 
30832                 var url = _this.urlAPI.createObjectURL(_this.file);
30833                 
30834                 _this.loadCanvas(url);
30835                 
30836                 return;
30837             }
30838             
30839             reader.readAsArrayBuffer(this.file);
30840             
30841         }
30842         
30843     },
30844     
30845     parseExifData : function(dataView, offset, length)
30846     {
30847         var tiffOffset = offset + 10,
30848             littleEndian,
30849             dirOffset;
30850     
30851         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30852             // No Exif data, might be XMP data instead
30853             return;
30854         }
30855         
30856         // Check for the ASCII code for "Exif" (0x45786966):
30857         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30858             // No Exif data, might be XMP data instead
30859             return;
30860         }
30861         if (tiffOffset + 8 > dataView.byteLength) {
30862             Roo.log('Invalid Exif data: Invalid segment size.');
30863             return;
30864         }
30865         // Check for the two null bytes:
30866         if (dataView.getUint16(offset + 8) !== 0x0000) {
30867             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30868             return;
30869         }
30870         // Check the byte alignment:
30871         switch (dataView.getUint16(tiffOffset)) {
30872         case 0x4949:
30873             littleEndian = true;
30874             break;
30875         case 0x4D4D:
30876             littleEndian = false;
30877             break;
30878         default:
30879             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30880             return;
30881         }
30882         // Check for the TIFF tag marker (0x002A):
30883         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30884             Roo.log('Invalid Exif data: Missing TIFF marker.');
30885             return;
30886         }
30887         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30888         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30889         
30890         this.parseExifTags(
30891             dataView,
30892             tiffOffset,
30893             tiffOffset + dirOffset,
30894             littleEndian
30895         );
30896     },
30897     
30898     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30899     {
30900         var tagsNumber,
30901             dirEndOffset,
30902             i;
30903         if (dirOffset + 6 > dataView.byteLength) {
30904             Roo.log('Invalid Exif data: Invalid directory offset.');
30905             return;
30906         }
30907         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30908         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30909         if (dirEndOffset + 4 > dataView.byteLength) {
30910             Roo.log('Invalid Exif data: Invalid directory size.');
30911             return;
30912         }
30913         for (i = 0; i < tagsNumber; i += 1) {
30914             this.parseExifTag(
30915                 dataView,
30916                 tiffOffset,
30917                 dirOffset + 2 + 12 * i, // tag offset
30918                 littleEndian
30919             );
30920         }
30921         // Return the offset to the next directory:
30922         return dataView.getUint32(dirEndOffset, littleEndian);
30923     },
30924     
30925     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30926     {
30927         var tag = dataView.getUint16(offset, littleEndian);
30928         
30929         this.exif[tag] = this.getExifValue(
30930             dataView,
30931             tiffOffset,
30932             offset,
30933             dataView.getUint16(offset + 2, littleEndian), // tag type
30934             dataView.getUint32(offset + 4, littleEndian), // tag length
30935             littleEndian
30936         );
30937     },
30938     
30939     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30940     {
30941         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30942             tagSize,
30943             dataOffset,
30944             values,
30945             i,
30946             str,
30947             c;
30948     
30949         if (!tagType) {
30950             Roo.log('Invalid Exif data: Invalid tag type.');
30951             return;
30952         }
30953         
30954         tagSize = tagType.size * length;
30955         // Determine if the value is contained in the dataOffset bytes,
30956         // or if the value at the dataOffset is a pointer to the actual data:
30957         dataOffset = tagSize > 4 ?
30958                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30959         if (dataOffset + tagSize > dataView.byteLength) {
30960             Roo.log('Invalid Exif data: Invalid data offset.');
30961             return;
30962         }
30963         if (length === 1) {
30964             return tagType.getValue(dataView, dataOffset, littleEndian);
30965         }
30966         values = [];
30967         for (i = 0; i < length; i += 1) {
30968             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30969         }
30970         
30971         if (tagType.ascii) {
30972             str = '';
30973             // Concatenate the chars:
30974             for (i = 0; i < values.length; i += 1) {
30975                 c = values[i];
30976                 // Ignore the terminating NULL byte(s):
30977                 if (c === '\u0000') {
30978                     break;
30979                 }
30980                 str += c;
30981             }
30982             return str;
30983         }
30984         return values;
30985     }
30986     
30987 });
30988
30989 Roo.apply(Roo.bootstrap.UploadCropbox, {
30990     tags : {
30991         'Orientation': 0x0112
30992     },
30993     
30994     Orientation: {
30995             1: 0, //'top-left',
30996 //            2: 'top-right',
30997             3: 180, //'bottom-right',
30998 //            4: 'bottom-left',
30999 //            5: 'left-top',
31000             6: 90, //'right-top',
31001 //            7: 'right-bottom',
31002             8: 270 //'left-bottom'
31003     },
31004     
31005     exifTagTypes : {
31006         // byte, 8-bit unsigned int:
31007         1: {
31008             getValue: function (dataView, dataOffset) {
31009                 return dataView.getUint8(dataOffset);
31010             },
31011             size: 1
31012         },
31013         // ascii, 8-bit byte:
31014         2: {
31015             getValue: function (dataView, dataOffset) {
31016                 return String.fromCharCode(dataView.getUint8(dataOffset));
31017             },
31018             size: 1,
31019             ascii: true
31020         },
31021         // short, 16 bit int:
31022         3: {
31023             getValue: function (dataView, dataOffset, littleEndian) {
31024                 return dataView.getUint16(dataOffset, littleEndian);
31025             },
31026             size: 2
31027         },
31028         // long, 32 bit int:
31029         4: {
31030             getValue: function (dataView, dataOffset, littleEndian) {
31031                 return dataView.getUint32(dataOffset, littleEndian);
31032             },
31033             size: 4
31034         },
31035         // rational = two long values, first is numerator, second is denominator:
31036         5: {
31037             getValue: function (dataView, dataOffset, littleEndian) {
31038                 return dataView.getUint32(dataOffset, littleEndian) /
31039                     dataView.getUint32(dataOffset + 4, littleEndian);
31040             },
31041             size: 8
31042         },
31043         // slong, 32 bit signed int:
31044         9: {
31045             getValue: function (dataView, dataOffset, littleEndian) {
31046                 return dataView.getInt32(dataOffset, littleEndian);
31047             },
31048             size: 4
31049         },
31050         // srational, two slongs, first is numerator, second is denominator:
31051         10: {
31052             getValue: function (dataView, dataOffset, littleEndian) {
31053                 return dataView.getInt32(dataOffset, littleEndian) /
31054                     dataView.getInt32(dataOffset + 4, littleEndian);
31055             },
31056             size: 8
31057         }
31058     },
31059     
31060     footer : {
31061         STANDARD : [
31062             {
31063                 tag : 'div',
31064                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31065                 action : 'rotate-left',
31066                 cn : [
31067                     {
31068                         tag : 'button',
31069                         cls : 'btn btn-default',
31070                         html : '<i class="fa fa-undo"></i>'
31071                     }
31072                 ]
31073             },
31074             {
31075                 tag : 'div',
31076                 cls : 'btn-group roo-upload-cropbox-picture',
31077                 action : 'picture',
31078                 cn : [
31079                     {
31080                         tag : 'button',
31081                         cls : 'btn btn-default',
31082                         html : '<i class="fa fa-picture-o"></i>'
31083                     }
31084                 ]
31085             },
31086             {
31087                 tag : 'div',
31088                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31089                 action : 'rotate-right',
31090                 cn : [
31091                     {
31092                         tag : 'button',
31093                         cls : 'btn btn-default',
31094                         html : '<i class="fa fa-repeat"></i>'
31095                     }
31096                 ]
31097             }
31098         ],
31099         DOCUMENT : [
31100             {
31101                 tag : 'div',
31102                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31103                 action : 'rotate-left',
31104                 cn : [
31105                     {
31106                         tag : 'button',
31107                         cls : 'btn btn-default',
31108                         html : '<i class="fa fa-undo"></i>'
31109                     }
31110                 ]
31111             },
31112             {
31113                 tag : 'div',
31114                 cls : 'btn-group roo-upload-cropbox-download',
31115                 action : 'download',
31116                 cn : [
31117                     {
31118                         tag : 'button',
31119                         cls : 'btn btn-default',
31120                         html : '<i class="fa fa-download"></i>'
31121                     }
31122                 ]
31123             },
31124             {
31125                 tag : 'div',
31126                 cls : 'btn-group roo-upload-cropbox-crop',
31127                 action : 'crop',
31128                 cn : [
31129                     {
31130                         tag : 'button',
31131                         cls : 'btn btn-default',
31132                         html : '<i class="fa fa-crop"></i>'
31133                     }
31134                 ]
31135             },
31136             {
31137                 tag : 'div',
31138                 cls : 'btn-group roo-upload-cropbox-trash',
31139                 action : 'trash',
31140                 cn : [
31141                     {
31142                         tag : 'button',
31143                         cls : 'btn btn-default',
31144                         html : '<i class="fa fa-trash"></i>'
31145                     }
31146                 ]
31147             },
31148             {
31149                 tag : 'div',
31150                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31151                 action : 'rotate-right',
31152                 cn : [
31153                     {
31154                         tag : 'button',
31155                         cls : 'btn btn-default',
31156                         html : '<i class="fa fa-repeat"></i>'
31157                     }
31158                 ]
31159             }
31160         ],
31161         ROTATOR : [
31162             {
31163                 tag : 'div',
31164                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31165                 action : 'rotate-left',
31166                 cn : [
31167                     {
31168                         tag : 'button',
31169                         cls : 'btn btn-default',
31170                         html : '<i class="fa fa-undo"></i>'
31171                     }
31172                 ]
31173             },
31174             {
31175                 tag : 'div',
31176                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31177                 action : 'rotate-right',
31178                 cn : [
31179                     {
31180                         tag : 'button',
31181                         cls : 'btn btn-default',
31182                         html : '<i class="fa fa-repeat"></i>'
31183                     }
31184                 ]
31185             }
31186         ]
31187     }
31188 });
31189
31190 /*
31191 * Licence: LGPL
31192 */
31193
31194 /**
31195  * @class Roo.bootstrap.DocumentManager
31196  * @extends Roo.bootstrap.Component
31197  * Bootstrap DocumentManager class
31198  * @cfg {String} paramName default 'imageUpload'
31199  * @cfg {String} toolTipName default 'filename'
31200  * @cfg {String} method default POST
31201  * @cfg {String} url action url
31202  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31203  * @cfg {Boolean} multiple multiple upload default true
31204  * @cfg {Number} thumbSize default 300
31205  * @cfg {String} fieldLabel
31206  * @cfg {Number} labelWidth default 4
31207  * @cfg {String} labelAlign (left|top) default left
31208  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31209 * @cfg {Number} labellg set the width of label (1-12)
31210  * @cfg {Number} labelmd set the width of label (1-12)
31211  * @cfg {Number} labelsm set the width of label (1-12)
31212  * @cfg {Number} labelxs set the width of label (1-12)
31213  * 
31214  * @constructor
31215  * Create a new DocumentManager
31216  * @param {Object} config The config object
31217  */
31218
31219 Roo.bootstrap.DocumentManager = function(config){
31220     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31221     
31222     this.files = [];
31223     this.delegates = [];
31224     
31225     this.addEvents({
31226         /**
31227          * @event initial
31228          * Fire when initial the DocumentManager
31229          * @param {Roo.bootstrap.DocumentManager} this
31230          */
31231         "initial" : true,
31232         /**
31233          * @event inspect
31234          * inspect selected file
31235          * @param {Roo.bootstrap.DocumentManager} this
31236          * @param {File} file
31237          */
31238         "inspect" : true,
31239         /**
31240          * @event exception
31241          * Fire when xhr load exception
31242          * @param {Roo.bootstrap.DocumentManager} this
31243          * @param {XMLHttpRequest} xhr
31244          */
31245         "exception" : true,
31246         /**
31247          * @event afterupload
31248          * Fire when xhr load exception
31249          * @param {Roo.bootstrap.DocumentManager} this
31250          * @param {XMLHttpRequest} xhr
31251          */
31252         "afterupload" : true,
31253         /**
31254          * @event prepare
31255          * prepare the form data
31256          * @param {Roo.bootstrap.DocumentManager} this
31257          * @param {Object} formData
31258          */
31259         "prepare" : true,
31260         /**
31261          * @event remove
31262          * Fire when remove the file
31263          * @param {Roo.bootstrap.DocumentManager} this
31264          * @param {Object} file
31265          */
31266         "remove" : true,
31267         /**
31268          * @event refresh
31269          * Fire after refresh the file
31270          * @param {Roo.bootstrap.DocumentManager} this
31271          */
31272         "refresh" : true,
31273         /**
31274          * @event click
31275          * Fire after click the image
31276          * @param {Roo.bootstrap.DocumentManager} this
31277          * @param {Object} file
31278          */
31279         "click" : true,
31280         /**
31281          * @event edit
31282          * Fire when upload a image and editable set to true
31283          * @param {Roo.bootstrap.DocumentManager} this
31284          * @param {Object} file
31285          */
31286         "edit" : true,
31287         /**
31288          * @event beforeselectfile
31289          * Fire before select file
31290          * @param {Roo.bootstrap.DocumentManager} this
31291          */
31292         "beforeselectfile" : true,
31293         /**
31294          * @event process
31295          * Fire before process file
31296          * @param {Roo.bootstrap.DocumentManager} this
31297          * @param {Object} file
31298          */
31299         "process" : true,
31300         /**
31301          * @event previewrendered
31302          * Fire when preview rendered
31303          * @param {Roo.bootstrap.DocumentManager} this
31304          * @param {Object} file
31305          */
31306         "previewrendered" : true,
31307         /**
31308          */
31309         "previewResize" : true
31310         
31311     });
31312 };
31313
31314 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31315     
31316     boxes : 0,
31317     inputName : '',
31318     thumbSize : 300,
31319     multiple : true,
31320     files : false,
31321     method : 'POST',
31322     url : '',
31323     paramName : 'imageUpload',
31324     toolTipName : 'filename',
31325     fieldLabel : '',
31326     labelWidth : 4,
31327     labelAlign : 'left',
31328     editable : true,
31329     delegates : false,
31330     xhr : false, 
31331     
31332     labellg : 0,
31333     labelmd : 0,
31334     labelsm : 0,
31335     labelxs : 0,
31336     
31337     getAutoCreate : function()
31338     {   
31339         var managerWidget = {
31340             tag : 'div',
31341             cls : 'roo-document-manager',
31342             cn : [
31343                 {
31344                     tag : 'input',
31345                     cls : 'roo-document-manager-selector',
31346                     type : 'file'
31347                 },
31348                 {
31349                     tag : 'div',
31350                     cls : 'roo-document-manager-uploader',
31351                     cn : [
31352                         {
31353                             tag : 'div',
31354                             cls : 'roo-document-manager-upload-btn',
31355                             html : '<i class="fa fa-plus"></i>'
31356                         }
31357                     ]
31358                     
31359                 }
31360             ]
31361         };
31362         
31363         var content = [
31364             {
31365                 tag : 'div',
31366                 cls : 'column col-md-12',
31367                 cn : managerWidget
31368             }
31369         ];
31370         
31371         if(this.fieldLabel.length){
31372             
31373             content = [
31374                 {
31375                     tag : 'div',
31376                     cls : 'column col-md-12',
31377                     html : this.fieldLabel
31378                 },
31379                 {
31380                     tag : 'div',
31381                     cls : 'column col-md-12',
31382                     cn : managerWidget
31383                 }
31384             ];
31385
31386             if(this.labelAlign == 'left'){
31387                 content = [
31388                     {
31389                         tag : 'div',
31390                         cls : 'column',
31391                         html : this.fieldLabel
31392                     },
31393                     {
31394                         tag : 'div',
31395                         cls : 'column',
31396                         cn : managerWidget
31397                     }
31398                 ];
31399                 
31400                 if(this.labelWidth > 12){
31401                     content[0].style = "width: " + this.labelWidth + 'px';
31402                 }
31403
31404                 if(this.labelWidth < 13 && this.labelmd == 0){
31405                     this.labelmd = this.labelWidth;
31406                 }
31407
31408                 if(this.labellg > 0){
31409                     content[0].cls += ' col-lg-' + this.labellg;
31410                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31411                 }
31412
31413                 if(this.labelmd > 0){
31414                     content[0].cls += ' col-md-' + this.labelmd;
31415                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31416                 }
31417
31418                 if(this.labelsm > 0){
31419                     content[0].cls += ' col-sm-' + this.labelsm;
31420                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31421                 }
31422
31423                 if(this.labelxs > 0){
31424                     content[0].cls += ' col-xs-' + this.labelxs;
31425                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31426                 }
31427                 
31428             }
31429         }
31430         
31431         var cfg = {
31432             tag : 'div',
31433             cls : 'row clearfix',
31434             cn : content
31435         };
31436         
31437         return cfg;
31438         
31439     },
31440     
31441     initEvents : function()
31442     {
31443         this.managerEl = this.el.select('.roo-document-manager', true).first();
31444         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31445         
31446         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31447         this.selectorEl.hide();
31448         
31449         if(this.multiple){
31450             this.selectorEl.attr('multiple', 'multiple');
31451         }
31452         
31453         this.selectorEl.on('change', this.onFileSelected, this);
31454         
31455         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31456         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31457         
31458         this.uploader.on('click', this.onUploaderClick, this);
31459         
31460         this.renderProgressDialog();
31461         
31462         var _this = this;
31463         
31464         window.addEventListener("resize", function() { _this.refresh(); } );
31465         
31466         this.fireEvent('initial', this);
31467     },
31468     
31469     renderProgressDialog : function()
31470     {
31471         var _this = this;
31472         
31473         this.progressDialog = new Roo.bootstrap.Modal({
31474             cls : 'roo-document-manager-progress-dialog',
31475             allow_close : false,
31476             animate : false,
31477             title : '',
31478             buttons : [
31479                 {
31480                     name  :'cancel',
31481                     weight : 'danger',
31482                     html : 'Cancel'
31483                 }
31484             ], 
31485             listeners : { 
31486                 btnclick : function() {
31487                     _this.uploadCancel();
31488                     this.hide();
31489                 }
31490             }
31491         });
31492          
31493         this.progressDialog.render(Roo.get(document.body));
31494          
31495         this.progress = new Roo.bootstrap.Progress({
31496             cls : 'roo-document-manager-progress',
31497             active : true,
31498             striped : true
31499         });
31500         
31501         this.progress.render(this.progressDialog.getChildContainer());
31502         
31503         this.progressBar = new Roo.bootstrap.ProgressBar({
31504             cls : 'roo-document-manager-progress-bar',
31505             aria_valuenow : 0,
31506             aria_valuemin : 0,
31507             aria_valuemax : 12,
31508             panel : 'success'
31509         });
31510         
31511         this.progressBar.render(this.progress.getChildContainer());
31512     },
31513     
31514     onUploaderClick : function(e)
31515     {
31516         e.preventDefault();
31517      
31518         if(this.fireEvent('beforeselectfile', this) != false){
31519             this.selectorEl.dom.click();
31520         }
31521         
31522     },
31523     
31524     onFileSelected : function(e)
31525     {
31526         e.preventDefault();
31527         
31528         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31529             return;
31530         }
31531         
31532         Roo.each(this.selectorEl.dom.files, function(file){
31533             if(this.fireEvent('inspect', this, file) != false){
31534                 this.files.push(file);
31535             }
31536         }, this);
31537         
31538         this.queue();
31539         
31540     },
31541     
31542     queue : function()
31543     {
31544         this.selectorEl.dom.value = '';
31545         
31546         if(!this.files || !this.files.length){
31547             return;
31548         }
31549         
31550         if(this.boxes > 0 && this.files.length > this.boxes){
31551             this.files = this.files.slice(0, this.boxes);
31552         }
31553         
31554         this.uploader.show();
31555         
31556         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31557             this.uploader.hide();
31558         }
31559         
31560         var _this = this;
31561         
31562         var files = [];
31563         
31564         var docs = [];
31565         
31566         Roo.each(this.files, function(file){
31567             
31568             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31569                 var f = this.renderPreview(file);
31570                 files.push(f);
31571                 return;
31572             }
31573             
31574             if(file.type.indexOf('image') != -1){
31575                 this.delegates.push(
31576                     (function(){
31577                         _this.process(file);
31578                     }).createDelegate(this)
31579                 );
31580         
31581                 return;
31582             }
31583             
31584             docs.push(
31585                 (function(){
31586                     _this.process(file);
31587                 }).createDelegate(this)
31588             );
31589             
31590         }, this);
31591         
31592         this.files = files;
31593         
31594         this.delegates = this.delegates.concat(docs);
31595         
31596         if(!this.delegates.length){
31597             this.refresh();
31598             return;
31599         }
31600         
31601         this.progressBar.aria_valuemax = this.delegates.length;
31602         
31603         this.arrange();
31604         
31605         return;
31606     },
31607     
31608     arrange : function()
31609     {
31610         if(!this.delegates.length){
31611             this.progressDialog.hide();
31612             this.refresh();
31613             return;
31614         }
31615         
31616         var delegate = this.delegates.shift();
31617         
31618         this.progressDialog.show();
31619         
31620         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31621         
31622         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31623         
31624         delegate();
31625     },
31626     
31627     refresh : function()
31628     {
31629         this.uploader.show();
31630         
31631         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31632             this.uploader.hide();
31633         }
31634         
31635         Roo.isTouch ? this.closable(false) : this.closable(true);
31636         
31637         this.fireEvent('refresh', this);
31638     },
31639     
31640     onRemove : function(e, el, o)
31641     {
31642         e.preventDefault();
31643         
31644         this.fireEvent('remove', this, o);
31645         
31646     },
31647     
31648     remove : function(o)
31649     {
31650         var files = [];
31651         
31652         Roo.each(this.files, function(file){
31653             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31654                 files.push(file);
31655                 return;
31656             }
31657
31658             o.target.remove();
31659
31660         }, this);
31661         
31662         this.files = files;
31663         
31664         this.refresh();
31665     },
31666     
31667     clear : function()
31668     {
31669         Roo.each(this.files, function(file){
31670             if(!file.target){
31671                 return;
31672             }
31673             
31674             file.target.remove();
31675
31676         }, this);
31677         
31678         this.files = [];
31679         
31680         this.refresh();
31681     },
31682     
31683     onClick : function(e, el, o)
31684     {
31685         e.preventDefault();
31686         
31687         this.fireEvent('click', this, o);
31688         
31689     },
31690     
31691     closable : function(closable)
31692     {
31693         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31694             
31695             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31696             
31697             if(closable){
31698                 el.show();
31699                 return;
31700             }
31701             
31702             el.hide();
31703             
31704         }, this);
31705     },
31706     
31707     xhrOnLoad : function(xhr)
31708     {
31709         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31710             el.remove();
31711         }, this);
31712         
31713         if (xhr.readyState !== 4) {
31714             this.arrange();
31715             this.fireEvent('exception', this, xhr);
31716             return;
31717         }
31718
31719         var response = Roo.decode(xhr.responseText);
31720         
31721         if(!response.success){
31722             this.arrange();
31723             this.fireEvent('exception', this, xhr);
31724             return;
31725         }
31726         
31727         var file = this.renderPreview(response.data);
31728         
31729         this.files.push(file);
31730         
31731         this.arrange();
31732         
31733         this.fireEvent('afterupload', this, xhr);
31734         
31735     },
31736     
31737     xhrOnError : function(xhr)
31738     {
31739         Roo.log('xhr on error');
31740         
31741         var response = Roo.decode(xhr.responseText);
31742           
31743         Roo.log(response);
31744         
31745         this.arrange();
31746     },
31747     
31748     process : function(file)
31749     {
31750         if(this.fireEvent('process', this, file) !== false){
31751             if(this.editable && file.type.indexOf('image') != -1){
31752                 this.fireEvent('edit', this, file);
31753                 return;
31754             }
31755
31756             this.uploadStart(file, false);
31757
31758             return;
31759         }
31760         
31761     },
31762     
31763     uploadStart : function(file, crop)
31764     {
31765         this.xhr = new XMLHttpRequest();
31766         
31767         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31768             this.arrange();
31769             return;
31770         }
31771         
31772         file.xhr = this.xhr;
31773             
31774         this.managerEl.createChild({
31775             tag : 'div',
31776             cls : 'roo-document-manager-loading',
31777             cn : [
31778                 {
31779                     tag : 'div',
31780                     tooltip : file.name,
31781                     cls : 'roo-document-manager-thumb',
31782                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31783                 }
31784             ]
31785
31786         });
31787
31788         this.xhr.open(this.method, this.url, true);
31789         
31790         var headers = {
31791             "Accept": "application/json",
31792             "Cache-Control": "no-cache",
31793             "X-Requested-With": "XMLHttpRequest"
31794         };
31795         
31796         for (var headerName in headers) {
31797             var headerValue = headers[headerName];
31798             if (headerValue) {
31799                 this.xhr.setRequestHeader(headerName, headerValue);
31800             }
31801         }
31802         
31803         var _this = this;
31804         
31805         this.xhr.onload = function()
31806         {
31807             _this.xhrOnLoad(_this.xhr);
31808         }
31809         
31810         this.xhr.onerror = function()
31811         {
31812             _this.xhrOnError(_this.xhr);
31813         }
31814         
31815         var formData = new FormData();
31816
31817         formData.append('returnHTML', 'NO');
31818         
31819         if(crop){
31820             formData.append('crop', crop);
31821         }
31822         
31823         formData.append(this.paramName, file, file.name);
31824         
31825         var options = {
31826             file : file, 
31827             manually : false
31828         };
31829         
31830         if(this.fireEvent('prepare', this, formData, options) != false){
31831             
31832             if(options.manually){
31833                 return;
31834             }
31835             
31836             this.xhr.send(formData);
31837             return;
31838         };
31839         
31840         this.uploadCancel();
31841     },
31842     
31843     uploadCancel : function()
31844     {
31845         if (this.xhr) {
31846             this.xhr.abort();
31847         }
31848         
31849         this.delegates = [];
31850         
31851         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31852             el.remove();
31853         }, this);
31854         
31855         this.arrange();
31856     },
31857     
31858     renderPreview : function(file)
31859     {
31860         if(typeof(file.target) != 'undefined' && file.target){
31861             return file;
31862         }
31863         
31864         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31865         
31866         var previewEl = this.managerEl.createChild({
31867             tag : 'div',
31868             cls : 'roo-document-manager-preview',
31869             cn : [
31870                 {
31871                     tag : 'div',
31872                     tooltip : file[this.toolTipName],
31873                     cls : 'roo-document-manager-thumb',
31874                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31875                 },
31876                 {
31877                     tag : 'button',
31878                     cls : 'close',
31879                     html : '<i class="fa fa-times-circle"></i>'
31880                 }
31881             ]
31882         });
31883
31884         var close = previewEl.select('button.close', true).first();
31885
31886         close.on('click', this.onRemove, this, file);
31887
31888         file.target = previewEl;
31889
31890         var image = previewEl.select('img', true).first();
31891         
31892         var _this = this;
31893         
31894         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31895         
31896         image.on('click', this.onClick, this, file);
31897         
31898         this.fireEvent('previewrendered', this, file);
31899         
31900         return file;
31901         
31902     },
31903     
31904     onPreviewLoad : function(file, image)
31905     {
31906         if(typeof(file.target) == 'undefined' || !file.target){
31907             return;
31908         }
31909         
31910         var width = image.dom.naturalWidth || image.dom.width;
31911         var height = image.dom.naturalHeight || image.dom.height;
31912         
31913         if(!this.previewResize) {
31914             return;
31915         }
31916         
31917         if(width > height){
31918             file.target.addClass('wide');
31919             return;
31920         }
31921         
31922         file.target.addClass('tall');
31923         return;
31924         
31925     },
31926     
31927     uploadFromSource : function(file, crop)
31928     {
31929         this.xhr = new XMLHttpRequest();
31930         
31931         this.managerEl.createChild({
31932             tag : 'div',
31933             cls : 'roo-document-manager-loading',
31934             cn : [
31935                 {
31936                     tag : 'div',
31937                     tooltip : file.name,
31938                     cls : 'roo-document-manager-thumb',
31939                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31940                 }
31941             ]
31942
31943         });
31944
31945         this.xhr.open(this.method, this.url, true);
31946         
31947         var headers = {
31948             "Accept": "application/json",
31949             "Cache-Control": "no-cache",
31950             "X-Requested-With": "XMLHttpRequest"
31951         };
31952         
31953         for (var headerName in headers) {
31954             var headerValue = headers[headerName];
31955             if (headerValue) {
31956                 this.xhr.setRequestHeader(headerName, headerValue);
31957             }
31958         }
31959         
31960         var _this = this;
31961         
31962         this.xhr.onload = function()
31963         {
31964             _this.xhrOnLoad(_this.xhr);
31965         }
31966         
31967         this.xhr.onerror = function()
31968         {
31969             _this.xhrOnError(_this.xhr);
31970         }
31971         
31972         var formData = new FormData();
31973
31974         formData.append('returnHTML', 'NO');
31975         
31976         formData.append('crop', crop);
31977         
31978         if(typeof(file.filename) != 'undefined'){
31979             formData.append('filename', file.filename);
31980         }
31981         
31982         if(typeof(file.mimetype) != 'undefined'){
31983             formData.append('mimetype', file.mimetype);
31984         }
31985         
31986         Roo.log(formData);
31987         
31988         if(this.fireEvent('prepare', this, formData) != false){
31989             this.xhr.send(formData);
31990         };
31991     }
31992 });
31993
31994 /*
31995 * Licence: LGPL
31996 */
31997
31998 /**
31999  * @class Roo.bootstrap.DocumentViewer
32000  * @extends Roo.bootstrap.Component
32001  * Bootstrap DocumentViewer class
32002  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32003  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32004  * 
32005  * @constructor
32006  * Create a new DocumentViewer
32007  * @param {Object} config The config object
32008  */
32009
32010 Roo.bootstrap.DocumentViewer = function(config){
32011     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32012     
32013     this.addEvents({
32014         /**
32015          * @event initial
32016          * Fire after initEvent
32017          * @param {Roo.bootstrap.DocumentViewer} this
32018          */
32019         "initial" : true,
32020         /**
32021          * @event click
32022          * Fire after click
32023          * @param {Roo.bootstrap.DocumentViewer} this
32024          */
32025         "click" : true,
32026         /**
32027          * @event download
32028          * Fire after download button
32029          * @param {Roo.bootstrap.DocumentViewer} this
32030          */
32031         "download" : true,
32032         /**
32033          * @event trash
32034          * Fire after trash button
32035          * @param {Roo.bootstrap.DocumentViewer} this
32036          */
32037         "trash" : true
32038         
32039     });
32040 };
32041
32042 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32043     
32044     showDownload : true,
32045     
32046     showTrash : true,
32047     
32048     getAutoCreate : function()
32049     {
32050         var cfg = {
32051             tag : 'div',
32052             cls : 'roo-document-viewer',
32053             cn : [
32054                 {
32055                     tag : 'div',
32056                     cls : 'roo-document-viewer-body',
32057                     cn : [
32058                         {
32059                             tag : 'div',
32060                             cls : 'roo-document-viewer-thumb',
32061                             cn : [
32062                                 {
32063                                     tag : 'img',
32064                                     cls : 'roo-document-viewer-image'
32065                                 }
32066                             ]
32067                         }
32068                     ]
32069                 },
32070                 {
32071                     tag : 'div',
32072                     cls : 'roo-document-viewer-footer',
32073                     cn : {
32074                         tag : 'div',
32075                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32076                         cn : [
32077                             {
32078                                 tag : 'div',
32079                                 cls : 'btn-group roo-document-viewer-download',
32080                                 cn : [
32081                                     {
32082                                         tag : 'button',
32083                                         cls : 'btn btn-default',
32084                                         html : '<i class="fa fa-download"></i>'
32085                                     }
32086                                 ]
32087                             },
32088                             {
32089                                 tag : 'div',
32090                                 cls : 'btn-group roo-document-viewer-trash',
32091                                 cn : [
32092                                     {
32093                                         tag : 'button',
32094                                         cls : 'btn btn-default',
32095                                         html : '<i class="fa fa-trash"></i>'
32096                                     }
32097                                 ]
32098                             }
32099                         ]
32100                     }
32101                 }
32102             ]
32103         };
32104         
32105         return cfg;
32106     },
32107     
32108     initEvents : function()
32109     {
32110         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32111         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32112         
32113         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32114         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32115         
32116         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32117         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32118         
32119         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32120         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32121         
32122         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32123         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32124         
32125         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32126         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32127         
32128         this.bodyEl.on('click', this.onClick, this);
32129         this.downloadBtn.on('click', this.onDownload, this);
32130         this.trashBtn.on('click', this.onTrash, this);
32131         
32132         this.downloadBtn.hide();
32133         this.trashBtn.hide();
32134         
32135         if(this.showDownload){
32136             this.downloadBtn.show();
32137         }
32138         
32139         if(this.showTrash){
32140             this.trashBtn.show();
32141         }
32142         
32143         if(!this.showDownload && !this.showTrash) {
32144             this.footerEl.hide();
32145         }
32146         
32147     },
32148     
32149     initial : function()
32150     {
32151         this.fireEvent('initial', this);
32152         
32153     },
32154     
32155     onClick : function(e)
32156     {
32157         e.preventDefault();
32158         
32159         this.fireEvent('click', this);
32160     },
32161     
32162     onDownload : function(e)
32163     {
32164         e.preventDefault();
32165         
32166         this.fireEvent('download', this);
32167     },
32168     
32169     onTrash : function(e)
32170     {
32171         e.preventDefault();
32172         
32173         this.fireEvent('trash', this);
32174     }
32175     
32176 });
32177 /*
32178  * - LGPL
32179  *
32180  * nav progress bar
32181  * 
32182  */
32183
32184 /**
32185  * @class Roo.bootstrap.NavProgressBar
32186  * @extends Roo.bootstrap.Component
32187  * Bootstrap NavProgressBar class
32188  * 
32189  * @constructor
32190  * Create a new nav progress bar
32191  * @param {Object} config The config object
32192  */
32193
32194 Roo.bootstrap.NavProgressBar = function(config){
32195     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32196
32197     this.bullets = this.bullets || [];
32198    
32199 //    Roo.bootstrap.NavProgressBar.register(this);
32200      this.addEvents({
32201         /**
32202              * @event changed
32203              * Fires when the active item changes
32204              * @param {Roo.bootstrap.NavProgressBar} this
32205              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32206              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32207          */
32208         'changed': true
32209      });
32210     
32211 };
32212
32213 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32214     
32215     bullets : [],
32216     barItems : [],
32217     
32218     getAutoCreate : function()
32219     {
32220         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32221         
32222         cfg = {
32223             tag : 'div',
32224             cls : 'roo-navigation-bar-group',
32225             cn : [
32226                 {
32227                     tag : 'div',
32228                     cls : 'roo-navigation-top-bar'
32229                 },
32230                 {
32231                     tag : 'div',
32232                     cls : 'roo-navigation-bullets-bar',
32233                     cn : [
32234                         {
32235                             tag : 'ul',
32236                             cls : 'roo-navigation-bar'
32237                         }
32238                     ]
32239                 },
32240                 
32241                 {
32242                     tag : 'div',
32243                     cls : 'roo-navigation-bottom-bar'
32244                 }
32245             ]
32246             
32247         };
32248         
32249         return cfg;
32250         
32251     },
32252     
32253     initEvents: function() 
32254     {
32255         
32256     },
32257     
32258     onRender : function(ct, position) 
32259     {
32260         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32261         
32262         if(this.bullets.length){
32263             Roo.each(this.bullets, function(b){
32264                this.addItem(b);
32265             }, this);
32266         }
32267         
32268         this.format();
32269         
32270     },
32271     
32272     addItem : function(cfg)
32273     {
32274         var item = new Roo.bootstrap.NavProgressItem(cfg);
32275         
32276         item.parentId = this.id;
32277         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32278         
32279         if(cfg.html){
32280             var top = new Roo.bootstrap.Element({
32281                 tag : 'div',
32282                 cls : 'roo-navigation-bar-text'
32283             });
32284             
32285             var bottom = new Roo.bootstrap.Element({
32286                 tag : 'div',
32287                 cls : 'roo-navigation-bar-text'
32288             });
32289             
32290             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32291             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32292             
32293             var topText = new Roo.bootstrap.Element({
32294                 tag : 'span',
32295                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32296             });
32297             
32298             var bottomText = new Roo.bootstrap.Element({
32299                 tag : 'span',
32300                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32301             });
32302             
32303             topText.onRender(top.el, null);
32304             bottomText.onRender(bottom.el, null);
32305             
32306             item.topEl = top;
32307             item.bottomEl = bottom;
32308         }
32309         
32310         this.barItems.push(item);
32311         
32312         return item;
32313     },
32314     
32315     getActive : function()
32316     {
32317         var active = false;
32318         
32319         Roo.each(this.barItems, function(v){
32320             
32321             if (!v.isActive()) {
32322                 return;
32323             }
32324             
32325             active = v;
32326             return false;
32327             
32328         });
32329         
32330         return active;
32331     },
32332     
32333     setActiveItem : function(item)
32334     {
32335         var prev = false;
32336         
32337         Roo.each(this.barItems, function(v){
32338             if (v.rid == item.rid) {
32339                 return ;
32340             }
32341             
32342             if (v.isActive()) {
32343                 v.setActive(false);
32344                 prev = v;
32345             }
32346         });
32347
32348         item.setActive(true);
32349         
32350         this.fireEvent('changed', this, item, prev);
32351     },
32352     
32353     getBarItem: function(rid)
32354     {
32355         var ret = false;
32356         
32357         Roo.each(this.barItems, function(e) {
32358             if (e.rid != rid) {
32359                 return;
32360             }
32361             
32362             ret =  e;
32363             return false;
32364         });
32365         
32366         return ret;
32367     },
32368     
32369     indexOfItem : function(item)
32370     {
32371         var index = false;
32372         
32373         Roo.each(this.barItems, function(v, i){
32374             
32375             if (v.rid != item.rid) {
32376                 return;
32377             }
32378             
32379             index = i;
32380             return false
32381         });
32382         
32383         return index;
32384     },
32385     
32386     setActiveNext : function()
32387     {
32388         var i = this.indexOfItem(this.getActive());
32389         
32390         if (i > this.barItems.length) {
32391             return;
32392         }
32393         
32394         this.setActiveItem(this.barItems[i+1]);
32395     },
32396     
32397     setActivePrev : function()
32398     {
32399         var i = this.indexOfItem(this.getActive());
32400         
32401         if (i  < 1) {
32402             return;
32403         }
32404         
32405         this.setActiveItem(this.barItems[i-1]);
32406     },
32407     
32408     format : function()
32409     {
32410         if(!this.barItems.length){
32411             return;
32412         }
32413      
32414         var width = 100 / this.barItems.length;
32415         
32416         Roo.each(this.barItems, function(i){
32417             i.el.setStyle('width', width + '%');
32418             i.topEl.el.setStyle('width', width + '%');
32419             i.bottomEl.el.setStyle('width', width + '%');
32420         }, this);
32421         
32422     }
32423     
32424 });
32425 /*
32426  * - LGPL
32427  *
32428  * Nav Progress Item
32429  * 
32430  */
32431
32432 /**
32433  * @class Roo.bootstrap.NavProgressItem
32434  * @extends Roo.bootstrap.Component
32435  * Bootstrap NavProgressItem class
32436  * @cfg {String} rid the reference id
32437  * @cfg {Boolean} active (true|false) Is item active default false
32438  * @cfg {Boolean} disabled (true|false) Is item active default false
32439  * @cfg {String} html
32440  * @cfg {String} position (top|bottom) text position default bottom
32441  * @cfg {String} icon show icon instead of number
32442  * 
32443  * @constructor
32444  * Create a new NavProgressItem
32445  * @param {Object} config The config object
32446  */
32447 Roo.bootstrap.NavProgressItem = function(config){
32448     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32449     this.addEvents({
32450         // raw events
32451         /**
32452          * @event click
32453          * The raw click event for the entire grid.
32454          * @param {Roo.bootstrap.NavProgressItem} this
32455          * @param {Roo.EventObject} e
32456          */
32457         "click" : true
32458     });
32459    
32460 };
32461
32462 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32463     
32464     rid : '',
32465     active : false,
32466     disabled : false,
32467     html : '',
32468     position : 'bottom',
32469     icon : false,
32470     
32471     getAutoCreate : function()
32472     {
32473         var iconCls = 'roo-navigation-bar-item-icon';
32474         
32475         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32476         
32477         var cfg = {
32478             tag: 'li',
32479             cls: 'roo-navigation-bar-item',
32480             cn : [
32481                 {
32482                     tag : 'i',
32483                     cls : iconCls
32484                 }
32485             ]
32486         };
32487         
32488         if(this.active){
32489             cfg.cls += ' active';
32490         }
32491         if(this.disabled){
32492             cfg.cls += ' disabled';
32493         }
32494         
32495         return cfg;
32496     },
32497     
32498     disable : function()
32499     {
32500         this.setDisabled(true);
32501     },
32502     
32503     enable : function()
32504     {
32505         this.setDisabled(false);
32506     },
32507     
32508     initEvents: function() 
32509     {
32510         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32511         
32512         this.iconEl.on('click', this.onClick, this);
32513     },
32514     
32515     onClick : function(e)
32516     {
32517         e.preventDefault();
32518         
32519         if(this.disabled){
32520             return;
32521         }
32522         
32523         if(this.fireEvent('click', this, e) === false){
32524             return;
32525         };
32526         
32527         this.parent().setActiveItem(this);
32528     },
32529     
32530     isActive: function () 
32531     {
32532         return this.active;
32533     },
32534     
32535     setActive : function(state)
32536     {
32537         if(this.active == state){
32538             return;
32539         }
32540         
32541         this.active = state;
32542         
32543         if (state) {
32544             this.el.addClass('active');
32545             return;
32546         }
32547         
32548         this.el.removeClass('active');
32549         
32550         return;
32551     },
32552     
32553     setDisabled : function(state)
32554     {
32555         if(this.disabled == state){
32556             return;
32557         }
32558         
32559         this.disabled = state;
32560         
32561         if (state) {
32562             this.el.addClass('disabled');
32563             return;
32564         }
32565         
32566         this.el.removeClass('disabled');
32567     },
32568     
32569     tooltipEl : function()
32570     {
32571         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32572     }
32573 });
32574  
32575
32576  /*
32577  * - LGPL
32578  *
32579  * FieldLabel
32580  * 
32581  */
32582
32583 /**
32584  * @class Roo.bootstrap.FieldLabel
32585  * @extends Roo.bootstrap.Component
32586  * Bootstrap FieldLabel class
32587  * @cfg {String} html contents of the element
32588  * @cfg {String} tag tag of the element default label
32589  * @cfg {String} cls class of the element
32590  * @cfg {String} target label target 
32591  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32592  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32593  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32594  * @cfg {String} iconTooltip default "This field is required"
32595  * @cfg {String} indicatorpos (left|right) default left
32596  * 
32597  * @constructor
32598  * Create a new FieldLabel
32599  * @param {Object} config The config object
32600  */
32601
32602 Roo.bootstrap.FieldLabel = function(config){
32603     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32604     
32605     this.addEvents({
32606             /**
32607              * @event invalid
32608              * Fires after the field has been marked as invalid.
32609              * @param {Roo.form.FieldLabel} this
32610              * @param {String} msg The validation message
32611              */
32612             invalid : true,
32613             /**
32614              * @event valid
32615              * Fires after the field has been validated with no errors.
32616              * @param {Roo.form.FieldLabel} this
32617              */
32618             valid : true
32619         });
32620 };
32621
32622 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32623     
32624     tag: 'label',
32625     cls: '',
32626     html: '',
32627     target: '',
32628     allowBlank : true,
32629     invalidClass : 'has-warning',
32630     validClass : 'has-success',
32631     iconTooltip : 'This field is required',
32632     indicatorpos : 'left',
32633     
32634     getAutoCreate : function(){
32635         
32636         var cls = "";
32637         if (!this.allowBlank) {
32638             cls  = "visible";
32639         }
32640         
32641         var cfg = {
32642             tag : this.tag,
32643             cls : 'roo-bootstrap-field-label ' + this.cls,
32644             for : this.target,
32645             cn : [
32646                 {
32647                     tag : 'i',
32648                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32649                     tooltip : this.iconTooltip
32650                 },
32651                 {
32652                     tag : 'span',
32653                     html : this.html
32654                 }
32655             ] 
32656         };
32657         
32658         if(this.indicatorpos == 'right'){
32659             var cfg = {
32660                 tag : this.tag,
32661                 cls : 'roo-bootstrap-field-label ' + this.cls,
32662                 for : this.target,
32663                 cn : [
32664                     {
32665                         tag : 'span',
32666                         html : this.html
32667                     },
32668                     {
32669                         tag : 'i',
32670                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32671                         tooltip : this.iconTooltip
32672                     }
32673                 ] 
32674             };
32675         }
32676         
32677         return cfg;
32678     },
32679     
32680     initEvents: function() 
32681     {
32682         Roo.bootstrap.Element.superclass.initEvents.call(this);
32683         
32684         this.indicator = this.indicatorEl();
32685         
32686         if(this.indicator){
32687             this.indicator.removeClass('visible');
32688             this.indicator.addClass('invisible');
32689         }
32690         
32691         Roo.bootstrap.FieldLabel.register(this);
32692     },
32693     
32694     indicatorEl : function()
32695     {
32696         var indicator = this.el.select('i.roo-required-indicator',true).first();
32697         
32698         if(!indicator){
32699             return false;
32700         }
32701         
32702         return indicator;
32703         
32704     },
32705     
32706     /**
32707      * Mark this field as valid
32708      */
32709     markValid : function()
32710     {
32711         if(this.indicator){
32712             this.indicator.removeClass('visible');
32713             this.indicator.addClass('invisible');
32714         }
32715         if (Roo.bootstrap.version == 3) {
32716             this.el.removeClass(this.invalidClass);
32717             this.el.addClass(this.validClass);
32718         } else {
32719             this.el.removeClass('is-invalid');
32720             this.el.addClass('is-valid');
32721         }
32722         
32723         
32724         this.fireEvent('valid', this);
32725     },
32726     
32727     /**
32728      * Mark this field as invalid
32729      * @param {String} msg The validation message
32730      */
32731     markInvalid : function(msg)
32732     {
32733         if(this.indicator){
32734             this.indicator.removeClass('invisible');
32735             this.indicator.addClass('visible');
32736         }
32737           if (Roo.bootstrap.version == 3) {
32738             this.el.removeClass(this.validClass);
32739             this.el.addClass(this.invalidClass);
32740         } else {
32741             this.el.removeClass('is-valid');
32742             this.el.addClass('is-invalid');
32743         }
32744         
32745         
32746         this.fireEvent('invalid', this, msg);
32747     }
32748     
32749    
32750 });
32751
32752 Roo.apply(Roo.bootstrap.FieldLabel, {
32753     
32754     groups: {},
32755     
32756      /**
32757     * register a FieldLabel Group
32758     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32759     */
32760     register : function(label)
32761     {
32762         if(this.groups.hasOwnProperty(label.target)){
32763             return;
32764         }
32765      
32766         this.groups[label.target] = label;
32767         
32768     },
32769     /**
32770     * fetch a FieldLabel Group based on the target
32771     * @param {string} target
32772     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32773     */
32774     get: function(target) {
32775         if (typeof(this.groups[target]) == 'undefined') {
32776             return false;
32777         }
32778         
32779         return this.groups[target] ;
32780     }
32781 });
32782
32783  
32784
32785  /*
32786  * - LGPL
32787  *
32788  * page DateSplitField.
32789  * 
32790  */
32791
32792
32793 /**
32794  * @class Roo.bootstrap.DateSplitField
32795  * @extends Roo.bootstrap.Component
32796  * Bootstrap DateSplitField class
32797  * @cfg {string} fieldLabel - the label associated
32798  * @cfg {Number} labelWidth set the width of label (0-12)
32799  * @cfg {String} labelAlign (top|left)
32800  * @cfg {Boolean} dayAllowBlank (true|false) default false
32801  * @cfg {Boolean} monthAllowBlank (true|false) default false
32802  * @cfg {Boolean} yearAllowBlank (true|false) default false
32803  * @cfg {string} dayPlaceholder 
32804  * @cfg {string} monthPlaceholder
32805  * @cfg {string} yearPlaceholder
32806  * @cfg {string} dayFormat default 'd'
32807  * @cfg {string} monthFormat default 'm'
32808  * @cfg {string} yearFormat default 'Y'
32809  * @cfg {Number} labellg set the width of label (1-12)
32810  * @cfg {Number} labelmd set the width of label (1-12)
32811  * @cfg {Number} labelsm set the width of label (1-12)
32812  * @cfg {Number} labelxs set the width of label (1-12)
32813
32814  *     
32815  * @constructor
32816  * Create a new DateSplitField
32817  * @param {Object} config The config object
32818  */
32819
32820 Roo.bootstrap.DateSplitField = function(config){
32821     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32822     
32823     this.addEvents({
32824         // raw events
32825          /**
32826          * @event years
32827          * getting the data of years
32828          * @param {Roo.bootstrap.DateSplitField} this
32829          * @param {Object} years
32830          */
32831         "years" : true,
32832         /**
32833          * @event days
32834          * getting the data of days
32835          * @param {Roo.bootstrap.DateSplitField} this
32836          * @param {Object} days
32837          */
32838         "days" : true,
32839         /**
32840          * @event invalid
32841          * Fires after the field has been marked as invalid.
32842          * @param {Roo.form.Field} this
32843          * @param {String} msg The validation message
32844          */
32845         invalid : true,
32846        /**
32847          * @event valid
32848          * Fires after the field has been validated with no errors.
32849          * @param {Roo.form.Field} this
32850          */
32851         valid : true
32852     });
32853 };
32854
32855 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32856     
32857     fieldLabel : '',
32858     labelAlign : 'top',
32859     labelWidth : 3,
32860     dayAllowBlank : false,
32861     monthAllowBlank : false,
32862     yearAllowBlank : false,
32863     dayPlaceholder : '',
32864     monthPlaceholder : '',
32865     yearPlaceholder : '',
32866     dayFormat : 'd',
32867     monthFormat : 'm',
32868     yearFormat : 'Y',
32869     isFormField : true,
32870     labellg : 0,
32871     labelmd : 0,
32872     labelsm : 0,
32873     labelxs : 0,
32874     
32875     getAutoCreate : function()
32876     {
32877         var cfg = {
32878             tag : 'div',
32879             cls : 'row roo-date-split-field-group',
32880             cn : [
32881                 {
32882                     tag : 'input',
32883                     type : 'hidden',
32884                     cls : 'form-hidden-field roo-date-split-field-group-value',
32885                     name : this.name
32886                 }
32887             ]
32888         };
32889         
32890         var labelCls = 'col-md-12';
32891         var contentCls = 'col-md-4';
32892         
32893         if(this.fieldLabel){
32894             
32895             var label = {
32896                 tag : 'div',
32897                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32898                 cn : [
32899                     {
32900                         tag : 'label',
32901                         html : this.fieldLabel
32902                     }
32903                 ]
32904             };
32905             
32906             if(this.labelAlign == 'left'){
32907             
32908                 if(this.labelWidth > 12){
32909                     label.style = "width: " + this.labelWidth + 'px';
32910                 }
32911
32912                 if(this.labelWidth < 13 && this.labelmd == 0){
32913                     this.labelmd = this.labelWidth;
32914                 }
32915
32916                 if(this.labellg > 0){
32917                     labelCls = ' col-lg-' + this.labellg;
32918                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32919                 }
32920
32921                 if(this.labelmd > 0){
32922                     labelCls = ' col-md-' + this.labelmd;
32923                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32924                 }
32925
32926                 if(this.labelsm > 0){
32927                     labelCls = ' col-sm-' + this.labelsm;
32928                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32929                 }
32930
32931                 if(this.labelxs > 0){
32932                     labelCls = ' col-xs-' + this.labelxs;
32933                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32934                 }
32935             }
32936             
32937             label.cls += ' ' + labelCls;
32938             
32939             cfg.cn.push(label);
32940         }
32941         
32942         Roo.each(['day', 'month', 'year'], function(t){
32943             cfg.cn.push({
32944                 tag : 'div',
32945                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32946             });
32947         }, this);
32948         
32949         return cfg;
32950     },
32951     
32952     inputEl: function ()
32953     {
32954         return this.el.select('.roo-date-split-field-group-value', true).first();
32955     },
32956     
32957     onRender : function(ct, position) 
32958     {
32959         var _this = this;
32960         
32961         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32962         
32963         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32964         
32965         this.dayField = new Roo.bootstrap.ComboBox({
32966             allowBlank : this.dayAllowBlank,
32967             alwaysQuery : true,
32968             displayField : 'value',
32969             editable : false,
32970             fieldLabel : '',
32971             forceSelection : true,
32972             mode : 'local',
32973             placeholder : this.dayPlaceholder,
32974             selectOnFocus : true,
32975             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32976             triggerAction : 'all',
32977             typeAhead : true,
32978             valueField : 'value',
32979             store : new Roo.data.SimpleStore({
32980                 data : (function() {    
32981                     var days = [];
32982                     _this.fireEvent('days', _this, days);
32983                     return days;
32984                 })(),
32985                 fields : [ 'value' ]
32986             }),
32987             listeners : {
32988                 select : function (_self, record, index)
32989                 {
32990                     _this.setValue(_this.getValue());
32991                 }
32992             }
32993         });
32994
32995         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32996         
32997         this.monthField = new Roo.bootstrap.MonthField({
32998             after : '<i class=\"fa fa-calendar\"></i>',
32999             allowBlank : this.monthAllowBlank,
33000             placeholder : this.monthPlaceholder,
33001             readOnly : true,
33002             listeners : {
33003                 render : function (_self)
33004                 {
33005                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33006                         e.preventDefault();
33007                         _self.focus();
33008                     });
33009                 },
33010                 select : function (_self, oldvalue, newvalue)
33011                 {
33012                     _this.setValue(_this.getValue());
33013                 }
33014             }
33015         });
33016         
33017         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33018         
33019         this.yearField = new Roo.bootstrap.ComboBox({
33020             allowBlank : this.yearAllowBlank,
33021             alwaysQuery : true,
33022             displayField : 'value',
33023             editable : false,
33024             fieldLabel : '',
33025             forceSelection : true,
33026             mode : 'local',
33027             placeholder : this.yearPlaceholder,
33028             selectOnFocus : true,
33029             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33030             triggerAction : 'all',
33031             typeAhead : true,
33032             valueField : 'value',
33033             store : new Roo.data.SimpleStore({
33034                 data : (function() {
33035                     var years = [];
33036                     _this.fireEvent('years', _this, years);
33037                     return years;
33038                 })(),
33039                 fields : [ 'value' ]
33040             }),
33041             listeners : {
33042                 select : function (_self, record, index)
33043                 {
33044                     _this.setValue(_this.getValue());
33045                 }
33046             }
33047         });
33048
33049         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33050     },
33051     
33052     setValue : function(v, format)
33053     {
33054         this.inputEl.dom.value = v;
33055         
33056         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33057         
33058         var d = Date.parseDate(v, f);
33059         
33060         if(!d){
33061             this.validate();
33062             return;
33063         }
33064         
33065         this.setDay(d.format(this.dayFormat));
33066         this.setMonth(d.format(this.monthFormat));
33067         this.setYear(d.format(this.yearFormat));
33068         
33069         this.validate();
33070         
33071         return;
33072     },
33073     
33074     setDay : function(v)
33075     {
33076         this.dayField.setValue(v);
33077         this.inputEl.dom.value = this.getValue();
33078         this.validate();
33079         return;
33080     },
33081     
33082     setMonth : function(v)
33083     {
33084         this.monthField.setValue(v, true);
33085         this.inputEl.dom.value = this.getValue();
33086         this.validate();
33087         return;
33088     },
33089     
33090     setYear : function(v)
33091     {
33092         this.yearField.setValue(v);
33093         this.inputEl.dom.value = this.getValue();
33094         this.validate();
33095         return;
33096     },
33097     
33098     getDay : function()
33099     {
33100         return this.dayField.getValue();
33101     },
33102     
33103     getMonth : function()
33104     {
33105         return this.monthField.getValue();
33106     },
33107     
33108     getYear : function()
33109     {
33110         return this.yearField.getValue();
33111     },
33112     
33113     getValue : function()
33114     {
33115         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33116         
33117         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33118         
33119         return date;
33120     },
33121     
33122     reset : function()
33123     {
33124         this.setDay('');
33125         this.setMonth('');
33126         this.setYear('');
33127         this.inputEl.dom.value = '';
33128         this.validate();
33129         return;
33130     },
33131     
33132     validate : function()
33133     {
33134         var d = this.dayField.validate();
33135         var m = this.monthField.validate();
33136         var y = this.yearField.validate();
33137         
33138         var valid = true;
33139         
33140         if(
33141                 (!this.dayAllowBlank && !d) ||
33142                 (!this.monthAllowBlank && !m) ||
33143                 (!this.yearAllowBlank && !y)
33144         ){
33145             valid = false;
33146         }
33147         
33148         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33149             return valid;
33150         }
33151         
33152         if(valid){
33153             this.markValid();
33154             return valid;
33155         }
33156         
33157         this.markInvalid();
33158         
33159         return valid;
33160     },
33161     
33162     markValid : function()
33163     {
33164         
33165         var label = this.el.select('label', true).first();
33166         var icon = this.el.select('i.fa-star', true).first();
33167
33168         if(label && icon){
33169             icon.remove();
33170         }
33171         
33172         this.fireEvent('valid', this);
33173     },
33174     
33175      /**
33176      * Mark this field as invalid
33177      * @param {String} msg The validation message
33178      */
33179     markInvalid : function(msg)
33180     {
33181         
33182         var label = this.el.select('label', true).first();
33183         var icon = this.el.select('i.fa-star', true).first();
33184
33185         if(label && !icon){
33186             this.el.select('.roo-date-split-field-label', true).createChild({
33187                 tag : 'i',
33188                 cls : 'text-danger fa fa-lg fa-star',
33189                 tooltip : 'This field is required',
33190                 style : 'margin-right:5px;'
33191             }, label, true);
33192         }
33193         
33194         this.fireEvent('invalid', this, msg);
33195     },
33196     
33197     clearInvalid : function()
33198     {
33199         var label = this.el.select('label', true).first();
33200         var icon = this.el.select('i.fa-star', true).first();
33201
33202         if(label && icon){
33203             icon.remove();
33204         }
33205         
33206         this.fireEvent('valid', this);
33207     },
33208     
33209     getName: function()
33210     {
33211         return this.name;
33212     }
33213     
33214 });
33215
33216  /**
33217  *
33218  * This is based on 
33219  * http://masonry.desandro.com
33220  *
33221  * The idea is to render all the bricks based on vertical width...
33222  *
33223  * The original code extends 'outlayer' - we might need to use that....
33224  * 
33225  */
33226
33227
33228 /**
33229  * @class Roo.bootstrap.LayoutMasonry
33230  * @extends Roo.bootstrap.Component
33231  * Bootstrap Layout Masonry class
33232  * 
33233  * @constructor
33234  * Create a new Element
33235  * @param {Object} config The config object
33236  */
33237
33238 Roo.bootstrap.LayoutMasonry = function(config){
33239     
33240     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33241     
33242     this.bricks = [];
33243     
33244     Roo.bootstrap.LayoutMasonry.register(this);
33245     
33246     this.addEvents({
33247         // raw events
33248         /**
33249          * @event layout
33250          * Fire after layout the items
33251          * @param {Roo.bootstrap.LayoutMasonry} this
33252          * @param {Roo.EventObject} e
33253          */
33254         "layout" : true
33255     });
33256     
33257 };
33258
33259 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33260     
33261     /**
33262      * @cfg {Boolean} isLayoutInstant = no animation?
33263      */   
33264     isLayoutInstant : false, // needed?
33265    
33266     /**
33267      * @cfg {Number} boxWidth  width of the columns
33268      */   
33269     boxWidth : 450,
33270     
33271       /**
33272      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33273      */   
33274     boxHeight : 0,
33275     
33276     /**
33277      * @cfg {Number} padWidth padding below box..
33278      */   
33279     padWidth : 10, 
33280     
33281     /**
33282      * @cfg {Number} gutter gutter width..
33283      */   
33284     gutter : 10,
33285     
33286      /**
33287      * @cfg {Number} maxCols maximum number of columns
33288      */   
33289     
33290     maxCols: 0,
33291     
33292     /**
33293      * @cfg {Boolean} isAutoInitial defalut true
33294      */   
33295     isAutoInitial : true, 
33296     
33297     containerWidth: 0,
33298     
33299     /**
33300      * @cfg {Boolean} isHorizontal defalut false
33301      */   
33302     isHorizontal : false, 
33303
33304     currentSize : null,
33305     
33306     tag: 'div',
33307     
33308     cls: '',
33309     
33310     bricks: null, //CompositeElement
33311     
33312     cols : 1,
33313     
33314     _isLayoutInited : false,
33315     
33316 //    isAlternative : false, // only use for vertical layout...
33317     
33318     /**
33319      * @cfg {Number} alternativePadWidth padding below box..
33320      */   
33321     alternativePadWidth : 50,
33322     
33323     selectedBrick : [],
33324     
33325     getAutoCreate : function(){
33326         
33327         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33328         
33329         var cfg = {
33330             tag: this.tag,
33331             cls: 'blog-masonary-wrapper ' + this.cls,
33332             cn : {
33333                 cls : 'mas-boxes masonary'
33334             }
33335         };
33336         
33337         return cfg;
33338     },
33339     
33340     getChildContainer: function( )
33341     {
33342         if (this.boxesEl) {
33343             return this.boxesEl;
33344         }
33345         
33346         this.boxesEl = this.el.select('.mas-boxes').first();
33347         
33348         return this.boxesEl;
33349     },
33350     
33351     
33352     initEvents : function()
33353     {
33354         var _this = this;
33355         
33356         if(this.isAutoInitial){
33357             Roo.log('hook children rendered');
33358             this.on('childrenrendered', function() {
33359                 Roo.log('children rendered');
33360                 _this.initial();
33361             } ,this);
33362         }
33363     },
33364     
33365     initial : function()
33366     {
33367         this.selectedBrick = [];
33368         
33369         this.currentSize = this.el.getBox(true);
33370         
33371         Roo.EventManager.onWindowResize(this.resize, this); 
33372
33373         if(!this.isAutoInitial){
33374             this.layout();
33375             return;
33376         }
33377         
33378         this.layout();
33379         
33380         return;
33381         //this.layout.defer(500,this);
33382         
33383     },
33384     
33385     resize : function()
33386     {
33387         var cs = this.el.getBox(true);
33388         
33389         if (
33390                 this.currentSize.width == cs.width && 
33391                 this.currentSize.x == cs.x && 
33392                 this.currentSize.height == cs.height && 
33393                 this.currentSize.y == cs.y 
33394         ) {
33395             Roo.log("no change in with or X or Y");
33396             return;
33397         }
33398         
33399         this.currentSize = cs;
33400         
33401         this.layout();
33402         
33403     },
33404     
33405     layout : function()
33406     {   
33407         this._resetLayout();
33408         
33409         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33410         
33411         this.layoutItems( isInstant );
33412       
33413         this._isLayoutInited = true;
33414         
33415         this.fireEvent('layout', this);
33416         
33417     },
33418     
33419     _resetLayout : function()
33420     {
33421         if(this.isHorizontal){
33422             this.horizontalMeasureColumns();
33423             return;
33424         }
33425         
33426         this.verticalMeasureColumns();
33427         
33428     },
33429     
33430     verticalMeasureColumns : function()
33431     {
33432         this.getContainerWidth();
33433         
33434 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33435 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33436 //            return;
33437 //        }
33438         
33439         var boxWidth = this.boxWidth + this.padWidth;
33440         
33441         if(this.containerWidth < this.boxWidth){
33442             boxWidth = this.containerWidth
33443         }
33444         
33445         var containerWidth = this.containerWidth;
33446         
33447         var cols = Math.floor(containerWidth / boxWidth);
33448         
33449         this.cols = Math.max( cols, 1 );
33450         
33451         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33452         
33453         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33454         
33455         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33456         
33457         this.colWidth = boxWidth + avail - this.padWidth;
33458         
33459         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33460         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33461     },
33462     
33463     horizontalMeasureColumns : function()
33464     {
33465         this.getContainerWidth();
33466         
33467         var boxWidth = this.boxWidth;
33468         
33469         if(this.containerWidth < boxWidth){
33470             boxWidth = this.containerWidth;
33471         }
33472         
33473         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33474         
33475         this.el.setHeight(boxWidth);
33476         
33477     },
33478     
33479     getContainerWidth : function()
33480     {
33481         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33482     },
33483     
33484     layoutItems : function( isInstant )
33485     {
33486         Roo.log(this.bricks);
33487         
33488         var items = Roo.apply([], this.bricks);
33489         
33490         if(this.isHorizontal){
33491             this._horizontalLayoutItems( items , isInstant );
33492             return;
33493         }
33494         
33495 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33496 //            this._verticalAlternativeLayoutItems( items , isInstant );
33497 //            return;
33498 //        }
33499         
33500         this._verticalLayoutItems( items , isInstant );
33501         
33502     },
33503     
33504     _verticalLayoutItems : function ( items , isInstant)
33505     {
33506         if ( !items || !items.length ) {
33507             return;
33508         }
33509         
33510         var standard = [
33511             ['xs', 'xs', 'xs', 'tall'],
33512             ['xs', 'xs', 'tall'],
33513             ['xs', 'xs', 'sm'],
33514             ['xs', 'xs', 'xs'],
33515             ['xs', 'tall'],
33516             ['xs', 'sm'],
33517             ['xs', 'xs'],
33518             ['xs'],
33519             
33520             ['sm', 'xs', 'xs'],
33521             ['sm', 'xs'],
33522             ['sm'],
33523             
33524             ['tall', 'xs', 'xs', 'xs'],
33525             ['tall', 'xs', 'xs'],
33526             ['tall', 'xs'],
33527             ['tall']
33528             
33529         ];
33530         
33531         var queue = [];
33532         
33533         var boxes = [];
33534         
33535         var box = [];
33536         
33537         Roo.each(items, function(item, k){
33538             
33539             switch (item.size) {
33540                 // these layouts take up a full box,
33541                 case 'md' :
33542                 case 'md-left' :
33543                 case 'md-right' :
33544                 case 'wide' :
33545                     
33546                     if(box.length){
33547                         boxes.push(box);
33548                         box = [];
33549                     }
33550                     
33551                     boxes.push([item]);
33552                     
33553                     break;
33554                     
33555                 case 'xs' :
33556                 case 'sm' :
33557                 case 'tall' :
33558                     
33559                     box.push(item);
33560                     
33561                     break;
33562                 default :
33563                     break;
33564                     
33565             }
33566             
33567         }, this);
33568         
33569         if(box.length){
33570             boxes.push(box);
33571             box = [];
33572         }
33573         
33574         var filterPattern = function(box, length)
33575         {
33576             if(!box.length){
33577                 return;
33578             }
33579             
33580             var match = false;
33581             
33582             var pattern = box.slice(0, length);
33583             
33584             var format = [];
33585             
33586             Roo.each(pattern, function(i){
33587                 format.push(i.size);
33588             }, this);
33589             
33590             Roo.each(standard, function(s){
33591                 
33592                 if(String(s) != String(format)){
33593                     return;
33594                 }
33595                 
33596                 match = true;
33597                 return false;
33598                 
33599             }, this);
33600             
33601             if(!match && length == 1){
33602                 return;
33603             }
33604             
33605             if(!match){
33606                 filterPattern(box, length - 1);
33607                 return;
33608             }
33609                 
33610             queue.push(pattern);
33611
33612             box = box.slice(length, box.length);
33613
33614             filterPattern(box, 4);
33615
33616             return;
33617             
33618         }
33619         
33620         Roo.each(boxes, function(box, k){
33621             
33622             if(!box.length){
33623                 return;
33624             }
33625             
33626             if(box.length == 1){
33627                 queue.push(box);
33628                 return;
33629             }
33630             
33631             filterPattern(box, 4);
33632             
33633         }, this);
33634         
33635         this._processVerticalLayoutQueue( queue, isInstant );
33636         
33637     },
33638     
33639 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33640 //    {
33641 //        if ( !items || !items.length ) {
33642 //            return;
33643 //        }
33644 //
33645 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33646 //        
33647 //    },
33648     
33649     _horizontalLayoutItems : function ( items , isInstant)
33650     {
33651         if ( !items || !items.length || items.length < 3) {
33652             return;
33653         }
33654         
33655         items.reverse();
33656         
33657         var eItems = items.slice(0, 3);
33658         
33659         items = items.slice(3, items.length);
33660         
33661         var standard = [
33662             ['xs', 'xs', 'xs', 'wide'],
33663             ['xs', 'xs', 'wide'],
33664             ['xs', 'xs', 'sm'],
33665             ['xs', 'xs', 'xs'],
33666             ['xs', 'wide'],
33667             ['xs', 'sm'],
33668             ['xs', 'xs'],
33669             ['xs'],
33670             
33671             ['sm', 'xs', 'xs'],
33672             ['sm', 'xs'],
33673             ['sm'],
33674             
33675             ['wide', 'xs', 'xs', 'xs'],
33676             ['wide', 'xs', 'xs'],
33677             ['wide', 'xs'],
33678             ['wide'],
33679             
33680             ['wide-thin']
33681         ];
33682         
33683         var queue = [];
33684         
33685         var boxes = [];
33686         
33687         var box = [];
33688         
33689         Roo.each(items, function(item, k){
33690             
33691             switch (item.size) {
33692                 case 'md' :
33693                 case 'md-left' :
33694                 case 'md-right' :
33695                 case 'tall' :
33696                     
33697                     if(box.length){
33698                         boxes.push(box);
33699                         box = [];
33700                     }
33701                     
33702                     boxes.push([item]);
33703                     
33704                     break;
33705                     
33706                 case 'xs' :
33707                 case 'sm' :
33708                 case 'wide' :
33709                 case 'wide-thin' :
33710                     
33711                     box.push(item);
33712                     
33713                     break;
33714                 default :
33715                     break;
33716                     
33717             }
33718             
33719         }, this);
33720         
33721         if(box.length){
33722             boxes.push(box);
33723             box = [];
33724         }
33725         
33726         var filterPattern = function(box, length)
33727         {
33728             if(!box.length){
33729                 return;
33730             }
33731             
33732             var match = false;
33733             
33734             var pattern = box.slice(0, length);
33735             
33736             var format = [];
33737             
33738             Roo.each(pattern, function(i){
33739                 format.push(i.size);
33740             }, this);
33741             
33742             Roo.each(standard, function(s){
33743                 
33744                 if(String(s) != String(format)){
33745                     return;
33746                 }
33747                 
33748                 match = true;
33749                 return false;
33750                 
33751             }, this);
33752             
33753             if(!match && length == 1){
33754                 return;
33755             }
33756             
33757             if(!match){
33758                 filterPattern(box, length - 1);
33759                 return;
33760             }
33761                 
33762             queue.push(pattern);
33763
33764             box = box.slice(length, box.length);
33765
33766             filterPattern(box, 4);
33767
33768             return;
33769             
33770         }
33771         
33772         Roo.each(boxes, function(box, k){
33773             
33774             if(!box.length){
33775                 return;
33776             }
33777             
33778             if(box.length == 1){
33779                 queue.push(box);
33780                 return;
33781             }
33782             
33783             filterPattern(box, 4);
33784             
33785         }, this);
33786         
33787         
33788         var prune = [];
33789         
33790         var pos = this.el.getBox(true);
33791         
33792         var minX = pos.x;
33793         
33794         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33795         
33796         var hit_end = false;
33797         
33798         Roo.each(queue, function(box){
33799             
33800             if(hit_end){
33801                 
33802                 Roo.each(box, function(b){
33803                 
33804                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33805                     b.el.hide();
33806
33807                 }, this);
33808
33809                 return;
33810             }
33811             
33812             var mx = 0;
33813             
33814             Roo.each(box, function(b){
33815                 
33816                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33817                 b.el.show();
33818
33819                 mx = Math.max(mx, b.x);
33820                 
33821             }, this);
33822             
33823             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33824             
33825             if(maxX < minX){
33826                 
33827                 Roo.each(box, function(b){
33828                 
33829                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33830                     b.el.hide();
33831                     
33832                 }, this);
33833                 
33834                 hit_end = true;
33835                 
33836                 return;
33837             }
33838             
33839             prune.push(box);
33840             
33841         }, this);
33842         
33843         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33844     },
33845     
33846     /** Sets position of item in DOM
33847     * @param {Element} item
33848     * @param {Number} x - horizontal position
33849     * @param {Number} y - vertical position
33850     * @param {Boolean} isInstant - disables transitions
33851     */
33852     _processVerticalLayoutQueue : function( queue, isInstant )
33853     {
33854         var pos = this.el.getBox(true);
33855         var x = pos.x;
33856         var y = pos.y;
33857         var maxY = [];
33858         
33859         for (var i = 0; i < this.cols; i++){
33860             maxY[i] = pos.y;
33861         }
33862         
33863         Roo.each(queue, function(box, k){
33864             
33865             var col = k % this.cols;
33866             
33867             Roo.each(box, function(b,kk){
33868                 
33869                 b.el.position('absolute');
33870                 
33871                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33872                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33873                 
33874                 if(b.size == 'md-left' || b.size == 'md-right'){
33875                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33876                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33877                 }
33878                 
33879                 b.el.setWidth(width);
33880                 b.el.setHeight(height);
33881                 // iframe?
33882                 b.el.select('iframe',true).setSize(width,height);
33883                 
33884             }, this);
33885             
33886             for (var i = 0; i < this.cols; i++){
33887                 
33888                 if(maxY[i] < maxY[col]){
33889                     col = i;
33890                     continue;
33891                 }
33892                 
33893                 col = Math.min(col, i);
33894                 
33895             }
33896             
33897             x = pos.x + col * (this.colWidth + this.padWidth);
33898             
33899             y = maxY[col];
33900             
33901             var positions = [];
33902             
33903             switch (box.length){
33904                 case 1 :
33905                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33906                     break;
33907                 case 2 :
33908                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33909                     break;
33910                 case 3 :
33911                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33912                     break;
33913                 case 4 :
33914                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33915                     break;
33916                 default :
33917                     break;
33918             }
33919             
33920             Roo.each(box, function(b,kk){
33921                 
33922                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33923                 
33924                 var sz = b.el.getSize();
33925                 
33926                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33927                 
33928             }, this);
33929             
33930         }, this);
33931         
33932         var mY = 0;
33933         
33934         for (var i = 0; i < this.cols; i++){
33935             mY = Math.max(mY, maxY[i]);
33936         }
33937         
33938         this.el.setHeight(mY - pos.y);
33939         
33940     },
33941     
33942 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33943 //    {
33944 //        var pos = this.el.getBox(true);
33945 //        var x = pos.x;
33946 //        var y = pos.y;
33947 //        var maxX = pos.right;
33948 //        
33949 //        var maxHeight = 0;
33950 //        
33951 //        Roo.each(items, function(item, k){
33952 //            
33953 //            var c = k % 2;
33954 //            
33955 //            item.el.position('absolute');
33956 //                
33957 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33958 //
33959 //            item.el.setWidth(width);
33960 //
33961 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33962 //
33963 //            item.el.setHeight(height);
33964 //            
33965 //            if(c == 0){
33966 //                item.el.setXY([x, y], isInstant ? false : true);
33967 //            } else {
33968 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33969 //            }
33970 //            
33971 //            y = y + height + this.alternativePadWidth;
33972 //            
33973 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33974 //            
33975 //        }, this);
33976 //        
33977 //        this.el.setHeight(maxHeight);
33978 //        
33979 //    },
33980     
33981     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33982     {
33983         var pos = this.el.getBox(true);
33984         
33985         var minX = pos.x;
33986         var minY = pos.y;
33987         
33988         var maxX = pos.right;
33989         
33990         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33991         
33992         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33993         
33994         Roo.each(queue, function(box, k){
33995             
33996             Roo.each(box, function(b, kk){
33997                 
33998                 b.el.position('absolute');
33999                 
34000                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34001                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34002                 
34003                 if(b.size == 'md-left' || b.size == 'md-right'){
34004                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34005                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34006                 }
34007                 
34008                 b.el.setWidth(width);
34009                 b.el.setHeight(height);
34010                 
34011             }, this);
34012             
34013             if(!box.length){
34014                 return;
34015             }
34016             
34017             var positions = [];
34018             
34019             switch (box.length){
34020                 case 1 :
34021                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34022                     break;
34023                 case 2 :
34024                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34025                     break;
34026                 case 3 :
34027                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34028                     break;
34029                 case 4 :
34030                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34031                     break;
34032                 default :
34033                     break;
34034             }
34035             
34036             Roo.each(box, function(b,kk){
34037                 
34038                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34039                 
34040                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34041                 
34042             }, this);
34043             
34044         }, this);
34045         
34046     },
34047     
34048     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34049     {
34050         Roo.each(eItems, function(b,k){
34051             
34052             b.size = (k == 0) ? 'sm' : 'xs';
34053             b.x = (k == 0) ? 2 : 1;
34054             b.y = (k == 0) ? 2 : 1;
34055             
34056             b.el.position('absolute');
34057             
34058             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34059                 
34060             b.el.setWidth(width);
34061             
34062             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34063             
34064             b.el.setHeight(height);
34065             
34066         }, this);
34067
34068         var positions = [];
34069         
34070         positions.push({
34071             x : maxX - this.unitWidth * 2 - this.gutter,
34072             y : minY
34073         });
34074         
34075         positions.push({
34076             x : maxX - this.unitWidth,
34077             y : minY + (this.unitWidth + this.gutter) * 2
34078         });
34079         
34080         positions.push({
34081             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34082             y : minY
34083         });
34084         
34085         Roo.each(eItems, function(b,k){
34086             
34087             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34088
34089         }, this);
34090         
34091     },
34092     
34093     getVerticalOneBoxColPositions : function(x, y, box)
34094     {
34095         var pos = [];
34096         
34097         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34098         
34099         if(box[0].size == 'md-left'){
34100             rand = 0;
34101         }
34102         
34103         if(box[0].size == 'md-right'){
34104             rand = 1;
34105         }
34106         
34107         pos.push({
34108             x : x + (this.unitWidth + this.gutter) * rand,
34109             y : y
34110         });
34111         
34112         return pos;
34113     },
34114     
34115     getVerticalTwoBoxColPositions : function(x, y, box)
34116     {
34117         var pos = [];
34118         
34119         if(box[0].size == 'xs'){
34120             
34121             pos.push({
34122                 x : x,
34123                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34124             });
34125
34126             pos.push({
34127                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34128                 y : y
34129             });
34130             
34131             return pos;
34132             
34133         }
34134         
34135         pos.push({
34136             x : x,
34137             y : y
34138         });
34139
34140         pos.push({
34141             x : x + (this.unitWidth + this.gutter) * 2,
34142             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34143         });
34144         
34145         return pos;
34146         
34147     },
34148     
34149     getVerticalThreeBoxColPositions : function(x, y, box)
34150     {
34151         var pos = [];
34152         
34153         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34154             
34155             pos.push({
34156                 x : x,
34157                 y : y
34158             });
34159
34160             pos.push({
34161                 x : x + (this.unitWidth + this.gutter) * 1,
34162                 y : y
34163             });
34164             
34165             pos.push({
34166                 x : x + (this.unitWidth + this.gutter) * 2,
34167                 y : y
34168             });
34169             
34170             return pos;
34171             
34172         }
34173         
34174         if(box[0].size == 'xs' && box[1].size == 'xs'){
34175             
34176             pos.push({
34177                 x : x,
34178                 y : y
34179             });
34180
34181             pos.push({
34182                 x : x,
34183                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34184             });
34185             
34186             pos.push({
34187                 x : x + (this.unitWidth + this.gutter) * 1,
34188                 y : y
34189             });
34190             
34191             return pos;
34192             
34193         }
34194         
34195         pos.push({
34196             x : x,
34197             y : y
34198         });
34199
34200         pos.push({
34201             x : x + (this.unitWidth + this.gutter) * 2,
34202             y : y
34203         });
34204
34205         pos.push({
34206             x : x + (this.unitWidth + this.gutter) * 2,
34207             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34208         });
34209             
34210         return pos;
34211         
34212     },
34213     
34214     getVerticalFourBoxColPositions : function(x, y, box)
34215     {
34216         var pos = [];
34217         
34218         if(box[0].size == 'xs'){
34219             
34220             pos.push({
34221                 x : x,
34222                 y : y
34223             });
34224
34225             pos.push({
34226                 x : x,
34227                 y : y + (this.unitHeight + this.gutter) * 1
34228             });
34229             
34230             pos.push({
34231                 x : x,
34232                 y : y + (this.unitHeight + this.gutter) * 2
34233             });
34234             
34235             pos.push({
34236                 x : x + (this.unitWidth + this.gutter) * 1,
34237                 y : y
34238             });
34239             
34240             return pos;
34241             
34242         }
34243         
34244         pos.push({
34245             x : x,
34246             y : y
34247         });
34248
34249         pos.push({
34250             x : x + (this.unitWidth + this.gutter) * 2,
34251             y : y
34252         });
34253
34254         pos.push({
34255             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34256             y : y + (this.unitHeight + this.gutter) * 1
34257         });
34258
34259         pos.push({
34260             x : x + (this.unitWidth + this.gutter) * 2,
34261             y : y + (this.unitWidth + this.gutter) * 2
34262         });
34263
34264         return pos;
34265         
34266     },
34267     
34268     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34269     {
34270         var pos = [];
34271         
34272         if(box[0].size == 'md-left'){
34273             pos.push({
34274                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34275                 y : minY
34276             });
34277             
34278             return pos;
34279         }
34280         
34281         if(box[0].size == 'md-right'){
34282             pos.push({
34283                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34284                 y : minY + (this.unitWidth + this.gutter) * 1
34285             });
34286             
34287             return pos;
34288         }
34289         
34290         var rand = Math.floor(Math.random() * (4 - box[0].y));
34291         
34292         pos.push({
34293             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34294             y : minY + (this.unitWidth + this.gutter) * rand
34295         });
34296         
34297         return pos;
34298         
34299     },
34300     
34301     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34302     {
34303         var pos = [];
34304         
34305         if(box[0].size == 'xs'){
34306             
34307             pos.push({
34308                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34309                 y : minY
34310             });
34311
34312             pos.push({
34313                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34314                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34315             });
34316             
34317             return pos;
34318             
34319         }
34320         
34321         pos.push({
34322             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34323             y : minY
34324         });
34325
34326         pos.push({
34327             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34328             y : minY + (this.unitWidth + this.gutter) * 2
34329         });
34330         
34331         return pos;
34332         
34333     },
34334     
34335     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34336     {
34337         var pos = [];
34338         
34339         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34340             
34341             pos.push({
34342                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34343                 y : minY
34344             });
34345
34346             pos.push({
34347                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34348                 y : minY + (this.unitWidth + this.gutter) * 1
34349             });
34350             
34351             pos.push({
34352                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34353                 y : minY + (this.unitWidth + this.gutter) * 2
34354             });
34355             
34356             return pos;
34357             
34358         }
34359         
34360         if(box[0].size == 'xs' && box[1].size == 'xs'){
34361             
34362             pos.push({
34363                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34364                 y : minY
34365             });
34366
34367             pos.push({
34368                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34369                 y : minY
34370             });
34371             
34372             pos.push({
34373                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34374                 y : minY + (this.unitWidth + this.gutter) * 1
34375             });
34376             
34377             return pos;
34378             
34379         }
34380         
34381         pos.push({
34382             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34383             y : minY
34384         });
34385
34386         pos.push({
34387             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34388             y : minY + (this.unitWidth + this.gutter) * 2
34389         });
34390
34391         pos.push({
34392             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34393             y : minY + (this.unitWidth + this.gutter) * 2
34394         });
34395             
34396         return pos;
34397         
34398     },
34399     
34400     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34401     {
34402         var pos = [];
34403         
34404         if(box[0].size == 'xs'){
34405             
34406             pos.push({
34407                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34408                 y : minY
34409             });
34410
34411             pos.push({
34412                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34413                 y : minY
34414             });
34415             
34416             pos.push({
34417                 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),
34418                 y : minY
34419             });
34420             
34421             pos.push({
34422                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34423                 y : minY + (this.unitWidth + this.gutter) * 1
34424             });
34425             
34426             return pos;
34427             
34428         }
34429         
34430         pos.push({
34431             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34432             y : minY
34433         });
34434         
34435         pos.push({
34436             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34437             y : minY + (this.unitWidth + this.gutter) * 2
34438         });
34439         
34440         pos.push({
34441             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34442             y : minY + (this.unitWidth + this.gutter) * 2
34443         });
34444         
34445         pos.push({
34446             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),
34447             y : minY + (this.unitWidth + this.gutter) * 2
34448         });
34449
34450         return pos;
34451         
34452     },
34453     
34454     /**
34455     * remove a Masonry Brick
34456     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34457     */
34458     removeBrick : function(brick_id)
34459     {
34460         if (!brick_id) {
34461             return;
34462         }
34463         
34464         for (var i = 0; i<this.bricks.length; i++) {
34465             if (this.bricks[i].id == brick_id) {
34466                 this.bricks.splice(i,1);
34467                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34468                 this.initial();
34469             }
34470         }
34471     },
34472     
34473     /**
34474     * adds a Masonry Brick
34475     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34476     */
34477     addBrick : function(cfg)
34478     {
34479         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34480         //this.register(cn);
34481         cn.parentId = this.id;
34482         cn.render(this.el);
34483         return cn;
34484     },
34485     
34486     /**
34487     * register a Masonry Brick
34488     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34489     */
34490     
34491     register : function(brick)
34492     {
34493         this.bricks.push(brick);
34494         brick.masonryId = this.id;
34495     },
34496     
34497     /**
34498     * clear all the Masonry Brick
34499     */
34500     clearAll : function()
34501     {
34502         this.bricks = [];
34503         //this.getChildContainer().dom.innerHTML = "";
34504         this.el.dom.innerHTML = '';
34505     },
34506     
34507     getSelected : function()
34508     {
34509         if (!this.selectedBrick) {
34510             return false;
34511         }
34512         
34513         return this.selectedBrick;
34514     }
34515 });
34516
34517 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34518     
34519     groups: {},
34520      /**
34521     * register a Masonry Layout
34522     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34523     */
34524     
34525     register : function(layout)
34526     {
34527         this.groups[layout.id] = layout;
34528     },
34529     /**
34530     * fetch a  Masonry Layout based on the masonry layout ID
34531     * @param {string} the masonry layout to add
34532     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34533     */
34534     
34535     get: function(layout_id) {
34536         if (typeof(this.groups[layout_id]) == 'undefined') {
34537             return false;
34538         }
34539         return this.groups[layout_id] ;
34540     }
34541     
34542     
34543     
34544 });
34545
34546  
34547
34548  /**
34549  *
34550  * This is based on 
34551  * http://masonry.desandro.com
34552  *
34553  * The idea is to render all the bricks based on vertical width...
34554  *
34555  * The original code extends 'outlayer' - we might need to use that....
34556  * 
34557  */
34558
34559
34560 /**
34561  * @class Roo.bootstrap.LayoutMasonryAuto
34562  * @extends Roo.bootstrap.Component
34563  * Bootstrap Layout Masonry class
34564  * 
34565  * @constructor
34566  * Create a new Element
34567  * @param {Object} config The config object
34568  */
34569
34570 Roo.bootstrap.LayoutMasonryAuto = function(config){
34571     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34572 };
34573
34574 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34575     
34576       /**
34577      * @cfg {Boolean} isFitWidth  - resize the width..
34578      */   
34579     isFitWidth : false,  // options..
34580     /**
34581      * @cfg {Boolean} isOriginLeft = left align?
34582      */   
34583     isOriginLeft : true,
34584     /**
34585      * @cfg {Boolean} isOriginTop = top align?
34586      */   
34587     isOriginTop : false,
34588     /**
34589      * @cfg {Boolean} isLayoutInstant = no animation?
34590      */   
34591     isLayoutInstant : false, // needed?
34592     /**
34593      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34594      */   
34595     isResizingContainer : true,
34596     /**
34597      * @cfg {Number} columnWidth  width of the columns 
34598      */   
34599     
34600     columnWidth : 0,
34601     
34602     /**
34603      * @cfg {Number} maxCols maximum number of columns
34604      */   
34605     
34606     maxCols: 0,
34607     /**
34608      * @cfg {Number} padHeight padding below box..
34609      */   
34610     
34611     padHeight : 10, 
34612     
34613     /**
34614      * @cfg {Boolean} isAutoInitial defalut true
34615      */   
34616     
34617     isAutoInitial : true, 
34618     
34619     // private?
34620     gutter : 0,
34621     
34622     containerWidth: 0,
34623     initialColumnWidth : 0,
34624     currentSize : null,
34625     
34626     colYs : null, // array.
34627     maxY : 0,
34628     padWidth: 10,
34629     
34630     
34631     tag: 'div',
34632     cls: '',
34633     bricks: null, //CompositeElement
34634     cols : 0, // array?
34635     // element : null, // wrapped now this.el
34636     _isLayoutInited : null, 
34637     
34638     
34639     getAutoCreate : function(){
34640         
34641         var cfg = {
34642             tag: this.tag,
34643             cls: 'blog-masonary-wrapper ' + this.cls,
34644             cn : {
34645                 cls : 'mas-boxes masonary'
34646             }
34647         };
34648         
34649         return cfg;
34650     },
34651     
34652     getChildContainer: function( )
34653     {
34654         if (this.boxesEl) {
34655             return this.boxesEl;
34656         }
34657         
34658         this.boxesEl = this.el.select('.mas-boxes').first();
34659         
34660         return this.boxesEl;
34661     },
34662     
34663     
34664     initEvents : function()
34665     {
34666         var _this = this;
34667         
34668         if(this.isAutoInitial){
34669             Roo.log('hook children rendered');
34670             this.on('childrenrendered', function() {
34671                 Roo.log('children rendered');
34672                 _this.initial();
34673             } ,this);
34674         }
34675         
34676     },
34677     
34678     initial : function()
34679     {
34680         this.reloadItems();
34681
34682         this.currentSize = this.el.getBox(true);
34683
34684         /// was window resize... - let's see if this works..
34685         Roo.EventManager.onWindowResize(this.resize, this); 
34686
34687         if(!this.isAutoInitial){
34688             this.layout();
34689             return;
34690         }
34691         
34692         this.layout.defer(500,this);
34693     },
34694     
34695     reloadItems: function()
34696     {
34697         this.bricks = this.el.select('.masonry-brick', true);
34698         
34699         this.bricks.each(function(b) {
34700             //Roo.log(b.getSize());
34701             if (!b.attr('originalwidth')) {
34702                 b.attr('originalwidth',  b.getSize().width);
34703             }
34704             
34705         });
34706         
34707         Roo.log(this.bricks.elements.length);
34708     },
34709     
34710     resize : function()
34711     {
34712         Roo.log('resize');
34713         var cs = this.el.getBox(true);
34714         
34715         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34716             Roo.log("no change in with or X");
34717             return;
34718         }
34719         this.currentSize = cs;
34720         this.layout();
34721     },
34722     
34723     layout : function()
34724     {
34725          Roo.log('layout');
34726         this._resetLayout();
34727         //this._manageStamps();
34728       
34729         // don't animate first layout
34730         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34731         this.layoutItems( isInstant );
34732       
34733         // flag for initalized
34734         this._isLayoutInited = true;
34735     },
34736     
34737     layoutItems : function( isInstant )
34738     {
34739         //var items = this._getItemsForLayout( this.items );
34740         // original code supports filtering layout items.. we just ignore it..
34741         
34742         this._layoutItems( this.bricks , isInstant );
34743       
34744         this._postLayout();
34745     },
34746     _layoutItems : function ( items , isInstant)
34747     {
34748        //this.fireEvent( 'layout', this, items );
34749     
34750
34751         if ( !items || !items.elements.length ) {
34752           // no items, emit event with empty array
34753             return;
34754         }
34755
34756         var queue = [];
34757         items.each(function(item) {
34758             Roo.log("layout item");
34759             Roo.log(item);
34760             // get x/y object from method
34761             var position = this._getItemLayoutPosition( item );
34762             // enqueue
34763             position.item = item;
34764             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34765             queue.push( position );
34766         }, this);
34767       
34768         this._processLayoutQueue( queue );
34769     },
34770     /** Sets position of item in DOM
34771     * @param {Element} item
34772     * @param {Number} x - horizontal position
34773     * @param {Number} y - vertical position
34774     * @param {Boolean} isInstant - disables transitions
34775     */
34776     _processLayoutQueue : function( queue )
34777     {
34778         for ( var i=0, len = queue.length; i < len; i++ ) {
34779             var obj = queue[i];
34780             obj.item.position('absolute');
34781             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34782         }
34783     },
34784       
34785     
34786     /**
34787     * Any logic you want to do after each layout,
34788     * i.e. size the container
34789     */
34790     _postLayout : function()
34791     {
34792         this.resizeContainer();
34793     },
34794     
34795     resizeContainer : function()
34796     {
34797         if ( !this.isResizingContainer ) {
34798             return;
34799         }
34800         var size = this._getContainerSize();
34801         if ( size ) {
34802             this.el.setSize(size.width,size.height);
34803             this.boxesEl.setSize(size.width,size.height);
34804         }
34805     },
34806     
34807     
34808     
34809     _resetLayout : function()
34810     {
34811         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34812         this.colWidth = this.el.getWidth();
34813         //this.gutter = this.el.getWidth(); 
34814         
34815         this.measureColumns();
34816
34817         // reset column Y
34818         var i = this.cols;
34819         this.colYs = [];
34820         while (i--) {
34821             this.colYs.push( 0 );
34822         }
34823     
34824         this.maxY = 0;
34825     },
34826
34827     measureColumns : function()
34828     {
34829         this.getContainerWidth();
34830       // if columnWidth is 0, default to outerWidth of first item
34831         if ( !this.columnWidth ) {
34832             var firstItem = this.bricks.first();
34833             Roo.log(firstItem);
34834             this.columnWidth  = this.containerWidth;
34835             if (firstItem && firstItem.attr('originalwidth') ) {
34836                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34837             }
34838             // columnWidth fall back to item of first element
34839             Roo.log("set column width?");
34840                         this.initialColumnWidth = this.columnWidth  ;
34841
34842             // if first elem has no width, default to size of container
34843             
34844         }
34845         
34846         
34847         if (this.initialColumnWidth) {
34848             this.columnWidth = this.initialColumnWidth;
34849         }
34850         
34851         
34852             
34853         // column width is fixed at the top - however if container width get's smaller we should
34854         // reduce it...
34855         
34856         // this bit calcs how man columns..
34857             
34858         var columnWidth = this.columnWidth += this.gutter;
34859       
34860         // calculate columns
34861         var containerWidth = this.containerWidth + this.gutter;
34862         
34863         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34864         // fix rounding errors, typically with gutters
34865         var excess = columnWidth - containerWidth % columnWidth;
34866         
34867         
34868         // if overshoot is less than a pixel, round up, otherwise floor it
34869         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34870         cols = Math[ mathMethod ]( cols );
34871         this.cols = Math.max( cols, 1 );
34872         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34873         
34874          // padding positioning..
34875         var totalColWidth = this.cols * this.columnWidth;
34876         var padavail = this.containerWidth - totalColWidth;
34877         // so for 2 columns - we need 3 'pads'
34878         
34879         var padNeeded = (1+this.cols) * this.padWidth;
34880         
34881         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34882         
34883         this.columnWidth += padExtra
34884         //this.padWidth = Math.floor(padavail /  ( this.cols));
34885         
34886         // adjust colum width so that padding is fixed??
34887         
34888         // we have 3 columns ... total = width * 3
34889         // we have X left over... that should be used by 
34890         
34891         //if (this.expandC) {
34892             
34893         //}
34894         
34895         
34896         
34897     },
34898     
34899     getContainerWidth : function()
34900     {
34901        /* // container is parent if fit width
34902         var container = this.isFitWidth ? this.element.parentNode : this.element;
34903         // check that this.size and size are there
34904         // IE8 triggers resize on body size change, so they might not be
34905         
34906         var size = getSize( container );  //FIXME
34907         this.containerWidth = size && size.innerWidth; //FIXME
34908         */
34909          
34910         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34911         
34912     },
34913     
34914     _getItemLayoutPosition : function( item )  // what is item?
34915     {
34916         // we resize the item to our columnWidth..
34917       
34918         item.setWidth(this.columnWidth);
34919         item.autoBoxAdjust  = false;
34920         
34921         var sz = item.getSize();
34922  
34923         // how many columns does this brick span
34924         var remainder = this.containerWidth % this.columnWidth;
34925         
34926         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34927         // round if off by 1 pixel, otherwise use ceil
34928         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34929         colSpan = Math.min( colSpan, this.cols );
34930         
34931         // normally this should be '1' as we dont' currently allow multi width columns..
34932         
34933         var colGroup = this._getColGroup( colSpan );
34934         // get the minimum Y value from the columns
34935         var minimumY = Math.min.apply( Math, colGroup );
34936         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34937         
34938         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34939          
34940         // position the brick
34941         var position = {
34942             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34943             y: this.currentSize.y + minimumY + this.padHeight
34944         };
34945         
34946         Roo.log(position);
34947         // apply setHeight to necessary columns
34948         var setHeight = minimumY + sz.height + this.padHeight;
34949         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34950         
34951         var setSpan = this.cols + 1 - colGroup.length;
34952         for ( var i = 0; i < setSpan; i++ ) {
34953           this.colYs[ shortColIndex + i ] = setHeight ;
34954         }
34955       
34956         return position;
34957     },
34958     
34959     /**
34960      * @param {Number} colSpan - number of columns the element spans
34961      * @returns {Array} colGroup
34962      */
34963     _getColGroup : function( colSpan )
34964     {
34965         if ( colSpan < 2 ) {
34966           // if brick spans only one column, use all the column Ys
34967           return this.colYs;
34968         }
34969       
34970         var colGroup = [];
34971         // how many different places could this brick fit horizontally
34972         var groupCount = this.cols + 1 - colSpan;
34973         // for each group potential horizontal position
34974         for ( var i = 0; i < groupCount; i++ ) {
34975           // make an array of colY values for that one group
34976           var groupColYs = this.colYs.slice( i, i + colSpan );
34977           // and get the max value of the array
34978           colGroup[i] = Math.max.apply( Math, groupColYs );
34979         }
34980         return colGroup;
34981     },
34982     /*
34983     _manageStamp : function( stamp )
34984     {
34985         var stampSize =  stamp.getSize();
34986         var offset = stamp.getBox();
34987         // get the columns that this stamp affects
34988         var firstX = this.isOriginLeft ? offset.x : offset.right;
34989         var lastX = firstX + stampSize.width;
34990         var firstCol = Math.floor( firstX / this.columnWidth );
34991         firstCol = Math.max( 0, firstCol );
34992         
34993         var lastCol = Math.floor( lastX / this.columnWidth );
34994         // lastCol should not go over if multiple of columnWidth #425
34995         lastCol -= lastX % this.columnWidth ? 0 : 1;
34996         lastCol = Math.min( this.cols - 1, lastCol );
34997         
34998         // set colYs to bottom of the stamp
34999         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35000             stampSize.height;
35001             
35002         for ( var i = firstCol; i <= lastCol; i++ ) {
35003           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35004         }
35005     },
35006     */
35007     
35008     _getContainerSize : function()
35009     {
35010         this.maxY = Math.max.apply( Math, this.colYs );
35011         var size = {
35012             height: this.maxY
35013         };
35014       
35015         if ( this.isFitWidth ) {
35016             size.width = this._getContainerFitWidth();
35017         }
35018       
35019         return size;
35020     },
35021     
35022     _getContainerFitWidth : function()
35023     {
35024         var unusedCols = 0;
35025         // count unused columns
35026         var i = this.cols;
35027         while ( --i ) {
35028           if ( this.colYs[i] !== 0 ) {
35029             break;
35030           }
35031           unusedCols++;
35032         }
35033         // fit container to columns that have been used
35034         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35035     },
35036     
35037     needsResizeLayout : function()
35038     {
35039         var previousWidth = this.containerWidth;
35040         this.getContainerWidth();
35041         return previousWidth !== this.containerWidth;
35042     }
35043  
35044 });
35045
35046  
35047
35048  /*
35049  * - LGPL
35050  *
35051  * element
35052  * 
35053  */
35054
35055 /**
35056  * @class Roo.bootstrap.MasonryBrick
35057  * @extends Roo.bootstrap.Component
35058  * Bootstrap MasonryBrick class
35059  * 
35060  * @constructor
35061  * Create a new MasonryBrick
35062  * @param {Object} config The config object
35063  */
35064
35065 Roo.bootstrap.MasonryBrick = function(config){
35066     
35067     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35068     
35069     Roo.bootstrap.MasonryBrick.register(this);
35070     
35071     this.addEvents({
35072         // raw events
35073         /**
35074          * @event click
35075          * When a MasonryBrick is clcik
35076          * @param {Roo.bootstrap.MasonryBrick} this
35077          * @param {Roo.EventObject} e
35078          */
35079         "click" : true
35080     });
35081 };
35082
35083 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35084     
35085     /**
35086      * @cfg {String} title
35087      */   
35088     title : '',
35089     /**
35090      * @cfg {String} html
35091      */   
35092     html : '',
35093     /**
35094      * @cfg {String} bgimage
35095      */   
35096     bgimage : '',
35097     /**
35098      * @cfg {String} videourl
35099      */   
35100     videourl : '',
35101     /**
35102      * @cfg {String} cls
35103      */   
35104     cls : '',
35105     /**
35106      * @cfg {String} href
35107      */   
35108     href : '',
35109     /**
35110      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35111      */   
35112     size : 'xs',
35113     
35114     /**
35115      * @cfg {String} placetitle (center|bottom)
35116      */   
35117     placetitle : '',
35118     
35119     /**
35120      * @cfg {Boolean} isFitContainer defalut true
35121      */   
35122     isFitContainer : true, 
35123     
35124     /**
35125      * @cfg {Boolean} preventDefault defalut false
35126      */   
35127     preventDefault : false, 
35128     
35129     /**
35130      * @cfg {Boolean} inverse defalut false
35131      */   
35132     maskInverse : false, 
35133     
35134     getAutoCreate : function()
35135     {
35136         if(!this.isFitContainer){
35137             return this.getSplitAutoCreate();
35138         }
35139         
35140         var cls = 'masonry-brick masonry-brick-full';
35141         
35142         if(this.href.length){
35143             cls += ' masonry-brick-link';
35144         }
35145         
35146         if(this.bgimage.length){
35147             cls += ' masonry-brick-image';
35148         }
35149         
35150         if(this.maskInverse){
35151             cls += ' mask-inverse';
35152         }
35153         
35154         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35155             cls += ' enable-mask';
35156         }
35157         
35158         if(this.size){
35159             cls += ' masonry-' + this.size + '-brick';
35160         }
35161         
35162         if(this.placetitle.length){
35163             
35164             switch (this.placetitle) {
35165                 case 'center' :
35166                     cls += ' masonry-center-title';
35167                     break;
35168                 case 'bottom' :
35169                     cls += ' masonry-bottom-title';
35170                     break;
35171                 default:
35172                     break;
35173             }
35174             
35175         } else {
35176             if(!this.html.length && !this.bgimage.length){
35177                 cls += ' masonry-center-title';
35178             }
35179
35180             if(!this.html.length && this.bgimage.length){
35181                 cls += ' masonry-bottom-title';
35182             }
35183         }
35184         
35185         if(this.cls){
35186             cls += ' ' + this.cls;
35187         }
35188         
35189         var cfg = {
35190             tag: (this.href.length) ? 'a' : 'div',
35191             cls: cls,
35192             cn: [
35193                 {
35194                     tag: 'div',
35195                     cls: 'masonry-brick-mask'
35196                 },
35197                 {
35198                     tag: 'div',
35199                     cls: 'masonry-brick-paragraph',
35200                     cn: []
35201                 }
35202             ]
35203         };
35204         
35205         if(this.href.length){
35206             cfg.href = this.href;
35207         }
35208         
35209         var cn = cfg.cn[1].cn;
35210         
35211         if(this.title.length){
35212             cn.push({
35213                 tag: 'h4',
35214                 cls: 'masonry-brick-title',
35215                 html: this.title
35216             });
35217         }
35218         
35219         if(this.html.length){
35220             cn.push({
35221                 tag: 'p',
35222                 cls: 'masonry-brick-text',
35223                 html: this.html
35224             });
35225         }
35226         
35227         if (!this.title.length && !this.html.length) {
35228             cfg.cn[1].cls += ' hide';
35229         }
35230         
35231         if(this.bgimage.length){
35232             cfg.cn.push({
35233                 tag: 'img',
35234                 cls: 'masonry-brick-image-view',
35235                 src: this.bgimage
35236             });
35237         }
35238         
35239         if(this.videourl.length){
35240             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35241             // youtube support only?
35242             cfg.cn.push({
35243                 tag: 'iframe',
35244                 cls: 'masonry-brick-image-view',
35245                 src: vurl,
35246                 frameborder : 0,
35247                 allowfullscreen : true
35248             });
35249         }
35250         
35251         return cfg;
35252         
35253     },
35254     
35255     getSplitAutoCreate : function()
35256     {
35257         var cls = 'masonry-brick masonry-brick-split';
35258         
35259         if(this.href.length){
35260             cls += ' masonry-brick-link';
35261         }
35262         
35263         if(this.bgimage.length){
35264             cls += ' masonry-brick-image';
35265         }
35266         
35267         if(this.size){
35268             cls += ' masonry-' + this.size + '-brick';
35269         }
35270         
35271         switch (this.placetitle) {
35272             case 'center' :
35273                 cls += ' masonry-center-title';
35274                 break;
35275             case 'bottom' :
35276                 cls += ' masonry-bottom-title';
35277                 break;
35278             default:
35279                 if(!this.bgimage.length){
35280                     cls += ' masonry-center-title';
35281                 }
35282
35283                 if(this.bgimage.length){
35284                     cls += ' masonry-bottom-title';
35285                 }
35286                 break;
35287         }
35288         
35289         if(this.cls){
35290             cls += ' ' + this.cls;
35291         }
35292         
35293         var cfg = {
35294             tag: (this.href.length) ? 'a' : 'div',
35295             cls: cls,
35296             cn: [
35297                 {
35298                     tag: 'div',
35299                     cls: 'masonry-brick-split-head',
35300                     cn: [
35301                         {
35302                             tag: 'div',
35303                             cls: 'masonry-brick-paragraph',
35304                             cn: []
35305                         }
35306                     ]
35307                 },
35308                 {
35309                     tag: 'div',
35310                     cls: 'masonry-brick-split-body',
35311                     cn: []
35312                 }
35313             ]
35314         };
35315         
35316         if(this.href.length){
35317             cfg.href = this.href;
35318         }
35319         
35320         if(this.title.length){
35321             cfg.cn[0].cn[0].cn.push({
35322                 tag: 'h4',
35323                 cls: 'masonry-brick-title',
35324                 html: this.title
35325             });
35326         }
35327         
35328         if(this.html.length){
35329             cfg.cn[1].cn.push({
35330                 tag: 'p',
35331                 cls: 'masonry-brick-text',
35332                 html: this.html
35333             });
35334         }
35335
35336         if(this.bgimage.length){
35337             cfg.cn[0].cn.push({
35338                 tag: 'img',
35339                 cls: 'masonry-brick-image-view',
35340                 src: this.bgimage
35341             });
35342         }
35343         
35344         if(this.videourl.length){
35345             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35346             // youtube support only?
35347             cfg.cn[0].cn.cn.push({
35348                 tag: 'iframe',
35349                 cls: 'masonry-brick-image-view',
35350                 src: vurl,
35351                 frameborder : 0,
35352                 allowfullscreen : true
35353             });
35354         }
35355         
35356         return cfg;
35357     },
35358     
35359     initEvents: function() 
35360     {
35361         switch (this.size) {
35362             case 'xs' :
35363                 this.x = 1;
35364                 this.y = 1;
35365                 break;
35366             case 'sm' :
35367                 this.x = 2;
35368                 this.y = 2;
35369                 break;
35370             case 'md' :
35371             case 'md-left' :
35372             case 'md-right' :
35373                 this.x = 3;
35374                 this.y = 3;
35375                 break;
35376             case 'tall' :
35377                 this.x = 2;
35378                 this.y = 3;
35379                 break;
35380             case 'wide' :
35381                 this.x = 3;
35382                 this.y = 2;
35383                 break;
35384             case 'wide-thin' :
35385                 this.x = 3;
35386                 this.y = 1;
35387                 break;
35388                         
35389             default :
35390                 break;
35391         }
35392         
35393         if(Roo.isTouch){
35394             this.el.on('touchstart', this.onTouchStart, this);
35395             this.el.on('touchmove', this.onTouchMove, this);
35396             this.el.on('touchend', this.onTouchEnd, this);
35397             this.el.on('contextmenu', this.onContextMenu, this);
35398         } else {
35399             this.el.on('mouseenter'  ,this.enter, this);
35400             this.el.on('mouseleave', this.leave, this);
35401             this.el.on('click', this.onClick, this);
35402         }
35403         
35404         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35405             this.parent().bricks.push(this);   
35406         }
35407         
35408     },
35409     
35410     onClick: function(e, el)
35411     {
35412         var time = this.endTimer - this.startTimer;
35413         // Roo.log(e.preventDefault());
35414         if(Roo.isTouch){
35415             if(time > 1000){
35416                 e.preventDefault();
35417                 return;
35418             }
35419         }
35420         
35421         if(!this.preventDefault){
35422             return;
35423         }
35424         
35425         e.preventDefault();
35426         
35427         if (this.activeClass != '') {
35428             this.selectBrick();
35429         }
35430         
35431         this.fireEvent('click', this, e);
35432     },
35433     
35434     enter: function(e, el)
35435     {
35436         e.preventDefault();
35437         
35438         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35439             return;
35440         }
35441         
35442         if(this.bgimage.length && this.html.length){
35443             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35444         }
35445     },
35446     
35447     leave: function(e, el)
35448     {
35449         e.preventDefault();
35450         
35451         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35452             return;
35453         }
35454         
35455         if(this.bgimage.length && this.html.length){
35456             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35457         }
35458     },
35459     
35460     onTouchStart: function(e, el)
35461     {
35462 //        e.preventDefault();
35463         
35464         this.touchmoved = false;
35465         
35466         if(!this.isFitContainer){
35467             return;
35468         }
35469         
35470         if(!this.bgimage.length || !this.html.length){
35471             return;
35472         }
35473         
35474         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35475         
35476         this.timer = new Date().getTime();
35477         
35478     },
35479     
35480     onTouchMove: function(e, el)
35481     {
35482         this.touchmoved = true;
35483     },
35484     
35485     onContextMenu : function(e,el)
35486     {
35487         e.preventDefault();
35488         e.stopPropagation();
35489         return false;
35490     },
35491     
35492     onTouchEnd: function(e, el)
35493     {
35494 //        e.preventDefault();
35495         
35496         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35497         
35498             this.leave(e,el);
35499             
35500             return;
35501         }
35502         
35503         if(!this.bgimage.length || !this.html.length){
35504             
35505             if(this.href.length){
35506                 window.location.href = this.href;
35507             }
35508             
35509             return;
35510         }
35511         
35512         if(!this.isFitContainer){
35513             return;
35514         }
35515         
35516         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35517         
35518         window.location.href = this.href;
35519     },
35520     
35521     //selection on single brick only
35522     selectBrick : function() {
35523         
35524         if (!this.parentId) {
35525             return;
35526         }
35527         
35528         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35529         var index = m.selectedBrick.indexOf(this.id);
35530         
35531         if ( index > -1) {
35532             m.selectedBrick.splice(index,1);
35533             this.el.removeClass(this.activeClass);
35534             return;
35535         }
35536         
35537         for(var i = 0; i < m.selectedBrick.length; i++) {
35538             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35539             b.el.removeClass(b.activeClass);
35540         }
35541         
35542         m.selectedBrick = [];
35543         
35544         m.selectedBrick.push(this.id);
35545         this.el.addClass(this.activeClass);
35546         return;
35547     },
35548     
35549     isSelected : function(){
35550         return this.el.hasClass(this.activeClass);
35551         
35552     }
35553 });
35554
35555 Roo.apply(Roo.bootstrap.MasonryBrick, {
35556     
35557     //groups: {},
35558     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35559      /**
35560     * register a Masonry Brick
35561     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35562     */
35563     
35564     register : function(brick)
35565     {
35566         //this.groups[brick.id] = brick;
35567         this.groups.add(brick.id, brick);
35568     },
35569     /**
35570     * fetch a  masonry brick based on the masonry brick ID
35571     * @param {string} the masonry brick to add
35572     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35573     */
35574     
35575     get: function(brick_id) 
35576     {
35577         // if (typeof(this.groups[brick_id]) == 'undefined') {
35578         //     return false;
35579         // }
35580         // return this.groups[brick_id] ;
35581         
35582         if(this.groups.key(brick_id)) {
35583             return this.groups.key(brick_id);
35584         }
35585         
35586         return false;
35587     }
35588     
35589     
35590     
35591 });
35592
35593  /*
35594  * - LGPL
35595  *
35596  * element
35597  * 
35598  */
35599
35600 /**
35601  * @class Roo.bootstrap.Brick
35602  * @extends Roo.bootstrap.Component
35603  * Bootstrap Brick class
35604  * 
35605  * @constructor
35606  * Create a new Brick
35607  * @param {Object} config The config object
35608  */
35609
35610 Roo.bootstrap.Brick = function(config){
35611     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35612     
35613     this.addEvents({
35614         // raw events
35615         /**
35616          * @event click
35617          * When a Brick is click
35618          * @param {Roo.bootstrap.Brick} this
35619          * @param {Roo.EventObject} e
35620          */
35621         "click" : true
35622     });
35623 };
35624
35625 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35626     
35627     /**
35628      * @cfg {String} title
35629      */   
35630     title : '',
35631     /**
35632      * @cfg {String} html
35633      */   
35634     html : '',
35635     /**
35636      * @cfg {String} bgimage
35637      */   
35638     bgimage : '',
35639     /**
35640      * @cfg {String} cls
35641      */   
35642     cls : '',
35643     /**
35644      * @cfg {String} href
35645      */   
35646     href : '',
35647     /**
35648      * @cfg {String} video
35649      */   
35650     video : '',
35651     /**
35652      * @cfg {Boolean} square
35653      */   
35654     square : true,
35655     
35656     getAutoCreate : function()
35657     {
35658         var cls = 'roo-brick';
35659         
35660         if(this.href.length){
35661             cls += ' roo-brick-link';
35662         }
35663         
35664         if(this.bgimage.length){
35665             cls += ' roo-brick-image';
35666         }
35667         
35668         if(!this.html.length && !this.bgimage.length){
35669             cls += ' roo-brick-center-title';
35670         }
35671         
35672         if(!this.html.length && this.bgimage.length){
35673             cls += ' roo-brick-bottom-title';
35674         }
35675         
35676         if(this.cls){
35677             cls += ' ' + this.cls;
35678         }
35679         
35680         var cfg = {
35681             tag: (this.href.length) ? 'a' : 'div',
35682             cls: cls,
35683             cn: [
35684                 {
35685                     tag: 'div',
35686                     cls: 'roo-brick-paragraph',
35687                     cn: []
35688                 }
35689             ]
35690         };
35691         
35692         if(this.href.length){
35693             cfg.href = this.href;
35694         }
35695         
35696         var cn = cfg.cn[0].cn;
35697         
35698         if(this.title.length){
35699             cn.push({
35700                 tag: 'h4',
35701                 cls: 'roo-brick-title',
35702                 html: this.title
35703             });
35704         }
35705         
35706         if(this.html.length){
35707             cn.push({
35708                 tag: 'p',
35709                 cls: 'roo-brick-text',
35710                 html: this.html
35711             });
35712         } else {
35713             cn.cls += ' hide';
35714         }
35715         
35716         if(this.bgimage.length){
35717             cfg.cn.push({
35718                 tag: 'img',
35719                 cls: 'roo-brick-image-view',
35720                 src: this.bgimage
35721             });
35722         }
35723         
35724         return cfg;
35725     },
35726     
35727     initEvents: function() 
35728     {
35729         if(this.title.length || this.html.length){
35730             this.el.on('mouseenter'  ,this.enter, this);
35731             this.el.on('mouseleave', this.leave, this);
35732         }
35733         
35734         Roo.EventManager.onWindowResize(this.resize, this); 
35735         
35736         if(this.bgimage.length){
35737             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35738             this.imageEl.on('load', this.onImageLoad, this);
35739             return;
35740         }
35741         
35742         this.resize();
35743     },
35744     
35745     onImageLoad : function()
35746     {
35747         this.resize();
35748     },
35749     
35750     resize : function()
35751     {
35752         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35753         
35754         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35755         
35756         if(this.bgimage.length){
35757             var image = this.el.select('.roo-brick-image-view', true).first();
35758             
35759             image.setWidth(paragraph.getWidth());
35760             
35761             if(this.square){
35762                 image.setHeight(paragraph.getWidth());
35763             }
35764             
35765             this.el.setHeight(image.getHeight());
35766             paragraph.setHeight(image.getHeight());
35767             
35768         }
35769         
35770     },
35771     
35772     enter: function(e, el)
35773     {
35774         e.preventDefault();
35775         
35776         if(this.bgimage.length){
35777             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35778             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35779         }
35780     },
35781     
35782     leave: function(e, el)
35783     {
35784         e.preventDefault();
35785         
35786         if(this.bgimage.length){
35787             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35788             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35789         }
35790     }
35791     
35792 });
35793
35794  
35795
35796  /*
35797  * - LGPL
35798  *
35799  * Number field 
35800  */
35801
35802 /**
35803  * @class Roo.bootstrap.NumberField
35804  * @extends Roo.bootstrap.Input
35805  * Bootstrap NumberField class
35806  * 
35807  * 
35808  * 
35809  * 
35810  * @constructor
35811  * Create a new NumberField
35812  * @param {Object} config The config object
35813  */
35814
35815 Roo.bootstrap.NumberField = function(config){
35816     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35817 };
35818
35819 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35820     
35821     /**
35822      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35823      */
35824     allowDecimals : true,
35825     /**
35826      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35827      */
35828     decimalSeparator : ".",
35829     /**
35830      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35831      */
35832     decimalPrecision : 2,
35833     /**
35834      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35835      */
35836     allowNegative : true,
35837     
35838     /**
35839      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35840      */
35841     allowZero: true,
35842     /**
35843      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35844      */
35845     minValue : Number.NEGATIVE_INFINITY,
35846     /**
35847      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35848      */
35849     maxValue : Number.MAX_VALUE,
35850     /**
35851      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35852      */
35853     minText : "The minimum value for this field is {0}",
35854     /**
35855      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35856      */
35857     maxText : "The maximum value for this field is {0}",
35858     /**
35859      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35860      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35861      */
35862     nanText : "{0} is not a valid number",
35863     /**
35864      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35865      */
35866     thousandsDelimiter : false,
35867     /**
35868      * @cfg {String} valueAlign alignment of value
35869      */
35870     valueAlign : "left",
35871
35872     getAutoCreate : function()
35873     {
35874         var hiddenInput = {
35875             tag: 'input',
35876             type: 'hidden',
35877             id: Roo.id(),
35878             cls: 'hidden-number-input'
35879         };
35880         
35881         if (this.name) {
35882             hiddenInput.name = this.name;
35883         }
35884         
35885         this.name = '';
35886         
35887         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35888         
35889         this.name = hiddenInput.name;
35890         
35891         if(cfg.cn.length > 0) {
35892             cfg.cn.push(hiddenInput);
35893         }
35894         
35895         return cfg;
35896     },
35897
35898     // private
35899     initEvents : function()
35900     {   
35901         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35902         
35903         var allowed = "0123456789";
35904         
35905         if(this.allowDecimals){
35906             allowed += this.decimalSeparator;
35907         }
35908         
35909         if(this.allowNegative){
35910             allowed += "-";
35911         }
35912         
35913         if(this.thousandsDelimiter) {
35914             allowed += ",";
35915         }
35916         
35917         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35918         
35919         var keyPress = function(e){
35920             
35921             var k = e.getKey();
35922             
35923             var c = e.getCharCode();
35924             
35925             if(
35926                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35927                     allowed.indexOf(String.fromCharCode(c)) === -1
35928             ){
35929                 e.stopEvent();
35930                 return;
35931             }
35932             
35933             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35934                 return;
35935             }
35936             
35937             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35938                 e.stopEvent();
35939             }
35940         };
35941         
35942         this.el.on("keypress", keyPress, this);
35943     },
35944     
35945     validateValue : function(value)
35946     {
35947         
35948         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35949             return false;
35950         }
35951         
35952         var num = this.parseValue(value);
35953         
35954         if(isNaN(num)){
35955             this.markInvalid(String.format(this.nanText, value));
35956             return false;
35957         }
35958         
35959         if(num < this.minValue){
35960             this.markInvalid(String.format(this.minText, this.minValue));
35961             return false;
35962         }
35963         
35964         if(num > this.maxValue){
35965             this.markInvalid(String.format(this.maxText, this.maxValue));
35966             return false;
35967         }
35968         
35969         return true;
35970     },
35971
35972     getValue : function()
35973     {
35974         var v = this.hiddenEl().getValue();
35975         
35976         return this.fixPrecision(this.parseValue(v));
35977     },
35978
35979     parseValue : function(value)
35980     {
35981         if(this.thousandsDelimiter) {
35982             value += "";
35983             r = new RegExp(",", "g");
35984             value = value.replace(r, "");
35985         }
35986         
35987         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35988         return isNaN(value) ? '' : value;
35989     },
35990
35991     fixPrecision : function(value)
35992     {
35993         if(this.thousandsDelimiter) {
35994             value += "";
35995             r = new RegExp(",", "g");
35996             value = value.replace(r, "");
35997         }
35998         
35999         var nan = isNaN(value);
36000         
36001         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36002             return nan ? '' : value;
36003         }
36004         return parseFloat(value).toFixed(this.decimalPrecision);
36005     },
36006
36007     setValue : function(v)
36008     {
36009         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36010         
36011         this.value = v;
36012         
36013         if(this.rendered){
36014             
36015             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36016             
36017             this.inputEl().dom.value = (v == '') ? '' :
36018                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36019             
36020             if(!this.allowZero && v === '0') {
36021                 this.hiddenEl().dom.value = '';
36022                 this.inputEl().dom.value = '';
36023             }
36024             
36025             this.validate();
36026         }
36027     },
36028
36029     decimalPrecisionFcn : function(v)
36030     {
36031         return Math.floor(v);
36032     },
36033
36034     beforeBlur : function()
36035     {
36036         var v = this.parseValue(this.getRawValue());
36037         
36038         if(v || v === 0 || v === ''){
36039             this.setValue(v);
36040         }
36041     },
36042     
36043     hiddenEl : function()
36044     {
36045         return this.el.select('input.hidden-number-input',true).first();
36046     }
36047     
36048 });
36049
36050  
36051
36052 /*
36053 * Licence: LGPL
36054 */
36055
36056 /**
36057  * @class Roo.bootstrap.DocumentSlider
36058  * @extends Roo.bootstrap.Component
36059  * Bootstrap DocumentSlider class
36060  * 
36061  * @constructor
36062  * Create a new DocumentViewer
36063  * @param {Object} config The config object
36064  */
36065
36066 Roo.bootstrap.DocumentSlider = function(config){
36067     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36068     
36069     this.files = [];
36070     
36071     this.addEvents({
36072         /**
36073          * @event initial
36074          * Fire after initEvent
36075          * @param {Roo.bootstrap.DocumentSlider} this
36076          */
36077         "initial" : true,
36078         /**
36079          * @event update
36080          * Fire after update
36081          * @param {Roo.bootstrap.DocumentSlider} this
36082          */
36083         "update" : true,
36084         /**
36085          * @event click
36086          * Fire after click
36087          * @param {Roo.bootstrap.DocumentSlider} this
36088          */
36089         "click" : true
36090     });
36091 };
36092
36093 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36094     
36095     files : false,
36096     
36097     indicator : 0,
36098     
36099     getAutoCreate : function()
36100     {
36101         var cfg = {
36102             tag : 'div',
36103             cls : 'roo-document-slider',
36104             cn : [
36105                 {
36106                     tag : 'div',
36107                     cls : 'roo-document-slider-header',
36108                     cn : [
36109                         {
36110                             tag : 'div',
36111                             cls : 'roo-document-slider-header-title'
36112                         }
36113                     ]
36114                 },
36115                 {
36116                     tag : 'div',
36117                     cls : 'roo-document-slider-body',
36118                     cn : [
36119                         {
36120                             tag : 'div',
36121                             cls : 'roo-document-slider-prev',
36122                             cn : [
36123                                 {
36124                                     tag : 'i',
36125                                     cls : 'fa fa-chevron-left'
36126                                 }
36127                             ]
36128                         },
36129                         {
36130                             tag : 'div',
36131                             cls : 'roo-document-slider-thumb',
36132                             cn : [
36133                                 {
36134                                     tag : 'img',
36135                                     cls : 'roo-document-slider-image'
36136                                 }
36137                             ]
36138                         },
36139                         {
36140                             tag : 'div',
36141                             cls : 'roo-document-slider-next',
36142                             cn : [
36143                                 {
36144                                     tag : 'i',
36145                                     cls : 'fa fa-chevron-right'
36146                                 }
36147                             ]
36148                         }
36149                     ]
36150                 }
36151             ]
36152         };
36153         
36154         return cfg;
36155     },
36156     
36157     initEvents : function()
36158     {
36159         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36160         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36161         
36162         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36163         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36164         
36165         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36166         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36167         
36168         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36169         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36170         
36171         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36172         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36173         
36174         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36175         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36176         
36177         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36178         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36179         
36180         this.thumbEl.on('click', this.onClick, this);
36181         
36182         this.prevIndicator.on('click', this.prev, this);
36183         
36184         this.nextIndicator.on('click', this.next, this);
36185         
36186     },
36187     
36188     initial : function()
36189     {
36190         if(this.files.length){
36191             this.indicator = 1;
36192             this.update()
36193         }
36194         
36195         this.fireEvent('initial', this);
36196     },
36197     
36198     update : function()
36199     {
36200         this.imageEl.attr('src', this.files[this.indicator - 1]);
36201         
36202         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36203         
36204         this.prevIndicator.show();
36205         
36206         if(this.indicator == 1){
36207             this.prevIndicator.hide();
36208         }
36209         
36210         this.nextIndicator.show();
36211         
36212         if(this.indicator == this.files.length){
36213             this.nextIndicator.hide();
36214         }
36215         
36216         this.thumbEl.scrollTo('top');
36217         
36218         this.fireEvent('update', this);
36219     },
36220     
36221     onClick : function(e)
36222     {
36223         e.preventDefault();
36224         
36225         this.fireEvent('click', this);
36226     },
36227     
36228     prev : function(e)
36229     {
36230         e.preventDefault();
36231         
36232         this.indicator = Math.max(1, this.indicator - 1);
36233         
36234         this.update();
36235     },
36236     
36237     next : function(e)
36238     {
36239         e.preventDefault();
36240         
36241         this.indicator = Math.min(this.files.length, this.indicator + 1);
36242         
36243         this.update();
36244     }
36245 });
36246 /*
36247  * - LGPL
36248  *
36249  * RadioSet
36250  *
36251  *
36252  */
36253
36254 /**
36255  * @class Roo.bootstrap.RadioSet
36256  * @extends Roo.bootstrap.Input
36257  * Bootstrap RadioSet class
36258  * @cfg {String} indicatorpos (left|right) default left
36259  * @cfg {Boolean} inline (true|false) inline the element (default true)
36260  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36261  * @constructor
36262  * Create a new RadioSet
36263  * @param {Object} config The config object
36264  */
36265
36266 Roo.bootstrap.RadioSet = function(config){
36267     
36268     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36269     
36270     this.radioes = [];
36271     
36272     Roo.bootstrap.RadioSet.register(this);
36273     
36274     this.addEvents({
36275         /**
36276         * @event check
36277         * Fires when the element is checked or unchecked.
36278         * @param {Roo.bootstrap.RadioSet} this This radio
36279         * @param {Roo.bootstrap.Radio} item The checked item
36280         */
36281        check : true,
36282        /**
36283         * @event click
36284         * Fires when the element is click.
36285         * @param {Roo.bootstrap.RadioSet} this This radio set
36286         * @param {Roo.bootstrap.Radio} item The checked item
36287         * @param {Roo.EventObject} e The event object
36288         */
36289        click : true
36290     });
36291     
36292 };
36293
36294 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36295
36296     radioes : false,
36297     
36298     inline : true,
36299     
36300     weight : '',
36301     
36302     indicatorpos : 'left',
36303     
36304     getAutoCreate : function()
36305     {
36306         var label = {
36307             tag : 'label',
36308             cls : 'roo-radio-set-label',
36309             cn : [
36310                 {
36311                     tag : 'span',
36312                     html : this.fieldLabel
36313                 }
36314             ]
36315         };
36316         if (Roo.bootstrap.version == 3) {
36317             
36318             
36319             if(this.indicatorpos == 'left'){
36320                 label.cn.unshift({
36321                     tag : 'i',
36322                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36323                     tooltip : 'This field is required'
36324                 });
36325             } else {
36326                 label.cn.push({
36327                     tag : 'i',
36328                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36329                     tooltip : 'This field is required'
36330                 });
36331             }
36332         }
36333         var items = {
36334             tag : 'div',
36335             cls : 'roo-radio-set-items'
36336         };
36337         
36338         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36339         
36340         if (align === 'left' && this.fieldLabel.length) {
36341             
36342             items = {
36343                 cls : "roo-radio-set-right", 
36344                 cn: [
36345                     items
36346                 ]
36347             };
36348             
36349             if(this.labelWidth > 12){
36350                 label.style = "width: " + this.labelWidth + 'px';
36351             }
36352             
36353             if(this.labelWidth < 13 && this.labelmd == 0){
36354                 this.labelmd = this.labelWidth;
36355             }
36356             
36357             if(this.labellg > 0){
36358                 label.cls += ' col-lg-' + this.labellg;
36359                 items.cls += ' col-lg-' + (12 - this.labellg);
36360             }
36361             
36362             if(this.labelmd > 0){
36363                 label.cls += ' col-md-' + this.labelmd;
36364                 items.cls += ' col-md-' + (12 - this.labelmd);
36365             }
36366             
36367             if(this.labelsm > 0){
36368                 label.cls += ' col-sm-' + this.labelsm;
36369                 items.cls += ' col-sm-' + (12 - this.labelsm);
36370             }
36371             
36372             if(this.labelxs > 0){
36373                 label.cls += ' col-xs-' + this.labelxs;
36374                 items.cls += ' col-xs-' + (12 - this.labelxs);
36375             }
36376         }
36377         
36378         var cfg = {
36379             tag : 'div',
36380             cls : 'roo-radio-set',
36381             cn : [
36382                 {
36383                     tag : 'input',
36384                     cls : 'roo-radio-set-input',
36385                     type : 'hidden',
36386                     name : this.name,
36387                     value : this.value ? this.value :  ''
36388                 },
36389                 label,
36390                 items
36391             ]
36392         };
36393         
36394         if(this.weight.length){
36395             cfg.cls += ' roo-radio-' + this.weight;
36396         }
36397         
36398         if(this.inline) {
36399             cfg.cls += ' roo-radio-set-inline';
36400         }
36401         
36402         var settings=this;
36403         ['xs','sm','md','lg'].map(function(size){
36404             if (settings[size]) {
36405                 cfg.cls += ' col-' + size + '-' + settings[size];
36406             }
36407         });
36408         
36409         return cfg;
36410         
36411     },
36412
36413     initEvents : function()
36414     {
36415         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36416         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36417         
36418         if(!this.fieldLabel.length){
36419             this.labelEl.hide();
36420         }
36421         
36422         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36423         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36424         
36425         this.indicator = this.indicatorEl();
36426         
36427         if(this.indicator){
36428             this.indicator.addClass('invisible');
36429         }
36430         
36431         this.originalValue = this.getValue();
36432         
36433     },
36434     
36435     inputEl: function ()
36436     {
36437         return this.el.select('.roo-radio-set-input', true).first();
36438     },
36439     
36440     getChildContainer : function()
36441     {
36442         return this.itemsEl;
36443     },
36444     
36445     register : function(item)
36446     {
36447         this.radioes.push(item);
36448         
36449     },
36450     
36451     validate : function()
36452     {   
36453         if(this.getVisibilityEl().hasClass('hidden')){
36454             return true;
36455         }
36456         
36457         var valid = false;
36458         
36459         Roo.each(this.radioes, function(i){
36460             if(!i.checked){
36461                 return;
36462             }
36463             
36464             valid = true;
36465             return false;
36466         });
36467         
36468         if(this.allowBlank) {
36469             return true;
36470         }
36471         
36472         if(this.disabled || valid){
36473             this.markValid();
36474             return true;
36475         }
36476         
36477         this.markInvalid();
36478         return false;
36479         
36480     },
36481     
36482     markValid : function()
36483     {
36484         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36485             this.indicatorEl().removeClass('visible');
36486             this.indicatorEl().addClass('invisible');
36487         }
36488         
36489         
36490         if (Roo.bootstrap.version == 3) {
36491             this.el.removeClass([this.invalidClass, this.validClass]);
36492             this.el.addClass(this.validClass);
36493         } else {
36494             this.el.removeClass(['is-invalid','is-valid']);
36495             this.el.addClass(['is-valid']);
36496         }
36497         this.fireEvent('valid', this);
36498     },
36499     
36500     markInvalid : function(msg)
36501     {
36502         if(this.allowBlank || this.disabled){
36503             return;
36504         }
36505         
36506         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36507             this.indicatorEl().removeClass('invisible');
36508             this.indicatorEl().addClass('visible');
36509         }
36510         if (Roo.bootstrap.version == 3) {
36511             this.el.removeClass([this.invalidClass, this.validClass]);
36512             this.el.addClass(this.invalidClass);
36513         } else {
36514             this.el.removeClass(['is-invalid','is-valid']);
36515             this.el.addClass(['is-invalid']);
36516         }
36517         
36518         this.fireEvent('invalid', this, msg);
36519         
36520     },
36521     
36522     setValue : function(v, suppressEvent)
36523     {   
36524         if(this.value === v){
36525             return;
36526         }
36527         
36528         this.value = v;
36529         
36530         if(this.rendered){
36531             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36532         }
36533         
36534         Roo.each(this.radioes, function(i){
36535             i.checked = false;
36536             i.el.removeClass('checked');
36537         });
36538         
36539         Roo.each(this.radioes, function(i){
36540             
36541             if(i.value === v || i.value.toString() === v.toString()){
36542                 i.checked = true;
36543                 i.el.addClass('checked');
36544                 
36545                 if(suppressEvent !== true){
36546                     this.fireEvent('check', this, i);
36547                 }
36548                 
36549                 return false;
36550             }
36551             
36552         }, this);
36553         
36554         this.validate();
36555     },
36556     
36557     clearInvalid : function(){
36558         
36559         if(!this.el || this.preventMark){
36560             return;
36561         }
36562         
36563         this.el.removeClass([this.invalidClass]);
36564         
36565         this.fireEvent('valid', this);
36566     }
36567     
36568 });
36569
36570 Roo.apply(Roo.bootstrap.RadioSet, {
36571     
36572     groups: {},
36573     
36574     register : function(set)
36575     {
36576         this.groups[set.name] = set;
36577     },
36578     
36579     get: function(name) 
36580     {
36581         if (typeof(this.groups[name]) == 'undefined') {
36582             return false;
36583         }
36584         
36585         return this.groups[name] ;
36586     }
36587     
36588 });
36589 /*
36590  * Based on:
36591  * Ext JS Library 1.1.1
36592  * Copyright(c) 2006-2007, Ext JS, LLC.
36593  *
36594  * Originally Released Under LGPL - original licence link has changed is not relivant.
36595  *
36596  * Fork - LGPL
36597  * <script type="text/javascript">
36598  */
36599
36600
36601 /**
36602  * @class Roo.bootstrap.SplitBar
36603  * @extends Roo.util.Observable
36604  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36605  * <br><br>
36606  * Usage:
36607  * <pre><code>
36608 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36609                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36610 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36611 split.minSize = 100;
36612 split.maxSize = 600;
36613 split.animate = true;
36614 split.on('moved', splitterMoved);
36615 </code></pre>
36616  * @constructor
36617  * Create a new SplitBar
36618  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36619  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36620  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36621  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36622                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36623                         position of the SplitBar).
36624  */
36625 Roo.bootstrap.SplitBar = function(cfg){
36626     
36627     /** @private */
36628     
36629     //{
36630     //  dragElement : elm
36631     //  resizingElement: el,
36632         // optional..
36633     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36634     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36635         // existingProxy ???
36636     //}
36637     
36638     this.el = Roo.get(cfg.dragElement, true);
36639     this.el.dom.unselectable = "on";
36640     /** @private */
36641     this.resizingEl = Roo.get(cfg.resizingElement, true);
36642
36643     /**
36644      * @private
36645      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36646      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36647      * @type Number
36648      */
36649     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36650     
36651     /**
36652      * The minimum size of the resizing element. (Defaults to 0)
36653      * @type Number
36654      */
36655     this.minSize = 0;
36656     
36657     /**
36658      * The maximum size of the resizing element. (Defaults to 2000)
36659      * @type Number
36660      */
36661     this.maxSize = 2000;
36662     
36663     /**
36664      * Whether to animate the transition to the new size
36665      * @type Boolean
36666      */
36667     this.animate = false;
36668     
36669     /**
36670      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36671      * @type Boolean
36672      */
36673     this.useShim = false;
36674     
36675     /** @private */
36676     this.shim = null;
36677     
36678     if(!cfg.existingProxy){
36679         /** @private */
36680         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36681     }else{
36682         this.proxy = Roo.get(cfg.existingProxy).dom;
36683     }
36684     /** @private */
36685     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36686     
36687     /** @private */
36688     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36689     
36690     /** @private */
36691     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36692     
36693     /** @private */
36694     this.dragSpecs = {};
36695     
36696     /**
36697      * @private The adapter to use to positon and resize elements
36698      */
36699     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36700     this.adapter.init(this);
36701     
36702     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36703         /** @private */
36704         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36705         this.el.addClass("roo-splitbar-h");
36706     }else{
36707         /** @private */
36708         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36709         this.el.addClass("roo-splitbar-v");
36710     }
36711     
36712     this.addEvents({
36713         /**
36714          * @event resize
36715          * Fires when the splitter is moved (alias for {@link #event-moved})
36716          * @param {Roo.bootstrap.SplitBar} this
36717          * @param {Number} newSize the new width or height
36718          */
36719         "resize" : true,
36720         /**
36721          * @event moved
36722          * Fires when the splitter is moved
36723          * @param {Roo.bootstrap.SplitBar} this
36724          * @param {Number} newSize the new width or height
36725          */
36726         "moved" : true,
36727         /**
36728          * @event beforeresize
36729          * Fires before the splitter is dragged
36730          * @param {Roo.bootstrap.SplitBar} this
36731          */
36732         "beforeresize" : true,
36733
36734         "beforeapply" : true
36735     });
36736
36737     Roo.util.Observable.call(this);
36738 };
36739
36740 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36741     onStartProxyDrag : function(x, y){
36742         this.fireEvent("beforeresize", this);
36743         if(!this.overlay){
36744             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36745             o.unselectable();
36746             o.enableDisplayMode("block");
36747             // all splitbars share the same overlay
36748             Roo.bootstrap.SplitBar.prototype.overlay = o;
36749         }
36750         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36751         this.overlay.show();
36752         Roo.get(this.proxy).setDisplayed("block");
36753         var size = this.adapter.getElementSize(this);
36754         this.activeMinSize = this.getMinimumSize();;
36755         this.activeMaxSize = this.getMaximumSize();;
36756         var c1 = size - this.activeMinSize;
36757         var c2 = Math.max(this.activeMaxSize - size, 0);
36758         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36759             this.dd.resetConstraints();
36760             this.dd.setXConstraint(
36761                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36762                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36763             );
36764             this.dd.setYConstraint(0, 0);
36765         }else{
36766             this.dd.resetConstraints();
36767             this.dd.setXConstraint(0, 0);
36768             this.dd.setYConstraint(
36769                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36770                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36771             );
36772          }
36773         this.dragSpecs.startSize = size;
36774         this.dragSpecs.startPoint = [x, y];
36775         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36776     },
36777     
36778     /** 
36779      * @private Called after the drag operation by the DDProxy
36780      */
36781     onEndProxyDrag : function(e){
36782         Roo.get(this.proxy).setDisplayed(false);
36783         var endPoint = Roo.lib.Event.getXY(e);
36784         if(this.overlay){
36785             this.overlay.hide();
36786         }
36787         var newSize;
36788         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36789             newSize = this.dragSpecs.startSize + 
36790                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36791                     endPoint[0] - this.dragSpecs.startPoint[0] :
36792                     this.dragSpecs.startPoint[0] - endPoint[0]
36793                 );
36794         }else{
36795             newSize = this.dragSpecs.startSize + 
36796                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36797                     endPoint[1] - this.dragSpecs.startPoint[1] :
36798                     this.dragSpecs.startPoint[1] - endPoint[1]
36799                 );
36800         }
36801         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36802         if(newSize != this.dragSpecs.startSize){
36803             if(this.fireEvent('beforeapply', this, newSize) !== false){
36804                 this.adapter.setElementSize(this, newSize);
36805                 this.fireEvent("moved", this, newSize);
36806                 this.fireEvent("resize", this, newSize);
36807             }
36808         }
36809     },
36810     
36811     /**
36812      * Get the adapter this SplitBar uses
36813      * @return The adapter object
36814      */
36815     getAdapter : function(){
36816         return this.adapter;
36817     },
36818     
36819     /**
36820      * Set the adapter this SplitBar uses
36821      * @param {Object} adapter A SplitBar adapter object
36822      */
36823     setAdapter : function(adapter){
36824         this.adapter = adapter;
36825         this.adapter.init(this);
36826     },
36827     
36828     /**
36829      * Gets the minimum size for the resizing element
36830      * @return {Number} The minimum size
36831      */
36832     getMinimumSize : function(){
36833         return this.minSize;
36834     },
36835     
36836     /**
36837      * Sets the minimum size for the resizing element
36838      * @param {Number} minSize The minimum size
36839      */
36840     setMinimumSize : function(minSize){
36841         this.minSize = minSize;
36842     },
36843     
36844     /**
36845      * Gets the maximum size for the resizing element
36846      * @return {Number} The maximum size
36847      */
36848     getMaximumSize : function(){
36849         return this.maxSize;
36850     },
36851     
36852     /**
36853      * Sets the maximum size for the resizing element
36854      * @param {Number} maxSize The maximum size
36855      */
36856     setMaximumSize : function(maxSize){
36857         this.maxSize = maxSize;
36858     },
36859     
36860     /**
36861      * Sets the initialize size for the resizing element
36862      * @param {Number} size The initial size
36863      */
36864     setCurrentSize : function(size){
36865         var oldAnimate = this.animate;
36866         this.animate = false;
36867         this.adapter.setElementSize(this, size);
36868         this.animate = oldAnimate;
36869     },
36870     
36871     /**
36872      * Destroy this splitbar. 
36873      * @param {Boolean} removeEl True to remove the element
36874      */
36875     destroy : function(removeEl){
36876         if(this.shim){
36877             this.shim.remove();
36878         }
36879         this.dd.unreg();
36880         this.proxy.parentNode.removeChild(this.proxy);
36881         if(removeEl){
36882             this.el.remove();
36883         }
36884     }
36885 });
36886
36887 /**
36888  * @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.
36889  */
36890 Roo.bootstrap.SplitBar.createProxy = function(dir){
36891     var proxy = new Roo.Element(document.createElement("div"));
36892     proxy.unselectable();
36893     var cls = 'roo-splitbar-proxy';
36894     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36895     document.body.appendChild(proxy.dom);
36896     return proxy.dom;
36897 };
36898
36899 /** 
36900  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36901  * Default Adapter. It assumes the splitter and resizing element are not positioned
36902  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36903  */
36904 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36905 };
36906
36907 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36908     // do nothing for now
36909     init : function(s){
36910     
36911     },
36912     /**
36913      * Called before drag operations to get the current size of the resizing element. 
36914      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36915      */
36916      getElementSize : function(s){
36917         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36918             return s.resizingEl.getWidth();
36919         }else{
36920             return s.resizingEl.getHeight();
36921         }
36922     },
36923     
36924     /**
36925      * Called after drag operations to set the size of the resizing element.
36926      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36927      * @param {Number} newSize The new size to set
36928      * @param {Function} onComplete A function to be invoked when resizing is complete
36929      */
36930     setElementSize : function(s, newSize, onComplete){
36931         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36932             if(!s.animate){
36933                 s.resizingEl.setWidth(newSize);
36934                 if(onComplete){
36935                     onComplete(s, newSize);
36936                 }
36937             }else{
36938                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36939             }
36940         }else{
36941             
36942             if(!s.animate){
36943                 s.resizingEl.setHeight(newSize);
36944                 if(onComplete){
36945                     onComplete(s, newSize);
36946                 }
36947             }else{
36948                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36949             }
36950         }
36951     }
36952 };
36953
36954 /** 
36955  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36956  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36957  * Adapter that  moves the splitter element to align with the resized sizing element. 
36958  * Used with an absolute positioned SplitBar.
36959  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36960  * document.body, make sure you assign an id to the body element.
36961  */
36962 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36963     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36964     this.container = Roo.get(container);
36965 };
36966
36967 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36968     init : function(s){
36969         this.basic.init(s);
36970     },
36971     
36972     getElementSize : function(s){
36973         return this.basic.getElementSize(s);
36974     },
36975     
36976     setElementSize : function(s, newSize, onComplete){
36977         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36978     },
36979     
36980     moveSplitter : function(s){
36981         var yes = Roo.bootstrap.SplitBar;
36982         switch(s.placement){
36983             case yes.LEFT:
36984                 s.el.setX(s.resizingEl.getRight());
36985                 break;
36986             case yes.RIGHT:
36987                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36988                 break;
36989             case yes.TOP:
36990                 s.el.setY(s.resizingEl.getBottom());
36991                 break;
36992             case yes.BOTTOM:
36993                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36994                 break;
36995         }
36996     }
36997 };
36998
36999 /**
37000  * Orientation constant - Create a vertical SplitBar
37001  * @static
37002  * @type Number
37003  */
37004 Roo.bootstrap.SplitBar.VERTICAL = 1;
37005
37006 /**
37007  * Orientation constant - Create a horizontal SplitBar
37008  * @static
37009  * @type Number
37010  */
37011 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37012
37013 /**
37014  * Placement constant - The resizing element is to the left of the splitter element
37015  * @static
37016  * @type Number
37017  */
37018 Roo.bootstrap.SplitBar.LEFT = 1;
37019
37020 /**
37021  * Placement constant - The resizing element is to the right of the splitter element
37022  * @static
37023  * @type Number
37024  */
37025 Roo.bootstrap.SplitBar.RIGHT = 2;
37026
37027 /**
37028  * Placement constant - The resizing element is positioned above the splitter element
37029  * @static
37030  * @type Number
37031  */
37032 Roo.bootstrap.SplitBar.TOP = 3;
37033
37034 /**
37035  * Placement constant - The resizing element is positioned under splitter element
37036  * @static
37037  * @type Number
37038  */
37039 Roo.bootstrap.SplitBar.BOTTOM = 4;
37040 Roo.namespace("Roo.bootstrap.layout");/*
37041  * Based on:
37042  * Ext JS Library 1.1.1
37043  * Copyright(c) 2006-2007, Ext JS, LLC.
37044  *
37045  * Originally Released Under LGPL - original licence link has changed is not relivant.
37046  *
37047  * Fork - LGPL
37048  * <script type="text/javascript">
37049  */
37050
37051 /**
37052  * @class Roo.bootstrap.layout.Manager
37053  * @extends Roo.bootstrap.Component
37054  * Base class for layout managers.
37055  */
37056 Roo.bootstrap.layout.Manager = function(config)
37057 {
37058     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37059
37060
37061
37062
37063
37064     /** false to disable window resize monitoring @type Boolean */
37065     this.monitorWindowResize = true;
37066     this.regions = {};
37067     this.addEvents({
37068         /**
37069          * @event layout
37070          * Fires when a layout is performed.
37071          * @param {Roo.LayoutManager} this
37072          */
37073         "layout" : true,
37074         /**
37075          * @event regionresized
37076          * Fires when the user resizes a region.
37077          * @param {Roo.LayoutRegion} region The resized region
37078          * @param {Number} newSize The new size (width for east/west, height for north/south)
37079          */
37080         "regionresized" : true,
37081         /**
37082          * @event regioncollapsed
37083          * Fires when a region is collapsed.
37084          * @param {Roo.LayoutRegion} region The collapsed region
37085          */
37086         "regioncollapsed" : true,
37087         /**
37088          * @event regionexpanded
37089          * Fires when a region is expanded.
37090          * @param {Roo.LayoutRegion} region The expanded region
37091          */
37092         "regionexpanded" : true
37093     });
37094     this.updating = false;
37095
37096     if (config.el) {
37097         this.el = Roo.get(config.el);
37098         this.initEvents();
37099     }
37100
37101 };
37102
37103 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37104
37105
37106     regions : null,
37107
37108     monitorWindowResize : true,
37109
37110
37111     updating : false,
37112
37113
37114     onRender : function(ct, position)
37115     {
37116         if(!this.el){
37117             this.el = Roo.get(ct);
37118             this.initEvents();
37119         }
37120         //this.fireEvent('render',this);
37121     },
37122
37123
37124     initEvents: function()
37125     {
37126
37127
37128         // ie scrollbar fix
37129         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37130             document.body.scroll = "no";
37131         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37132             this.el.position('relative');
37133         }
37134         this.id = this.el.id;
37135         this.el.addClass("roo-layout-container");
37136         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37137         if(this.el.dom != document.body ) {
37138             this.el.on('resize', this.layout,this);
37139             this.el.on('show', this.layout,this);
37140         }
37141
37142     },
37143
37144     /**
37145      * Returns true if this layout is currently being updated
37146      * @return {Boolean}
37147      */
37148     isUpdating : function(){
37149         return this.updating;
37150     },
37151
37152     /**
37153      * Suspend the LayoutManager from doing auto-layouts while
37154      * making multiple add or remove calls
37155      */
37156     beginUpdate : function(){
37157         this.updating = true;
37158     },
37159
37160     /**
37161      * Restore auto-layouts and optionally disable the manager from performing a layout
37162      * @param {Boolean} noLayout true to disable a layout update
37163      */
37164     endUpdate : function(noLayout){
37165         this.updating = false;
37166         if(!noLayout){
37167             this.layout();
37168         }
37169     },
37170
37171     layout: function(){
37172         // abstract...
37173     },
37174
37175     onRegionResized : function(region, newSize){
37176         this.fireEvent("regionresized", region, newSize);
37177         this.layout();
37178     },
37179
37180     onRegionCollapsed : function(region){
37181         this.fireEvent("regioncollapsed", region);
37182     },
37183
37184     onRegionExpanded : function(region){
37185         this.fireEvent("regionexpanded", region);
37186     },
37187
37188     /**
37189      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37190      * performs box-model adjustments.
37191      * @return {Object} The size as an object {width: (the width), height: (the height)}
37192      */
37193     getViewSize : function()
37194     {
37195         var size;
37196         if(this.el.dom != document.body){
37197             size = this.el.getSize();
37198         }else{
37199             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37200         }
37201         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37202         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37203         return size;
37204     },
37205
37206     /**
37207      * Returns the Element this layout is bound to.
37208      * @return {Roo.Element}
37209      */
37210     getEl : function(){
37211         return this.el;
37212     },
37213
37214     /**
37215      * Returns the specified region.
37216      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37217      * @return {Roo.LayoutRegion}
37218      */
37219     getRegion : function(target){
37220         return this.regions[target.toLowerCase()];
37221     },
37222
37223     onWindowResize : function(){
37224         if(this.monitorWindowResize){
37225             this.layout();
37226         }
37227     }
37228 });
37229 /*
37230  * Based on:
37231  * Ext JS Library 1.1.1
37232  * Copyright(c) 2006-2007, Ext JS, LLC.
37233  *
37234  * Originally Released Under LGPL - original licence link has changed is not relivant.
37235  *
37236  * Fork - LGPL
37237  * <script type="text/javascript">
37238  */
37239 /**
37240  * @class Roo.bootstrap.layout.Border
37241  * @extends Roo.bootstrap.layout.Manager
37242  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37243  * please see: examples/bootstrap/nested.html<br><br>
37244  
37245 <b>The container the layout is rendered into can be either the body element or any other element.
37246 If it is not the body element, the container needs to either be an absolute positioned element,
37247 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37248 the container size if it is not the body element.</b>
37249
37250 * @constructor
37251 * Create a new Border
37252 * @param {Object} config Configuration options
37253  */
37254 Roo.bootstrap.layout.Border = function(config){
37255     config = config || {};
37256     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37257     
37258     
37259     
37260     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37261         if(config[region]){
37262             config[region].region = region;
37263             this.addRegion(config[region]);
37264         }
37265     },this);
37266     
37267 };
37268
37269 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37270
37271 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37272     
37273     parent : false, // this might point to a 'nest' or a ???
37274     
37275     /**
37276      * Creates and adds a new region if it doesn't already exist.
37277      * @param {String} target The target region key (north, south, east, west or center).
37278      * @param {Object} config The regions config object
37279      * @return {BorderLayoutRegion} The new region
37280      */
37281     addRegion : function(config)
37282     {
37283         if(!this.regions[config.region]){
37284             var r = this.factory(config);
37285             this.bindRegion(r);
37286         }
37287         return this.regions[config.region];
37288     },
37289
37290     // private (kinda)
37291     bindRegion : function(r){
37292         this.regions[r.config.region] = r;
37293         
37294         r.on("visibilitychange",    this.layout, this);
37295         r.on("paneladded",          this.layout, this);
37296         r.on("panelremoved",        this.layout, this);
37297         r.on("invalidated",         this.layout, this);
37298         r.on("resized",             this.onRegionResized, this);
37299         r.on("collapsed",           this.onRegionCollapsed, this);
37300         r.on("expanded",            this.onRegionExpanded, this);
37301     },
37302
37303     /**
37304      * Performs a layout update.
37305      */
37306     layout : function()
37307     {
37308         if(this.updating) {
37309             return;
37310         }
37311         
37312         // render all the rebions if they have not been done alreayd?
37313         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37314             if(this.regions[region] && !this.regions[region].bodyEl){
37315                 this.regions[region].onRender(this.el)
37316             }
37317         },this);
37318         
37319         var size = this.getViewSize();
37320         var w = size.width;
37321         var h = size.height;
37322         var centerW = w;
37323         var centerH = h;
37324         var centerY = 0;
37325         var centerX = 0;
37326         //var x = 0, y = 0;
37327
37328         var rs = this.regions;
37329         var north = rs["north"];
37330         var south = rs["south"]; 
37331         var west = rs["west"];
37332         var east = rs["east"];
37333         var center = rs["center"];
37334         //if(this.hideOnLayout){ // not supported anymore
37335             //c.el.setStyle("display", "none");
37336         //}
37337         if(north && north.isVisible()){
37338             var b = north.getBox();
37339             var m = north.getMargins();
37340             b.width = w - (m.left+m.right);
37341             b.x = m.left;
37342             b.y = m.top;
37343             centerY = b.height + b.y + m.bottom;
37344             centerH -= centerY;
37345             north.updateBox(this.safeBox(b));
37346         }
37347         if(south && south.isVisible()){
37348             var b = south.getBox();
37349             var m = south.getMargins();
37350             b.width = w - (m.left+m.right);
37351             b.x = m.left;
37352             var totalHeight = (b.height + m.top + m.bottom);
37353             b.y = h - totalHeight + m.top;
37354             centerH -= totalHeight;
37355             south.updateBox(this.safeBox(b));
37356         }
37357         if(west && west.isVisible()){
37358             var b = west.getBox();
37359             var m = west.getMargins();
37360             b.height = centerH - (m.top+m.bottom);
37361             b.x = m.left;
37362             b.y = centerY + m.top;
37363             var totalWidth = (b.width + m.left + m.right);
37364             centerX += totalWidth;
37365             centerW -= totalWidth;
37366             west.updateBox(this.safeBox(b));
37367         }
37368         if(east && east.isVisible()){
37369             var b = east.getBox();
37370             var m = east.getMargins();
37371             b.height = centerH - (m.top+m.bottom);
37372             var totalWidth = (b.width + m.left + m.right);
37373             b.x = w - totalWidth + m.left;
37374             b.y = centerY + m.top;
37375             centerW -= totalWidth;
37376             east.updateBox(this.safeBox(b));
37377         }
37378         if(center){
37379             var m = center.getMargins();
37380             var centerBox = {
37381                 x: centerX + m.left,
37382                 y: centerY + m.top,
37383                 width: centerW - (m.left+m.right),
37384                 height: centerH - (m.top+m.bottom)
37385             };
37386             //if(this.hideOnLayout){
37387                 //center.el.setStyle("display", "block");
37388             //}
37389             center.updateBox(this.safeBox(centerBox));
37390         }
37391         this.el.repaint();
37392         this.fireEvent("layout", this);
37393     },
37394
37395     // private
37396     safeBox : function(box){
37397         box.width = Math.max(0, box.width);
37398         box.height = Math.max(0, box.height);
37399         return box;
37400     },
37401
37402     /**
37403      * Adds a ContentPanel (or subclass) to this layout.
37404      * @param {String} target The target region key (north, south, east, west or center).
37405      * @param {Roo.ContentPanel} panel The panel to add
37406      * @return {Roo.ContentPanel} The added panel
37407      */
37408     add : function(target, panel){
37409          
37410         target = target.toLowerCase();
37411         return this.regions[target].add(panel);
37412     },
37413
37414     /**
37415      * Remove a ContentPanel (or subclass) to this layout.
37416      * @param {String} target The target region key (north, south, east, west or center).
37417      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37418      * @return {Roo.ContentPanel} The removed panel
37419      */
37420     remove : function(target, panel){
37421         target = target.toLowerCase();
37422         return this.regions[target].remove(panel);
37423     },
37424
37425     /**
37426      * Searches all regions for a panel with the specified id
37427      * @param {String} panelId
37428      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37429      */
37430     findPanel : function(panelId){
37431         var rs = this.regions;
37432         for(var target in rs){
37433             if(typeof rs[target] != "function"){
37434                 var p = rs[target].getPanel(panelId);
37435                 if(p){
37436                     return p;
37437                 }
37438             }
37439         }
37440         return null;
37441     },
37442
37443     /**
37444      * Searches all regions for a panel with the specified id and activates (shows) it.
37445      * @param {String/ContentPanel} panelId The panels id or the panel itself
37446      * @return {Roo.ContentPanel} The shown panel or null
37447      */
37448     showPanel : function(panelId) {
37449       var rs = this.regions;
37450       for(var target in rs){
37451          var r = rs[target];
37452          if(typeof r != "function"){
37453             if(r.hasPanel(panelId)){
37454                return r.showPanel(panelId);
37455             }
37456          }
37457       }
37458       return null;
37459    },
37460
37461    /**
37462      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37463      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37464      */
37465    /*
37466     restoreState : function(provider){
37467         if(!provider){
37468             provider = Roo.state.Manager;
37469         }
37470         var sm = new Roo.LayoutStateManager();
37471         sm.init(this, provider);
37472     },
37473 */
37474  
37475  
37476     /**
37477      * Adds a xtype elements to the layout.
37478      * <pre><code>
37479
37480 layout.addxtype({
37481        xtype : 'ContentPanel',
37482        region: 'west',
37483        items: [ .... ]
37484    }
37485 );
37486
37487 layout.addxtype({
37488         xtype : 'NestedLayoutPanel',
37489         region: 'west',
37490         layout: {
37491            center: { },
37492            west: { }   
37493         },
37494         items : [ ... list of content panels or nested layout panels.. ]
37495    }
37496 );
37497 </code></pre>
37498      * @param {Object} cfg Xtype definition of item to add.
37499      */
37500     addxtype : function(cfg)
37501     {
37502         // basically accepts a pannel...
37503         // can accept a layout region..!?!?
37504         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37505         
37506         
37507         // theory?  children can only be panels??
37508         
37509         //if (!cfg.xtype.match(/Panel$/)) {
37510         //    return false;
37511         //}
37512         var ret = false;
37513         
37514         if (typeof(cfg.region) == 'undefined') {
37515             Roo.log("Failed to add Panel, region was not set");
37516             Roo.log(cfg);
37517             return false;
37518         }
37519         var region = cfg.region;
37520         delete cfg.region;
37521         
37522           
37523         var xitems = [];
37524         if (cfg.items) {
37525             xitems = cfg.items;
37526             delete cfg.items;
37527         }
37528         var nb = false;
37529         
37530         if ( region == 'center') {
37531             Roo.log("Center: " + cfg.title);
37532         }
37533         
37534         
37535         switch(cfg.xtype) 
37536         {
37537             case 'Content':  // ContentPanel (el, cfg)
37538             case 'Scroll':  // ContentPanel (el, cfg)
37539             case 'View': 
37540                 cfg.autoCreate = cfg.autoCreate || true;
37541                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37542                 //} else {
37543                 //    var el = this.el.createChild();
37544                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37545                 //}
37546                 
37547                 this.add(region, ret);
37548                 break;
37549             
37550             /*
37551             case 'TreePanel': // our new panel!
37552                 cfg.el = this.el.createChild();
37553                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37554                 this.add(region, ret);
37555                 break;
37556             */
37557             
37558             case 'Nest': 
37559                 // create a new Layout (which is  a Border Layout...
37560                 
37561                 var clayout = cfg.layout;
37562                 clayout.el  = this.el.createChild();
37563                 clayout.items   = clayout.items  || [];
37564                 
37565                 delete cfg.layout;
37566                 
37567                 // replace this exitems with the clayout ones..
37568                 xitems = clayout.items;
37569                  
37570                 // force background off if it's in center...
37571                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37572                     cfg.background = false;
37573                 }
37574                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37575                 
37576                 
37577                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37578                 //console.log('adding nested layout panel '  + cfg.toSource());
37579                 this.add(region, ret);
37580                 nb = {}; /// find first...
37581                 break;
37582             
37583             case 'Grid':
37584                 
37585                 // needs grid and region
37586                 
37587                 //var el = this.getRegion(region).el.createChild();
37588                 /*
37589                  *var el = this.el.createChild();
37590                 // create the grid first...
37591                 cfg.grid.container = el;
37592                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37593                 */
37594                 
37595                 if (region == 'center' && this.active ) {
37596                     cfg.background = false;
37597                 }
37598                 
37599                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37600                 
37601                 this.add(region, ret);
37602                 /*
37603                 if (cfg.background) {
37604                     // render grid on panel activation (if panel background)
37605                     ret.on('activate', function(gp) {
37606                         if (!gp.grid.rendered) {
37607                     //        gp.grid.render(el);
37608                         }
37609                     });
37610                 } else {
37611                   //  cfg.grid.render(el);
37612                 }
37613                 */
37614                 break;
37615            
37616            
37617             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37618                 // it was the old xcomponent building that caused this before.
37619                 // espeically if border is the top element in the tree.
37620                 ret = this;
37621                 break; 
37622                 
37623                     
37624                 
37625                 
37626                 
37627             default:
37628                 /*
37629                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37630                     
37631                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37632                     this.add(region, ret);
37633                 } else {
37634                 */
37635                     Roo.log(cfg);
37636                     throw "Can not add '" + cfg.xtype + "' to Border";
37637                     return null;
37638              
37639                                 
37640              
37641         }
37642         this.beginUpdate();
37643         // add children..
37644         var region = '';
37645         var abn = {};
37646         Roo.each(xitems, function(i)  {
37647             region = nb && i.region ? i.region : false;
37648             
37649             var add = ret.addxtype(i);
37650            
37651             if (region) {
37652                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37653                 if (!i.background) {
37654                     abn[region] = nb[region] ;
37655                 }
37656             }
37657             
37658         });
37659         this.endUpdate();
37660
37661         // make the last non-background panel active..
37662         //if (nb) { Roo.log(abn); }
37663         if (nb) {
37664             
37665             for(var r in abn) {
37666                 region = this.getRegion(r);
37667                 if (region) {
37668                     // tried using nb[r], but it does not work..
37669                      
37670                     region.showPanel(abn[r]);
37671                    
37672                 }
37673             }
37674         }
37675         return ret;
37676         
37677     },
37678     
37679     
37680 // private
37681     factory : function(cfg)
37682     {
37683         
37684         var validRegions = Roo.bootstrap.layout.Border.regions;
37685
37686         var target = cfg.region;
37687         cfg.mgr = this;
37688         
37689         var r = Roo.bootstrap.layout;
37690         Roo.log(target);
37691         switch(target){
37692             case "north":
37693                 return new r.North(cfg);
37694             case "south":
37695                 return new r.South(cfg);
37696             case "east":
37697                 return new r.East(cfg);
37698             case "west":
37699                 return new r.West(cfg);
37700             case "center":
37701                 return new r.Center(cfg);
37702         }
37703         throw 'Layout region "'+target+'" not supported.';
37704     }
37705     
37706     
37707 });
37708  /*
37709  * Based on:
37710  * Ext JS Library 1.1.1
37711  * Copyright(c) 2006-2007, Ext JS, LLC.
37712  *
37713  * Originally Released Under LGPL - original licence link has changed is not relivant.
37714  *
37715  * Fork - LGPL
37716  * <script type="text/javascript">
37717  */
37718  
37719 /**
37720  * @class Roo.bootstrap.layout.Basic
37721  * @extends Roo.util.Observable
37722  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37723  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37724  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37725  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37726  * @cfg {string}   region  the region that it inhabits..
37727  * @cfg {bool}   skipConfig skip config?
37728  * 
37729
37730  */
37731 Roo.bootstrap.layout.Basic = function(config){
37732     
37733     this.mgr = config.mgr;
37734     
37735     this.position = config.region;
37736     
37737     var skipConfig = config.skipConfig;
37738     
37739     this.events = {
37740         /**
37741          * @scope Roo.BasicLayoutRegion
37742          */
37743         
37744         /**
37745          * @event beforeremove
37746          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37747          * @param {Roo.LayoutRegion} this
37748          * @param {Roo.ContentPanel} panel The panel
37749          * @param {Object} e The cancel event object
37750          */
37751         "beforeremove" : true,
37752         /**
37753          * @event invalidated
37754          * Fires when the layout for this region is changed.
37755          * @param {Roo.LayoutRegion} this
37756          */
37757         "invalidated" : true,
37758         /**
37759          * @event visibilitychange
37760          * Fires when this region is shown or hidden 
37761          * @param {Roo.LayoutRegion} this
37762          * @param {Boolean} visibility true or false
37763          */
37764         "visibilitychange" : true,
37765         /**
37766          * @event paneladded
37767          * Fires when a panel is added. 
37768          * @param {Roo.LayoutRegion} this
37769          * @param {Roo.ContentPanel} panel The panel
37770          */
37771         "paneladded" : true,
37772         /**
37773          * @event panelremoved
37774          * Fires when a panel is removed. 
37775          * @param {Roo.LayoutRegion} this
37776          * @param {Roo.ContentPanel} panel The panel
37777          */
37778         "panelremoved" : true,
37779         /**
37780          * @event beforecollapse
37781          * Fires when this region before collapse.
37782          * @param {Roo.LayoutRegion} this
37783          */
37784         "beforecollapse" : true,
37785         /**
37786          * @event collapsed
37787          * Fires when this region is collapsed.
37788          * @param {Roo.LayoutRegion} this
37789          */
37790         "collapsed" : true,
37791         /**
37792          * @event expanded
37793          * Fires when this region is expanded.
37794          * @param {Roo.LayoutRegion} this
37795          */
37796         "expanded" : true,
37797         /**
37798          * @event slideshow
37799          * Fires when this region is slid into view.
37800          * @param {Roo.LayoutRegion} this
37801          */
37802         "slideshow" : true,
37803         /**
37804          * @event slidehide
37805          * Fires when this region slides out of view. 
37806          * @param {Roo.LayoutRegion} this
37807          */
37808         "slidehide" : true,
37809         /**
37810          * @event panelactivated
37811          * Fires when a panel is activated. 
37812          * @param {Roo.LayoutRegion} this
37813          * @param {Roo.ContentPanel} panel The activated panel
37814          */
37815         "panelactivated" : true,
37816         /**
37817          * @event resized
37818          * Fires when the user resizes this region. 
37819          * @param {Roo.LayoutRegion} this
37820          * @param {Number} newSize The new size (width for east/west, height for north/south)
37821          */
37822         "resized" : true
37823     };
37824     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37825     this.panels = new Roo.util.MixedCollection();
37826     this.panels.getKey = this.getPanelId.createDelegate(this);
37827     this.box = null;
37828     this.activePanel = null;
37829     // ensure listeners are added...
37830     
37831     if (config.listeners || config.events) {
37832         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37833             listeners : config.listeners || {},
37834             events : config.events || {}
37835         });
37836     }
37837     
37838     if(skipConfig !== true){
37839         this.applyConfig(config);
37840     }
37841 };
37842
37843 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37844 {
37845     getPanelId : function(p){
37846         return p.getId();
37847     },
37848     
37849     applyConfig : function(config){
37850         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37851         this.config = config;
37852         
37853     },
37854     
37855     /**
37856      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37857      * the width, for horizontal (north, south) the height.
37858      * @param {Number} newSize The new width or height
37859      */
37860     resizeTo : function(newSize){
37861         var el = this.el ? this.el :
37862                  (this.activePanel ? this.activePanel.getEl() : null);
37863         if(el){
37864             switch(this.position){
37865                 case "east":
37866                 case "west":
37867                     el.setWidth(newSize);
37868                     this.fireEvent("resized", this, newSize);
37869                 break;
37870                 case "north":
37871                 case "south":
37872                     el.setHeight(newSize);
37873                     this.fireEvent("resized", this, newSize);
37874                 break;                
37875             }
37876         }
37877     },
37878     
37879     getBox : function(){
37880         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37881     },
37882     
37883     getMargins : function(){
37884         return this.margins;
37885     },
37886     
37887     updateBox : function(box){
37888         this.box = box;
37889         var el = this.activePanel.getEl();
37890         el.dom.style.left = box.x + "px";
37891         el.dom.style.top = box.y + "px";
37892         this.activePanel.setSize(box.width, box.height);
37893     },
37894     
37895     /**
37896      * Returns the container element for this region.
37897      * @return {Roo.Element}
37898      */
37899     getEl : function(){
37900         return this.activePanel;
37901     },
37902     
37903     /**
37904      * Returns true if this region is currently visible.
37905      * @return {Boolean}
37906      */
37907     isVisible : function(){
37908         return this.activePanel ? true : false;
37909     },
37910     
37911     setActivePanel : function(panel){
37912         panel = this.getPanel(panel);
37913         if(this.activePanel && this.activePanel != panel){
37914             this.activePanel.setActiveState(false);
37915             this.activePanel.getEl().setLeftTop(-10000,-10000);
37916         }
37917         this.activePanel = panel;
37918         panel.setActiveState(true);
37919         if(this.box){
37920             panel.setSize(this.box.width, this.box.height);
37921         }
37922         this.fireEvent("panelactivated", this, panel);
37923         this.fireEvent("invalidated");
37924     },
37925     
37926     /**
37927      * Show the specified panel.
37928      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37929      * @return {Roo.ContentPanel} The shown panel or null
37930      */
37931     showPanel : function(panel){
37932         panel = this.getPanel(panel);
37933         if(panel){
37934             this.setActivePanel(panel);
37935         }
37936         return panel;
37937     },
37938     
37939     /**
37940      * Get the active panel for this region.
37941      * @return {Roo.ContentPanel} The active panel or null
37942      */
37943     getActivePanel : function(){
37944         return this.activePanel;
37945     },
37946     
37947     /**
37948      * Add the passed ContentPanel(s)
37949      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37950      * @return {Roo.ContentPanel} The panel added (if only one was added)
37951      */
37952     add : function(panel){
37953         if(arguments.length > 1){
37954             for(var i = 0, len = arguments.length; i < len; i++) {
37955                 this.add(arguments[i]);
37956             }
37957             return null;
37958         }
37959         if(this.hasPanel(panel)){
37960             this.showPanel(panel);
37961             return panel;
37962         }
37963         var el = panel.getEl();
37964         if(el.dom.parentNode != this.mgr.el.dom){
37965             this.mgr.el.dom.appendChild(el.dom);
37966         }
37967         if(panel.setRegion){
37968             panel.setRegion(this);
37969         }
37970         this.panels.add(panel);
37971         el.setStyle("position", "absolute");
37972         if(!panel.background){
37973             this.setActivePanel(panel);
37974             if(this.config.initialSize && this.panels.getCount()==1){
37975                 this.resizeTo(this.config.initialSize);
37976             }
37977         }
37978         this.fireEvent("paneladded", this, panel);
37979         return panel;
37980     },
37981     
37982     /**
37983      * Returns true if the panel is in this region.
37984      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37985      * @return {Boolean}
37986      */
37987     hasPanel : function(panel){
37988         if(typeof panel == "object"){ // must be panel obj
37989             panel = panel.getId();
37990         }
37991         return this.getPanel(panel) ? true : false;
37992     },
37993     
37994     /**
37995      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37996      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37997      * @param {Boolean} preservePanel Overrides the config preservePanel option
37998      * @return {Roo.ContentPanel} The panel that was removed
37999      */
38000     remove : function(panel, preservePanel){
38001         panel = this.getPanel(panel);
38002         if(!panel){
38003             return null;
38004         }
38005         var e = {};
38006         this.fireEvent("beforeremove", this, panel, e);
38007         if(e.cancel === true){
38008             return null;
38009         }
38010         var panelId = panel.getId();
38011         this.panels.removeKey(panelId);
38012         return panel;
38013     },
38014     
38015     /**
38016      * Returns the panel specified or null if it's not in this region.
38017      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38018      * @return {Roo.ContentPanel}
38019      */
38020     getPanel : function(id){
38021         if(typeof id == "object"){ // must be panel obj
38022             return id;
38023         }
38024         return this.panels.get(id);
38025     },
38026     
38027     /**
38028      * Returns this regions position (north/south/east/west/center).
38029      * @return {String} 
38030      */
38031     getPosition: function(){
38032         return this.position;    
38033     }
38034 });/*
38035  * Based on:
38036  * Ext JS Library 1.1.1
38037  * Copyright(c) 2006-2007, Ext JS, LLC.
38038  *
38039  * Originally Released Under LGPL - original licence link has changed is not relivant.
38040  *
38041  * Fork - LGPL
38042  * <script type="text/javascript">
38043  */
38044  
38045 /**
38046  * @class Roo.bootstrap.layout.Region
38047  * @extends Roo.bootstrap.layout.Basic
38048  * This class represents a region in a layout manager.
38049  
38050  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38051  * @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})
38052  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38053  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38054  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38055  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38056  * @cfg {String}    title           The title for the region (overrides panel titles)
38057  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38058  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38059  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38060  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38061  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38062  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38063  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38064  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38065  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38066  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38067
38068  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38069  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38070  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38071  * @cfg {Number}    width           For East/West panels
38072  * @cfg {Number}    height          For North/South panels
38073  * @cfg {Boolean}   split           To show the splitter
38074  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38075  * 
38076  * @cfg {string}   cls             Extra CSS classes to add to region
38077  * 
38078  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38079  * @cfg {string}   region  the region that it inhabits..
38080  *
38081
38082  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38083  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38084
38085  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38086  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38087  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38088  */
38089 Roo.bootstrap.layout.Region = function(config)
38090 {
38091     this.applyConfig(config);
38092
38093     var mgr = config.mgr;
38094     var pos = config.region;
38095     config.skipConfig = true;
38096     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38097     
38098     if (mgr.el) {
38099         this.onRender(mgr.el);   
38100     }
38101      
38102     this.visible = true;
38103     this.collapsed = false;
38104     this.unrendered_panels = [];
38105 };
38106
38107 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38108
38109     position: '', // set by wrapper (eg. north/south etc..)
38110     unrendered_panels : null,  // unrendered panels.
38111     
38112     tabPosition : false,
38113     
38114     mgr: false, // points to 'Border'
38115     
38116     
38117     createBody : function(){
38118         /** This region's body element 
38119         * @type Roo.Element */
38120         this.bodyEl = this.el.createChild({
38121                 tag: "div",
38122                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38123         });
38124     },
38125
38126     onRender: function(ctr, pos)
38127     {
38128         var dh = Roo.DomHelper;
38129         /** This region's container element 
38130         * @type Roo.Element */
38131         this.el = dh.append(ctr.dom, {
38132                 tag: "div",
38133                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38134             }, true);
38135         /** This region's title element 
38136         * @type Roo.Element */
38137     
38138         this.titleEl = dh.append(this.el.dom,  {
38139                 tag: "div",
38140                 unselectable: "on",
38141                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38142                 children:[
38143                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38144                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38145                 ]
38146             }, true);
38147         
38148         this.titleEl.enableDisplayMode();
38149         /** This region's title text element 
38150         * @type HTMLElement */
38151         this.titleTextEl = this.titleEl.dom.firstChild;
38152         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38153         /*
38154         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38155         this.closeBtn.enableDisplayMode();
38156         this.closeBtn.on("click", this.closeClicked, this);
38157         this.closeBtn.hide();
38158     */
38159         this.createBody(this.config);
38160         if(this.config.hideWhenEmpty){
38161             this.hide();
38162             this.on("paneladded", this.validateVisibility, this);
38163             this.on("panelremoved", this.validateVisibility, this);
38164         }
38165         if(this.autoScroll){
38166             this.bodyEl.setStyle("overflow", "auto");
38167         }else{
38168             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38169         }
38170         //if(c.titlebar !== false){
38171             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38172                 this.titleEl.hide();
38173             }else{
38174                 this.titleEl.show();
38175                 if(this.config.title){
38176                     this.titleTextEl.innerHTML = this.config.title;
38177                 }
38178             }
38179         //}
38180         if(this.config.collapsed){
38181             this.collapse(true);
38182         }
38183         if(this.config.hidden){
38184             this.hide();
38185         }
38186         
38187         if (this.unrendered_panels && this.unrendered_panels.length) {
38188             for (var i =0;i< this.unrendered_panels.length; i++) {
38189                 this.add(this.unrendered_panels[i]);
38190             }
38191             this.unrendered_panels = null;
38192             
38193         }
38194         
38195     },
38196     
38197     applyConfig : function(c)
38198     {
38199         /*
38200          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38201             var dh = Roo.DomHelper;
38202             if(c.titlebar !== false){
38203                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38204                 this.collapseBtn.on("click", this.collapse, this);
38205                 this.collapseBtn.enableDisplayMode();
38206                 /*
38207                 if(c.showPin === true || this.showPin){
38208                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38209                     this.stickBtn.enableDisplayMode();
38210                     this.stickBtn.on("click", this.expand, this);
38211                     this.stickBtn.hide();
38212                 }
38213                 
38214             }
38215             */
38216             /** This region's collapsed element
38217             * @type Roo.Element */
38218             /*
38219              *
38220             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38221                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38222             ]}, true);
38223             
38224             if(c.floatable !== false){
38225                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38226                this.collapsedEl.on("click", this.collapseClick, this);
38227             }
38228
38229             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38230                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38231                    id: "message", unselectable: "on", style:{"float":"left"}});
38232                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38233              }
38234             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38235             this.expandBtn.on("click", this.expand, this);
38236             
38237         }
38238         
38239         if(this.collapseBtn){
38240             this.collapseBtn.setVisible(c.collapsible == true);
38241         }
38242         
38243         this.cmargins = c.cmargins || this.cmargins ||
38244                          (this.position == "west" || this.position == "east" ?
38245                              {top: 0, left: 2, right:2, bottom: 0} :
38246                              {top: 2, left: 0, right:0, bottom: 2});
38247         */
38248         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38249         
38250         
38251         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38252         
38253         this.autoScroll = c.autoScroll || false;
38254         
38255         
38256        
38257         
38258         this.duration = c.duration || .30;
38259         this.slideDuration = c.slideDuration || .45;
38260         this.config = c;
38261        
38262     },
38263     /**
38264      * Returns true if this region is currently visible.
38265      * @return {Boolean}
38266      */
38267     isVisible : function(){
38268         return this.visible;
38269     },
38270
38271     /**
38272      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38273      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38274      */
38275     //setCollapsedTitle : function(title){
38276     //    title = title || "&#160;";
38277      //   if(this.collapsedTitleTextEl){
38278       //      this.collapsedTitleTextEl.innerHTML = title;
38279        // }
38280     //},
38281
38282     getBox : function(){
38283         var b;
38284       //  if(!this.collapsed){
38285             b = this.el.getBox(false, true);
38286        // }else{
38287           //  b = this.collapsedEl.getBox(false, true);
38288         //}
38289         return b;
38290     },
38291
38292     getMargins : function(){
38293         return this.margins;
38294         //return this.collapsed ? this.cmargins : this.margins;
38295     },
38296 /*
38297     highlight : function(){
38298         this.el.addClass("x-layout-panel-dragover");
38299     },
38300
38301     unhighlight : function(){
38302         this.el.removeClass("x-layout-panel-dragover");
38303     },
38304 */
38305     updateBox : function(box)
38306     {
38307         if (!this.bodyEl) {
38308             return; // not rendered yet..
38309         }
38310         
38311         this.box = box;
38312         if(!this.collapsed){
38313             this.el.dom.style.left = box.x + "px";
38314             this.el.dom.style.top = box.y + "px";
38315             this.updateBody(box.width, box.height);
38316         }else{
38317             this.collapsedEl.dom.style.left = box.x + "px";
38318             this.collapsedEl.dom.style.top = box.y + "px";
38319             this.collapsedEl.setSize(box.width, box.height);
38320         }
38321         if(this.tabs){
38322             this.tabs.autoSizeTabs();
38323         }
38324     },
38325
38326     updateBody : function(w, h)
38327     {
38328         if(w !== null){
38329             this.el.setWidth(w);
38330             w -= this.el.getBorderWidth("rl");
38331             if(this.config.adjustments){
38332                 w += this.config.adjustments[0];
38333             }
38334         }
38335         if(h !== null && h > 0){
38336             this.el.setHeight(h);
38337             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38338             h -= this.el.getBorderWidth("tb");
38339             if(this.config.adjustments){
38340                 h += this.config.adjustments[1];
38341             }
38342             this.bodyEl.setHeight(h);
38343             if(this.tabs){
38344                 h = this.tabs.syncHeight(h);
38345             }
38346         }
38347         if(this.panelSize){
38348             w = w !== null ? w : this.panelSize.width;
38349             h = h !== null ? h : this.panelSize.height;
38350         }
38351         if(this.activePanel){
38352             var el = this.activePanel.getEl();
38353             w = w !== null ? w : el.getWidth();
38354             h = h !== null ? h : el.getHeight();
38355             this.panelSize = {width: w, height: h};
38356             this.activePanel.setSize(w, h);
38357         }
38358         if(Roo.isIE && this.tabs){
38359             this.tabs.el.repaint();
38360         }
38361     },
38362
38363     /**
38364      * Returns the container element for this region.
38365      * @return {Roo.Element}
38366      */
38367     getEl : function(){
38368         return this.el;
38369     },
38370
38371     /**
38372      * Hides this region.
38373      */
38374     hide : function(){
38375         //if(!this.collapsed){
38376             this.el.dom.style.left = "-2000px";
38377             this.el.hide();
38378         //}else{
38379          //   this.collapsedEl.dom.style.left = "-2000px";
38380          //   this.collapsedEl.hide();
38381        // }
38382         this.visible = false;
38383         this.fireEvent("visibilitychange", this, false);
38384     },
38385
38386     /**
38387      * Shows this region if it was previously hidden.
38388      */
38389     show : function(){
38390         //if(!this.collapsed){
38391             this.el.show();
38392         //}else{
38393         //    this.collapsedEl.show();
38394        // }
38395         this.visible = true;
38396         this.fireEvent("visibilitychange", this, true);
38397     },
38398 /*
38399     closeClicked : function(){
38400         if(this.activePanel){
38401             this.remove(this.activePanel);
38402         }
38403     },
38404
38405     collapseClick : function(e){
38406         if(this.isSlid){
38407            e.stopPropagation();
38408            this.slideIn();
38409         }else{
38410            e.stopPropagation();
38411            this.slideOut();
38412         }
38413     },
38414 */
38415     /**
38416      * Collapses this region.
38417      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38418      */
38419     /*
38420     collapse : function(skipAnim, skipCheck = false){
38421         if(this.collapsed) {
38422             return;
38423         }
38424         
38425         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38426             
38427             this.collapsed = true;
38428             if(this.split){
38429                 this.split.el.hide();
38430             }
38431             if(this.config.animate && skipAnim !== true){
38432                 this.fireEvent("invalidated", this);
38433                 this.animateCollapse();
38434             }else{
38435                 this.el.setLocation(-20000,-20000);
38436                 this.el.hide();
38437                 this.collapsedEl.show();
38438                 this.fireEvent("collapsed", this);
38439                 this.fireEvent("invalidated", this);
38440             }
38441         }
38442         
38443     },
38444 */
38445     animateCollapse : function(){
38446         // overridden
38447     },
38448
38449     /**
38450      * Expands this region if it was previously collapsed.
38451      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38452      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38453      */
38454     /*
38455     expand : function(e, skipAnim){
38456         if(e) {
38457             e.stopPropagation();
38458         }
38459         if(!this.collapsed || this.el.hasActiveFx()) {
38460             return;
38461         }
38462         if(this.isSlid){
38463             this.afterSlideIn();
38464             skipAnim = true;
38465         }
38466         this.collapsed = false;
38467         if(this.config.animate && skipAnim !== true){
38468             this.animateExpand();
38469         }else{
38470             this.el.show();
38471             if(this.split){
38472                 this.split.el.show();
38473             }
38474             this.collapsedEl.setLocation(-2000,-2000);
38475             this.collapsedEl.hide();
38476             this.fireEvent("invalidated", this);
38477             this.fireEvent("expanded", this);
38478         }
38479     },
38480 */
38481     animateExpand : function(){
38482         // overridden
38483     },
38484
38485     initTabs : function()
38486     {
38487         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38488         
38489         var ts = new Roo.bootstrap.panel.Tabs({
38490             el: this.bodyEl.dom,
38491             region : this,
38492             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38493             disableTooltips: this.config.disableTabTips,
38494             toolbar : this.config.toolbar
38495         });
38496         
38497         if(this.config.hideTabs){
38498             ts.stripWrap.setDisplayed(false);
38499         }
38500         this.tabs = ts;
38501         ts.resizeTabs = this.config.resizeTabs === true;
38502         ts.minTabWidth = this.config.minTabWidth || 40;
38503         ts.maxTabWidth = this.config.maxTabWidth || 250;
38504         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38505         ts.monitorResize = false;
38506         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38507         ts.bodyEl.addClass('roo-layout-tabs-body');
38508         this.panels.each(this.initPanelAsTab, this);
38509     },
38510
38511     initPanelAsTab : function(panel){
38512         var ti = this.tabs.addTab(
38513             panel.getEl().id,
38514             panel.getTitle(),
38515             null,
38516             this.config.closeOnTab && panel.isClosable(),
38517             panel.tpl
38518         );
38519         if(panel.tabTip !== undefined){
38520             ti.setTooltip(panel.tabTip);
38521         }
38522         ti.on("activate", function(){
38523               this.setActivePanel(panel);
38524         }, this);
38525         
38526         if(this.config.closeOnTab){
38527             ti.on("beforeclose", function(t, e){
38528                 e.cancel = true;
38529                 this.remove(panel);
38530             }, this);
38531         }
38532         
38533         panel.tabItem = ti;
38534         
38535         return ti;
38536     },
38537
38538     updatePanelTitle : function(panel, title)
38539     {
38540         if(this.activePanel == panel){
38541             this.updateTitle(title);
38542         }
38543         if(this.tabs){
38544             var ti = this.tabs.getTab(panel.getEl().id);
38545             ti.setText(title);
38546             if(panel.tabTip !== undefined){
38547                 ti.setTooltip(panel.tabTip);
38548             }
38549         }
38550     },
38551
38552     updateTitle : function(title){
38553         if(this.titleTextEl && !this.config.title){
38554             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38555         }
38556     },
38557
38558     setActivePanel : function(panel)
38559     {
38560         panel = this.getPanel(panel);
38561         if(this.activePanel && this.activePanel != panel){
38562             if(this.activePanel.setActiveState(false) === false){
38563                 return;
38564             }
38565         }
38566         this.activePanel = panel;
38567         panel.setActiveState(true);
38568         if(this.panelSize){
38569             panel.setSize(this.panelSize.width, this.panelSize.height);
38570         }
38571         if(this.closeBtn){
38572             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38573         }
38574         this.updateTitle(panel.getTitle());
38575         if(this.tabs){
38576             this.fireEvent("invalidated", this);
38577         }
38578         this.fireEvent("panelactivated", this, panel);
38579     },
38580
38581     /**
38582      * Shows the specified panel.
38583      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38584      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38585      */
38586     showPanel : function(panel)
38587     {
38588         panel = this.getPanel(panel);
38589         if(panel){
38590             if(this.tabs){
38591                 var tab = this.tabs.getTab(panel.getEl().id);
38592                 if(tab.isHidden()){
38593                     this.tabs.unhideTab(tab.id);
38594                 }
38595                 tab.activate();
38596             }else{
38597                 this.setActivePanel(panel);
38598             }
38599         }
38600         return panel;
38601     },
38602
38603     /**
38604      * Get the active panel for this region.
38605      * @return {Roo.ContentPanel} The active panel or null
38606      */
38607     getActivePanel : function(){
38608         return this.activePanel;
38609     },
38610
38611     validateVisibility : function(){
38612         if(this.panels.getCount() < 1){
38613             this.updateTitle("&#160;");
38614             this.closeBtn.hide();
38615             this.hide();
38616         }else{
38617             if(!this.isVisible()){
38618                 this.show();
38619             }
38620         }
38621     },
38622
38623     /**
38624      * Adds the passed ContentPanel(s) to this region.
38625      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38626      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38627      */
38628     add : function(panel)
38629     {
38630         if(arguments.length > 1){
38631             for(var i = 0, len = arguments.length; i < len; i++) {
38632                 this.add(arguments[i]);
38633             }
38634             return null;
38635         }
38636         
38637         // if we have not been rendered yet, then we can not really do much of this..
38638         if (!this.bodyEl) {
38639             this.unrendered_panels.push(panel);
38640             return panel;
38641         }
38642         
38643         
38644         
38645         
38646         if(this.hasPanel(panel)){
38647             this.showPanel(panel);
38648             return panel;
38649         }
38650         panel.setRegion(this);
38651         this.panels.add(panel);
38652        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38653             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38654             // and hide them... ???
38655             this.bodyEl.dom.appendChild(panel.getEl().dom);
38656             if(panel.background !== true){
38657                 this.setActivePanel(panel);
38658             }
38659             this.fireEvent("paneladded", this, panel);
38660             return panel;
38661         }
38662         */
38663         if(!this.tabs){
38664             this.initTabs();
38665         }else{
38666             this.initPanelAsTab(panel);
38667         }
38668         
38669         
38670         if(panel.background !== true){
38671             this.tabs.activate(panel.getEl().id);
38672         }
38673         this.fireEvent("paneladded", this, panel);
38674         return panel;
38675     },
38676
38677     /**
38678      * Hides the tab for the specified panel.
38679      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38680      */
38681     hidePanel : function(panel){
38682         if(this.tabs && (panel = this.getPanel(panel))){
38683             this.tabs.hideTab(panel.getEl().id);
38684         }
38685     },
38686
38687     /**
38688      * Unhides the tab for a previously hidden panel.
38689      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38690      */
38691     unhidePanel : function(panel){
38692         if(this.tabs && (panel = this.getPanel(panel))){
38693             this.tabs.unhideTab(panel.getEl().id);
38694         }
38695     },
38696
38697     clearPanels : function(){
38698         while(this.panels.getCount() > 0){
38699              this.remove(this.panels.first());
38700         }
38701     },
38702
38703     /**
38704      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38705      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38706      * @param {Boolean} preservePanel Overrides the config preservePanel option
38707      * @return {Roo.ContentPanel} The panel that was removed
38708      */
38709     remove : function(panel, preservePanel)
38710     {
38711         panel = this.getPanel(panel);
38712         if(!panel){
38713             return null;
38714         }
38715         var e = {};
38716         this.fireEvent("beforeremove", this, panel, e);
38717         if(e.cancel === true){
38718             return null;
38719         }
38720         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38721         var panelId = panel.getId();
38722         this.panels.removeKey(panelId);
38723         if(preservePanel){
38724             document.body.appendChild(panel.getEl().dom);
38725         }
38726         if(this.tabs){
38727             this.tabs.removeTab(panel.getEl().id);
38728         }else if (!preservePanel){
38729             this.bodyEl.dom.removeChild(panel.getEl().dom);
38730         }
38731         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38732             var p = this.panels.first();
38733             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38734             tempEl.appendChild(p.getEl().dom);
38735             this.bodyEl.update("");
38736             this.bodyEl.dom.appendChild(p.getEl().dom);
38737             tempEl = null;
38738             this.updateTitle(p.getTitle());
38739             this.tabs = null;
38740             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38741             this.setActivePanel(p);
38742         }
38743         panel.setRegion(null);
38744         if(this.activePanel == panel){
38745             this.activePanel = null;
38746         }
38747         if(this.config.autoDestroy !== false && preservePanel !== true){
38748             try{panel.destroy();}catch(e){}
38749         }
38750         this.fireEvent("panelremoved", this, panel);
38751         return panel;
38752     },
38753
38754     /**
38755      * Returns the TabPanel component used by this region
38756      * @return {Roo.TabPanel}
38757      */
38758     getTabs : function(){
38759         return this.tabs;
38760     },
38761
38762     createTool : function(parentEl, className){
38763         var btn = Roo.DomHelper.append(parentEl, {
38764             tag: "div",
38765             cls: "x-layout-tools-button",
38766             children: [ {
38767                 tag: "div",
38768                 cls: "roo-layout-tools-button-inner " + className,
38769                 html: "&#160;"
38770             }]
38771         }, true);
38772         btn.addClassOnOver("roo-layout-tools-button-over");
38773         return btn;
38774     }
38775 });/*
38776  * Based on:
38777  * Ext JS Library 1.1.1
38778  * Copyright(c) 2006-2007, Ext JS, LLC.
38779  *
38780  * Originally Released Under LGPL - original licence link has changed is not relivant.
38781  *
38782  * Fork - LGPL
38783  * <script type="text/javascript">
38784  */
38785  
38786
38787
38788 /**
38789  * @class Roo.SplitLayoutRegion
38790  * @extends Roo.LayoutRegion
38791  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38792  */
38793 Roo.bootstrap.layout.Split = function(config){
38794     this.cursor = config.cursor;
38795     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38796 };
38797
38798 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38799 {
38800     splitTip : "Drag to resize.",
38801     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38802     useSplitTips : false,
38803
38804     applyConfig : function(config){
38805         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38806     },
38807     
38808     onRender : function(ctr,pos) {
38809         
38810         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38811         if(!this.config.split){
38812             return;
38813         }
38814         if(!this.split){
38815             
38816             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38817                             tag: "div",
38818                             id: this.el.id + "-split",
38819                             cls: "roo-layout-split roo-layout-split-"+this.position,
38820                             html: "&#160;"
38821             });
38822             /** The SplitBar for this region 
38823             * @type Roo.SplitBar */
38824             // does not exist yet...
38825             Roo.log([this.position, this.orientation]);
38826             
38827             this.split = new Roo.bootstrap.SplitBar({
38828                 dragElement : splitEl,
38829                 resizingElement: this.el,
38830                 orientation : this.orientation
38831             });
38832             
38833             this.split.on("moved", this.onSplitMove, this);
38834             this.split.useShim = this.config.useShim === true;
38835             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38836             if(this.useSplitTips){
38837                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38838             }
38839             //if(config.collapsible){
38840             //    this.split.el.on("dblclick", this.collapse,  this);
38841             //}
38842         }
38843         if(typeof this.config.minSize != "undefined"){
38844             this.split.minSize = this.config.minSize;
38845         }
38846         if(typeof this.config.maxSize != "undefined"){
38847             this.split.maxSize = this.config.maxSize;
38848         }
38849         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38850             this.hideSplitter();
38851         }
38852         
38853     },
38854
38855     getHMaxSize : function(){
38856          var cmax = this.config.maxSize || 10000;
38857          var center = this.mgr.getRegion("center");
38858          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38859     },
38860
38861     getVMaxSize : function(){
38862          var cmax = this.config.maxSize || 10000;
38863          var center = this.mgr.getRegion("center");
38864          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38865     },
38866
38867     onSplitMove : function(split, newSize){
38868         this.fireEvent("resized", this, newSize);
38869     },
38870     
38871     /** 
38872      * Returns the {@link Roo.SplitBar} for this region.
38873      * @return {Roo.SplitBar}
38874      */
38875     getSplitBar : function(){
38876         return this.split;
38877     },
38878     
38879     hide : function(){
38880         this.hideSplitter();
38881         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38882     },
38883
38884     hideSplitter : function(){
38885         if(this.split){
38886             this.split.el.setLocation(-2000,-2000);
38887             this.split.el.hide();
38888         }
38889     },
38890
38891     show : function(){
38892         if(this.split){
38893             this.split.el.show();
38894         }
38895         Roo.bootstrap.layout.Split.superclass.show.call(this);
38896     },
38897     
38898     beforeSlide: function(){
38899         if(Roo.isGecko){// firefox overflow auto bug workaround
38900             this.bodyEl.clip();
38901             if(this.tabs) {
38902                 this.tabs.bodyEl.clip();
38903             }
38904             if(this.activePanel){
38905                 this.activePanel.getEl().clip();
38906                 
38907                 if(this.activePanel.beforeSlide){
38908                     this.activePanel.beforeSlide();
38909                 }
38910             }
38911         }
38912     },
38913     
38914     afterSlide : function(){
38915         if(Roo.isGecko){// firefox overflow auto bug workaround
38916             this.bodyEl.unclip();
38917             if(this.tabs) {
38918                 this.tabs.bodyEl.unclip();
38919             }
38920             if(this.activePanel){
38921                 this.activePanel.getEl().unclip();
38922                 if(this.activePanel.afterSlide){
38923                     this.activePanel.afterSlide();
38924                 }
38925             }
38926         }
38927     },
38928
38929     initAutoHide : function(){
38930         if(this.autoHide !== false){
38931             if(!this.autoHideHd){
38932                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38933                 this.autoHideHd = {
38934                     "mouseout": function(e){
38935                         if(!e.within(this.el, true)){
38936                             st.delay(500);
38937                         }
38938                     },
38939                     "mouseover" : function(e){
38940                         st.cancel();
38941                     },
38942                     scope : this
38943                 };
38944             }
38945             this.el.on(this.autoHideHd);
38946         }
38947     },
38948
38949     clearAutoHide : function(){
38950         if(this.autoHide !== false){
38951             this.el.un("mouseout", this.autoHideHd.mouseout);
38952             this.el.un("mouseover", this.autoHideHd.mouseover);
38953         }
38954     },
38955
38956     clearMonitor : function(){
38957         Roo.get(document).un("click", this.slideInIf, this);
38958     },
38959
38960     // these names are backwards but not changed for compat
38961     slideOut : function(){
38962         if(this.isSlid || this.el.hasActiveFx()){
38963             return;
38964         }
38965         this.isSlid = true;
38966         if(this.collapseBtn){
38967             this.collapseBtn.hide();
38968         }
38969         this.closeBtnState = this.closeBtn.getStyle('display');
38970         this.closeBtn.hide();
38971         if(this.stickBtn){
38972             this.stickBtn.show();
38973         }
38974         this.el.show();
38975         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38976         this.beforeSlide();
38977         this.el.setStyle("z-index", 10001);
38978         this.el.slideIn(this.getSlideAnchor(), {
38979             callback: function(){
38980                 this.afterSlide();
38981                 this.initAutoHide();
38982                 Roo.get(document).on("click", this.slideInIf, this);
38983                 this.fireEvent("slideshow", this);
38984             },
38985             scope: this,
38986             block: true
38987         });
38988     },
38989
38990     afterSlideIn : function(){
38991         this.clearAutoHide();
38992         this.isSlid = false;
38993         this.clearMonitor();
38994         this.el.setStyle("z-index", "");
38995         if(this.collapseBtn){
38996             this.collapseBtn.show();
38997         }
38998         this.closeBtn.setStyle('display', this.closeBtnState);
38999         if(this.stickBtn){
39000             this.stickBtn.hide();
39001         }
39002         this.fireEvent("slidehide", this);
39003     },
39004
39005     slideIn : function(cb){
39006         if(!this.isSlid || this.el.hasActiveFx()){
39007             Roo.callback(cb);
39008             return;
39009         }
39010         this.isSlid = false;
39011         this.beforeSlide();
39012         this.el.slideOut(this.getSlideAnchor(), {
39013             callback: function(){
39014                 this.el.setLeftTop(-10000, -10000);
39015                 this.afterSlide();
39016                 this.afterSlideIn();
39017                 Roo.callback(cb);
39018             },
39019             scope: this,
39020             block: true
39021         });
39022     },
39023     
39024     slideInIf : function(e){
39025         if(!e.within(this.el)){
39026             this.slideIn();
39027         }
39028     },
39029
39030     animateCollapse : function(){
39031         this.beforeSlide();
39032         this.el.setStyle("z-index", 20000);
39033         var anchor = this.getSlideAnchor();
39034         this.el.slideOut(anchor, {
39035             callback : function(){
39036                 this.el.setStyle("z-index", "");
39037                 this.collapsedEl.slideIn(anchor, {duration:.3});
39038                 this.afterSlide();
39039                 this.el.setLocation(-10000,-10000);
39040                 this.el.hide();
39041                 this.fireEvent("collapsed", this);
39042             },
39043             scope: this,
39044             block: true
39045         });
39046     },
39047
39048     animateExpand : function(){
39049         this.beforeSlide();
39050         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39051         this.el.setStyle("z-index", 20000);
39052         this.collapsedEl.hide({
39053             duration:.1
39054         });
39055         this.el.slideIn(this.getSlideAnchor(), {
39056             callback : function(){
39057                 this.el.setStyle("z-index", "");
39058                 this.afterSlide();
39059                 if(this.split){
39060                     this.split.el.show();
39061                 }
39062                 this.fireEvent("invalidated", this);
39063                 this.fireEvent("expanded", this);
39064             },
39065             scope: this,
39066             block: true
39067         });
39068     },
39069
39070     anchors : {
39071         "west" : "left",
39072         "east" : "right",
39073         "north" : "top",
39074         "south" : "bottom"
39075     },
39076
39077     sanchors : {
39078         "west" : "l",
39079         "east" : "r",
39080         "north" : "t",
39081         "south" : "b"
39082     },
39083
39084     canchors : {
39085         "west" : "tl-tr",
39086         "east" : "tr-tl",
39087         "north" : "tl-bl",
39088         "south" : "bl-tl"
39089     },
39090
39091     getAnchor : function(){
39092         return this.anchors[this.position];
39093     },
39094
39095     getCollapseAnchor : function(){
39096         return this.canchors[this.position];
39097     },
39098
39099     getSlideAnchor : function(){
39100         return this.sanchors[this.position];
39101     },
39102
39103     getAlignAdj : function(){
39104         var cm = this.cmargins;
39105         switch(this.position){
39106             case "west":
39107                 return [0, 0];
39108             break;
39109             case "east":
39110                 return [0, 0];
39111             break;
39112             case "north":
39113                 return [0, 0];
39114             break;
39115             case "south":
39116                 return [0, 0];
39117             break;
39118         }
39119     },
39120
39121     getExpandAdj : function(){
39122         var c = this.collapsedEl, cm = this.cmargins;
39123         switch(this.position){
39124             case "west":
39125                 return [-(cm.right+c.getWidth()+cm.left), 0];
39126             break;
39127             case "east":
39128                 return [cm.right+c.getWidth()+cm.left, 0];
39129             break;
39130             case "north":
39131                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39132             break;
39133             case "south":
39134                 return [0, cm.top+cm.bottom+c.getHeight()];
39135             break;
39136         }
39137     }
39138 });/*
39139  * Based on:
39140  * Ext JS Library 1.1.1
39141  * Copyright(c) 2006-2007, Ext JS, LLC.
39142  *
39143  * Originally Released Under LGPL - original licence link has changed is not relivant.
39144  *
39145  * Fork - LGPL
39146  * <script type="text/javascript">
39147  */
39148 /*
39149  * These classes are private internal classes
39150  */
39151 Roo.bootstrap.layout.Center = function(config){
39152     config.region = "center";
39153     Roo.bootstrap.layout.Region.call(this, config);
39154     this.visible = true;
39155     this.minWidth = config.minWidth || 20;
39156     this.minHeight = config.minHeight || 20;
39157 };
39158
39159 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39160     hide : function(){
39161         // center panel can't be hidden
39162     },
39163     
39164     show : function(){
39165         // center panel can't be hidden
39166     },
39167     
39168     getMinWidth: function(){
39169         return this.minWidth;
39170     },
39171     
39172     getMinHeight: function(){
39173         return this.minHeight;
39174     }
39175 });
39176
39177
39178
39179
39180  
39181
39182
39183
39184
39185
39186
39187 Roo.bootstrap.layout.North = function(config)
39188 {
39189     config.region = 'north';
39190     config.cursor = 'n-resize';
39191     
39192     Roo.bootstrap.layout.Split.call(this, config);
39193     
39194     
39195     if(this.split){
39196         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39197         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39198         this.split.el.addClass("roo-layout-split-v");
39199     }
39200     var size = config.initialSize || config.height;
39201     if(typeof size != "undefined"){
39202         this.el.setHeight(size);
39203     }
39204 };
39205 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39206 {
39207     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39208     
39209     
39210     
39211     getBox : function(){
39212         if(this.collapsed){
39213             return this.collapsedEl.getBox();
39214         }
39215         var box = this.el.getBox();
39216         if(this.split){
39217             box.height += this.split.el.getHeight();
39218         }
39219         return box;
39220     },
39221     
39222     updateBox : function(box){
39223         if(this.split && !this.collapsed){
39224             box.height -= this.split.el.getHeight();
39225             this.split.el.setLeft(box.x);
39226             this.split.el.setTop(box.y+box.height);
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
39237
39238
39239
39240 Roo.bootstrap.layout.South = function(config){
39241     config.region = 'south';
39242     config.cursor = 's-resize';
39243     Roo.bootstrap.layout.Split.call(this, config);
39244     if(this.split){
39245         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39246         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39247         this.split.el.addClass("roo-layout-split-v");
39248     }
39249     var size = config.initialSize || config.height;
39250     if(typeof size != "undefined"){
39251         this.el.setHeight(size);
39252     }
39253 };
39254
39255 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39256     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39257     getBox : function(){
39258         if(this.collapsed){
39259             return this.collapsedEl.getBox();
39260         }
39261         var box = this.el.getBox();
39262         if(this.split){
39263             var sh = this.split.el.getHeight();
39264             box.height += sh;
39265             box.y -= sh;
39266         }
39267         return box;
39268     },
39269     
39270     updateBox : function(box){
39271         if(this.split && !this.collapsed){
39272             var sh = this.split.el.getHeight();
39273             box.height -= sh;
39274             box.y += sh;
39275             this.split.el.setLeft(box.x);
39276             this.split.el.setTop(box.y-sh);
39277             this.split.el.setWidth(box.width);
39278         }
39279         if(this.collapsed){
39280             this.updateBody(box.width, null);
39281         }
39282         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39283     }
39284 });
39285
39286 Roo.bootstrap.layout.East = function(config){
39287     config.region = "east";
39288     config.cursor = "e-resize";
39289     Roo.bootstrap.layout.Split.call(this, config);
39290     if(this.split){
39291         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39292         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39293         this.split.el.addClass("roo-layout-split-h");
39294     }
39295     var size = config.initialSize || config.width;
39296     if(typeof size != "undefined"){
39297         this.el.setWidth(size);
39298     }
39299 };
39300 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39301     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39302     getBox : function(){
39303         if(this.collapsed){
39304             return this.collapsedEl.getBox();
39305         }
39306         var box = this.el.getBox();
39307         if(this.split){
39308             var sw = this.split.el.getWidth();
39309             box.width += sw;
39310             box.x -= sw;
39311         }
39312         return box;
39313     },
39314
39315     updateBox : function(box){
39316         if(this.split && !this.collapsed){
39317             var sw = this.split.el.getWidth();
39318             box.width -= sw;
39319             this.split.el.setLeft(box.x);
39320             this.split.el.setTop(box.y);
39321             this.split.el.setHeight(box.height);
39322             box.x += sw;
39323         }
39324         if(this.collapsed){
39325             this.updateBody(null, box.height);
39326         }
39327         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39328     }
39329 });
39330
39331 Roo.bootstrap.layout.West = function(config){
39332     config.region = "west";
39333     config.cursor = "w-resize";
39334     
39335     Roo.bootstrap.layout.Split.call(this, config);
39336     if(this.split){
39337         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39338         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39339         this.split.el.addClass("roo-layout-split-h");
39340     }
39341     
39342 };
39343 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39344     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39345     
39346     onRender: function(ctr, pos)
39347     {
39348         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39349         var size = this.config.initialSize || this.config.width;
39350         if(typeof size != "undefined"){
39351             this.el.setWidth(size);
39352         }
39353     },
39354     
39355     getBox : function(){
39356         if(this.collapsed){
39357             return this.collapsedEl.getBox();
39358         }
39359         var box = this.el.getBox();
39360         if(this.split){
39361             box.width += this.split.el.getWidth();
39362         }
39363         return box;
39364     },
39365     
39366     updateBox : function(box){
39367         if(this.split && !this.collapsed){
39368             var sw = this.split.el.getWidth();
39369             box.width -= sw;
39370             this.split.el.setLeft(box.x+box.width);
39371             this.split.el.setTop(box.y);
39372             this.split.el.setHeight(box.height);
39373         }
39374         if(this.collapsed){
39375             this.updateBody(null, box.height);
39376         }
39377         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39378     }
39379 });Roo.namespace("Roo.bootstrap.panel");/*
39380  * Based on:
39381  * Ext JS Library 1.1.1
39382  * Copyright(c) 2006-2007, Ext JS, LLC.
39383  *
39384  * Originally Released Under LGPL - original licence link has changed is not relivant.
39385  *
39386  * Fork - LGPL
39387  * <script type="text/javascript">
39388  */
39389 /**
39390  * @class Roo.ContentPanel
39391  * @extends Roo.util.Observable
39392  * A basic ContentPanel element.
39393  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39394  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39395  * @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
39396  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39397  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39398  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39399  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39400  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39401  * @cfg {String} title          The title for this panel
39402  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39403  * @cfg {String} url            Calls {@link #setUrl} with this value
39404  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39405  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39406  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39407  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39408  * @cfg {Boolean} badges render the badges
39409  * @cfg {String} cls  extra classes to use  
39410  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39411
39412  * @constructor
39413  * Create a new ContentPanel.
39414  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39415  * @param {String/Object} config A string to set only the title or a config object
39416  * @param {String} content (optional) Set the HTML content for this panel
39417  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39418  */
39419 Roo.bootstrap.panel.Content = function( config){
39420     
39421     this.tpl = config.tpl || false;
39422     
39423     var el = config.el;
39424     var content = config.content;
39425
39426     if(config.autoCreate){ // xtype is available if this is called from factory
39427         el = Roo.id();
39428     }
39429     this.el = Roo.get(el);
39430     if(!this.el && config && config.autoCreate){
39431         if(typeof config.autoCreate == "object"){
39432             if(!config.autoCreate.id){
39433                 config.autoCreate.id = config.id||el;
39434             }
39435             this.el = Roo.DomHelper.append(document.body,
39436                         config.autoCreate, true);
39437         }else{
39438             var elcfg =  {
39439                 tag: "div",
39440                 cls: (config.cls || '') +
39441                     (config.background ? ' bg-' + config.background : '') +
39442                     " roo-layout-inactive-content",
39443                 id: config.id||el
39444             };
39445             if (config.html) {
39446                 elcfg.html = config.html;
39447                 
39448             }
39449                         
39450             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39451         }
39452     } 
39453     this.closable = false;
39454     this.loaded = false;
39455     this.active = false;
39456    
39457       
39458     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39459         
39460         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39461         
39462         this.wrapEl = this.el; //this.el.wrap();
39463         var ti = [];
39464         if (config.toolbar.items) {
39465             ti = config.toolbar.items ;
39466             delete config.toolbar.items ;
39467         }
39468         
39469         var nitems = [];
39470         this.toolbar.render(this.wrapEl, 'before');
39471         for(var i =0;i < ti.length;i++) {
39472           //  Roo.log(['add child', items[i]]);
39473             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39474         }
39475         this.toolbar.items = nitems;
39476         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39477         delete config.toolbar;
39478         
39479     }
39480     /*
39481     // xtype created footer. - not sure if will work as we normally have to render first..
39482     if (this.footer && !this.footer.el && this.footer.xtype) {
39483         if (!this.wrapEl) {
39484             this.wrapEl = this.el.wrap();
39485         }
39486     
39487         this.footer.container = this.wrapEl.createChild();
39488          
39489         this.footer = Roo.factory(this.footer, Roo);
39490         
39491     }
39492     */
39493     
39494      if(typeof config == "string"){
39495         this.title = config;
39496     }else{
39497         Roo.apply(this, config);
39498     }
39499     
39500     if(this.resizeEl){
39501         this.resizeEl = Roo.get(this.resizeEl, true);
39502     }else{
39503         this.resizeEl = this.el;
39504     }
39505     // handle view.xtype
39506     
39507  
39508     
39509     
39510     this.addEvents({
39511         /**
39512          * @event activate
39513          * Fires when this panel is activated. 
39514          * @param {Roo.ContentPanel} this
39515          */
39516         "activate" : true,
39517         /**
39518          * @event deactivate
39519          * Fires when this panel is activated. 
39520          * @param {Roo.ContentPanel} this
39521          */
39522         "deactivate" : true,
39523
39524         /**
39525          * @event resize
39526          * Fires when this panel is resized if fitToFrame is true.
39527          * @param {Roo.ContentPanel} this
39528          * @param {Number} width The width after any component adjustments
39529          * @param {Number} height The height after any component adjustments
39530          */
39531         "resize" : true,
39532         
39533          /**
39534          * @event render
39535          * Fires when this tab is created
39536          * @param {Roo.ContentPanel} this
39537          */
39538         "render" : true
39539         
39540         
39541         
39542     });
39543     
39544
39545     
39546     
39547     if(this.autoScroll){
39548         this.resizeEl.setStyle("overflow", "auto");
39549     } else {
39550         // fix randome scrolling
39551         //this.el.on('scroll', function() {
39552         //    Roo.log('fix random scolling');
39553         //    this.scrollTo('top',0); 
39554         //});
39555     }
39556     content = content || this.content;
39557     if(content){
39558         this.setContent(content);
39559     }
39560     if(config && config.url){
39561         this.setUrl(this.url, this.params, this.loadOnce);
39562     }
39563     
39564     
39565     
39566     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39567     
39568     if (this.view && typeof(this.view.xtype) != 'undefined') {
39569         this.view.el = this.el.appendChild(document.createElement("div"));
39570         this.view = Roo.factory(this.view); 
39571         this.view.render  &&  this.view.render(false, '');  
39572     }
39573     
39574     
39575     this.fireEvent('render', this);
39576 };
39577
39578 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39579     
39580     cls : '',
39581     background : '',
39582     
39583     tabTip : '',
39584     
39585     setRegion : function(region){
39586         this.region = region;
39587         this.setActiveClass(region && !this.background);
39588     },
39589     
39590     
39591     setActiveClass: function(state)
39592     {
39593         if(state){
39594            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39595            this.el.setStyle('position','relative');
39596         }else{
39597            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39598            this.el.setStyle('position', 'absolute');
39599         } 
39600     },
39601     
39602     /**
39603      * Returns the toolbar for this Panel if one was configured. 
39604      * @return {Roo.Toolbar} 
39605      */
39606     getToolbar : function(){
39607         return this.toolbar;
39608     },
39609     
39610     setActiveState : function(active)
39611     {
39612         this.active = active;
39613         this.setActiveClass(active);
39614         if(!active){
39615             if(this.fireEvent("deactivate", this) === false){
39616                 return false;
39617             }
39618             return true;
39619         }
39620         this.fireEvent("activate", this);
39621         return true;
39622     },
39623     /**
39624      * Updates this panel's element
39625      * @param {String} content The new content
39626      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39627     */
39628     setContent : function(content, loadScripts){
39629         this.el.update(content, loadScripts);
39630     },
39631
39632     ignoreResize : function(w, h){
39633         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39634             return true;
39635         }else{
39636             this.lastSize = {width: w, height: h};
39637             return false;
39638         }
39639     },
39640     /**
39641      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39642      * @return {Roo.UpdateManager} The UpdateManager
39643      */
39644     getUpdateManager : function(){
39645         return this.el.getUpdateManager();
39646     },
39647      /**
39648      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39649      * @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:
39650 <pre><code>
39651 panel.load({
39652     url: "your-url.php",
39653     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39654     callback: yourFunction,
39655     scope: yourObject, //(optional scope)
39656     discardUrl: false,
39657     nocache: false,
39658     text: "Loading...",
39659     timeout: 30,
39660     scripts: false
39661 });
39662 </code></pre>
39663      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39664      * 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.
39665      * @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}
39666      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39667      * @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.
39668      * @return {Roo.ContentPanel} this
39669      */
39670     load : function(){
39671         var um = this.el.getUpdateManager();
39672         um.update.apply(um, arguments);
39673         return this;
39674     },
39675
39676
39677     /**
39678      * 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.
39679      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39680      * @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)
39681      * @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)
39682      * @return {Roo.UpdateManager} The UpdateManager
39683      */
39684     setUrl : function(url, params, loadOnce){
39685         if(this.refreshDelegate){
39686             this.removeListener("activate", this.refreshDelegate);
39687         }
39688         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39689         this.on("activate", this.refreshDelegate);
39690         return this.el.getUpdateManager();
39691     },
39692     
39693     _handleRefresh : function(url, params, loadOnce){
39694         if(!loadOnce || !this.loaded){
39695             var updater = this.el.getUpdateManager();
39696             updater.update(url, params, this._setLoaded.createDelegate(this));
39697         }
39698     },
39699     
39700     _setLoaded : function(){
39701         this.loaded = true;
39702     }, 
39703     
39704     /**
39705      * Returns this panel's id
39706      * @return {String} 
39707      */
39708     getId : function(){
39709         return this.el.id;
39710     },
39711     
39712     /** 
39713      * Returns this panel's element - used by regiosn to add.
39714      * @return {Roo.Element} 
39715      */
39716     getEl : function(){
39717         return this.wrapEl || this.el;
39718     },
39719     
39720    
39721     
39722     adjustForComponents : function(width, height)
39723     {
39724         //Roo.log('adjustForComponents ');
39725         if(this.resizeEl != this.el){
39726             width -= this.el.getFrameWidth('lr');
39727             height -= this.el.getFrameWidth('tb');
39728         }
39729         if(this.toolbar){
39730             var te = this.toolbar.getEl();
39731             te.setWidth(width);
39732             height -= te.getHeight();
39733         }
39734         if(this.footer){
39735             var te = this.footer.getEl();
39736             te.setWidth(width);
39737             height -= te.getHeight();
39738         }
39739         
39740         
39741         if(this.adjustments){
39742             width += this.adjustments[0];
39743             height += this.adjustments[1];
39744         }
39745         return {"width": width, "height": height};
39746     },
39747     
39748     setSize : function(width, height){
39749         if(this.fitToFrame && !this.ignoreResize(width, height)){
39750             if(this.fitContainer && this.resizeEl != this.el){
39751                 this.el.setSize(width, height);
39752             }
39753             var size = this.adjustForComponents(width, height);
39754             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39755             this.fireEvent('resize', this, size.width, size.height);
39756         }
39757     },
39758     
39759     /**
39760      * Returns this panel's title
39761      * @return {String} 
39762      */
39763     getTitle : function(){
39764         
39765         if (typeof(this.title) != 'object') {
39766             return this.title;
39767         }
39768         
39769         var t = '';
39770         for (var k in this.title) {
39771             if (!this.title.hasOwnProperty(k)) {
39772                 continue;
39773             }
39774             
39775             if (k.indexOf('-') >= 0) {
39776                 var s = k.split('-');
39777                 for (var i = 0; i<s.length; i++) {
39778                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39779                 }
39780             } else {
39781                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39782             }
39783         }
39784         return t;
39785     },
39786     
39787     /**
39788      * Set this panel's title
39789      * @param {String} title
39790      */
39791     setTitle : function(title){
39792         this.title = title;
39793         if(this.region){
39794             this.region.updatePanelTitle(this, title);
39795         }
39796     },
39797     
39798     /**
39799      * Returns true is this panel was configured to be closable
39800      * @return {Boolean} 
39801      */
39802     isClosable : function(){
39803         return this.closable;
39804     },
39805     
39806     beforeSlide : function(){
39807         this.el.clip();
39808         this.resizeEl.clip();
39809     },
39810     
39811     afterSlide : function(){
39812         this.el.unclip();
39813         this.resizeEl.unclip();
39814     },
39815     
39816     /**
39817      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39818      *   Will fail silently if the {@link #setUrl} method has not been called.
39819      *   This does not activate the panel, just updates its content.
39820      */
39821     refresh : function(){
39822         if(this.refreshDelegate){
39823            this.loaded = false;
39824            this.refreshDelegate();
39825         }
39826     },
39827     
39828     /**
39829      * Destroys this panel
39830      */
39831     destroy : function(){
39832         this.el.removeAllListeners();
39833         var tempEl = document.createElement("span");
39834         tempEl.appendChild(this.el.dom);
39835         tempEl.innerHTML = "";
39836         this.el.remove();
39837         this.el = null;
39838     },
39839     
39840     /**
39841      * form - if the content panel contains a form - this is a reference to it.
39842      * @type {Roo.form.Form}
39843      */
39844     form : false,
39845     /**
39846      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39847      *    This contains a reference to it.
39848      * @type {Roo.View}
39849      */
39850     view : false,
39851     
39852       /**
39853      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39854      * <pre><code>
39855
39856 layout.addxtype({
39857        xtype : 'Form',
39858        items: [ .... ]
39859    }
39860 );
39861
39862 </code></pre>
39863      * @param {Object} cfg Xtype definition of item to add.
39864      */
39865     
39866     
39867     getChildContainer: function () {
39868         return this.getEl();
39869     }
39870     
39871     
39872     /*
39873         var  ret = new Roo.factory(cfg);
39874         return ret;
39875         
39876         
39877         // add form..
39878         if (cfg.xtype.match(/^Form$/)) {
39879             
39880             var el;
39881             //if (this.footer) {
39882             //    el = this.footer.container.insertSibling(false, 'before');
39883             //} else {
39884                 el = this.el.createChild();
39885             //}
39886
39887             this.form = new  Roo.form.Form(cfg);
39888             
39889             
39890             if ( this.form.allItems.length) {
39891                 this.form.render(el.dom);
39892             }
39893             return this.form;
39894         }
39895         // should only have one of theses..
39896         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39897             // views.. should not be just added - used named prop 'view''
39898             
39899             cfg.el = this.el.appendChild(document.createElement("div"));
39900             // factory?
39901             
39902             var ret = new Roo.factory(cfg);
39903              
39904              ret.render && ret.render(false, ''); // render blank..
39905             this.view = ret;
39906             return ret;
39907         }
39908         return false;
39909     }
39910     \*/
39911 });
39912  
39913 /**
39914  * @class Roo.bootstrap.panel.Grid
39915  * @extends Roo.bootstrap.panel.Content
39916  * @constructor
39917  * Create a new GridPanel.
39918  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39919  * @param {Object} config A the config object
39920   
39921  */
39922
39923
39924
39925 Roo.bootstrap.panel.Grid = function(config)
39926 {
39927     
39928       
39929     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39930         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39931
39932     config.el = this.wrapper;
39933     //this.el = this.wrapper;
39934     
39935       if (config.container) {
39936         // ctor'ed from a Border/panel.grid
39937         
39938         
39939         this.wrapper.setStyle("overflow", "hidden");
39940         this.wrapper.addClass('roo-grid-container');
39941
39942     }
39943     
39944     
39945     if(config.toolbar){
39946         var tool_el = this.wrapper.createChild();    
39947         this.toolbar = Roo.factory(config.toolbar);
39948         var ti = [];
39949         if (config.toolbar.items) {
39950             ti = config.toolbar.items ;
39951             delete config.toolbar.items ;
39952         }
39953         
39954         var nitems = [];
39955         this.toolbar.render(tool_el);
39956         for(var i =0;i < ti.length;i++) {
39957           //  Roo.log(['add child', items[i]]);
39958             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39959         }
39960         this.toolbar.items = nitems;
39961         
39962         delete config.toolbar;
39963     }
39964     
39965     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39966     config.grid.scrollBody = true;;
39967     config.grid.monitorWindowResize = false; // turn off autosizing
39968     config.grid.autoHeight = false;
39969     config.grid.autoWidth = false;
39970     
39971     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39972     
39973     if (config.background) {
39974         // render grid on panel activation (if panel background)
39975         this.on('activate', function(gp) {
39976             if (!gp.grid.rendered) {
39977                 gp.grid.render(this.wrapper);
39978                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39979             }
39980         });
39981             
39982     } else {
39983         this.grid.render(this.wrapper);
39984         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39985
39986     }
39987     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39988     // ??? needed ??? config.el = this.wrapper;
39989     
39990     
39991     
39992   
39993     // xtype created footer. - not sure if will work as we normally have to render first..
39994     if (this.footer && !this.footer.el && this.footer.xtype) {
39995         
39996         var ctr = this.grid.getView().getFooterPanel(true);
39997         this.footer.dataSource = this.grid.dataSource;
39998         this.footer = Roo.factory(this.footer, Roo);
39999         this.footer.render(ctr);
40000         
40001     }
40002     
40003     
40004     
40005     
40006      
40007 };
40008
40009 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40010     getId : function(){
40011         return this.grid.id;
40012     },
40013     
40014     /**
40015      * Returns the grid for this panel
40016      * @return {Roo.bootstrap.Table} 
40017      */
40018     getGrid : function(){
40019         return this.grid;    
40020     },
40021     
40022     setSize : function(width, height){
40023         if(!this.ignoreResize(width, height)){
40024             var grid = this.grid;
40025             var size = this.adjustForComponents(width, height);
40026             // tfoot is not a footer?
40027           
40028             
40029             var gridel = grid.getGridEl();
40030             gridel.setSize(size.width, size.height);
40031             
40032             var tbd = grid.getGridEl().select('tbody', true).first();
40033             var thd = grid.getGridEl().select('thead',true).first();
40034             var tbf= grid.getGridEl().select('tfoot', true).first();
40035
40036             if (tbf) {
40037                 size.height -= thd.getHeight();
40038             }
40039             if (thd) {
40040                 size.height -= thd.getHeight();
40041             }
40042             
40043             tbd.setSize(size.width, size.height );
40044             // this is for the account management tab -seems to work there.
40045             var thd = grid.getGridEl().select('thead',true).first();
40046             //if (tbd) {
40047             //    tbd.setSize(size.width, size.height - thd.getHeight());
40048             //}
40049              
40050             grid.autoSize();
40051         }
40052     },
40053      
40054     
40055     
40056     beforeSlide : function(){
40057         this.grid.getView().scroller.clip();
40058     },
40059     
40060     afterSlide : function(){
40061         this.grid.getView().scroller.unclip();
40062     },
40063     
40064     destroy : function(){
40065         this.grid.destroy();
40066         delete this.grid;
40067         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40068     }
40069 });
40070
40071 /**
40072  * @class Roo.bootstrap.panel.Nest
40073  * @extends Roo.bootstrap.panel.Content
40074  * @constructor
40075  * Create a new Panel, that can contain a layout.Border.
40076  * 
40077  * 
40078  * @param {Roo.BorderLayout} layout The layout for this panel
40079  * @param {String/Object} config A string to set only the title or a config object
40080  */
40081 Roo.bootstrap.panel.Nest = function(config)
40082 {
40083     // construct with only one argument..
40084     /* FIXME - implement nicer consturctors
40085     if (layout.layout) {
40086         config = layout;
40087         layout = config.layout;
40088         delete config.layout;
40089     }
40090     if (layout.xtype && !layout.getEl) {
40091         // then layout needs constructing..
40092         layout = Roo.factory(layout, Roo);
40093     }
40094     */
40095     
40096     config.el =  config.layout.getEl();
40097     
40098     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40099     
40100     config.layout.monitorWindowResize = false; // turn off autosizing
40101     this.layout = config.layout;
40102     this.layout.getEl().addClass("roo-layout-nested-layout");
40103     this.layout.parent = this;
40104     
40105     
40106     
40107     
40108 };
40109
40110 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40111
40112     setSize : function(width, height){
40113         if(!this.ignoreResize(width, height)){
40114             var size = this.adjustForComponents(width, height);
40115             var el = this.layout.getEl();
40116             if (size.height < 1) {
40117                 el.setWidth(size.width);   
40118             } else {
40119                 el.setSize(size.width, size.height);
40120             }
40121             var touch = el.dom.offsetWidth;
40122             this.layout.layout();
40123             // ie requires a double layout on the first pass
40124             if(Roo.isIE && !this.initialized){
40125                 this.initialized = true;
40126                 this.layout.layout();
40127             }
40128         }
40129     },
40130     
40131     // activate all subpanels if not currently active..
40132     
40133     setActiveState : function(active){
40134         this.active = active;
40135         this.setActiveClass(active);
40136         
40137         if(!active){
40138             this.fireEvent("deactivate", this);
40139             return;
40140         }
40141         
40142         this.fireEvent("activate", this);
40143         // not sure if this should happen before or after..
40144         if (!this.layout) {
40145             return; // should not happen..
40146         }
40147         var reg = false;
40148         for (var r in this.layout.regions) {
40149             reg = this.layout.getRegion(r);
40150             if (reg.getActivePanel()) {
40151                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40152                 reg.setActivePanel(reg.getActivePanel());
40153                 continue;
40154             }
40155             if (!reg.panels.length) {
40156                 continue;
40157             }
40158             reg.showPanel(reg.getPanel(0));
40159         }
40160         
40161         
40162         
40163         
40164     },
40165     
40166     /**
40167      * Returns the nested BorderLayout for this panel
40168      * @return {Roo.BorderLayout} 
40169      */
40170     getLayout : function(){
40171         return this.layout;
40172     },
40173     
40174      /**
40175      * Adds a xtype elements to the layout of the nested panel
40176      * <pre><code>
40177
40178 panel.addxtype({
40179        xtype : 'ContentPanel',
40180        region: 'west',
40181        items: [ .... ]
40182    }
40183 );
40184
40185 panel.addxtype({
40186         xtype : 'NestedLayoutPanel',
40187         region: 'west',
40188         layout: {
40189            center: { },
40190            west: { }   
40191         },
40192         items : [ ... list of content panels or nested layout panels.. ]
40193    }
40194 );
40195 </code></pre>
40196      * @param {Object} cfg Xtype definition of item to add.
40197      */
40198     addxtype : function(cfg) {
40199         return this.layout.addxtype(cfg);
40200     
40201     }
40202 });/*
40203  * Based on:
40204  * Ext JS Library 1.1.1
40205  * Copyright(c) 2006-2007, Ext JS, LLC.
40206  *
40207  * Originally Released Under LGPL - original licence link has changed is not relivant.
40208  *
40209  * Fork - LGPL
40210  * <script type="text/javascript">
40211  */
40212 /**
40213  * @class Roo.TabPanel
40214  * @extends Roo.util.Observable
40215  * A lightweight tab container.
40216  * <br><br>
40217  * Usage:
40218  * <pre><code>
40219 // basic tabs 1, built from existing content
40220 var tabs = new Roo.TabPanel("tabs1");
40221 tabs.addTab("script", "View Script");
40222 tabs.addTab("markup", "View Markup");
40223 tabs.activate("script");
40224
40225 // more advanced tabs, built from javascript
40226 var jtabs = new Roo.TabPanel("jtabs");
40227 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40228
40229 // set up the UpdateManager
40230 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40231 var updater = tab2.getUpdateManager();
40232 updater.setDefaultUrl("ajax1.htm");
40233 tab2.on('activate', updater.refresh, updater, true);
40234
40235 // Use setUrl for Ajax loading
40236 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40237 tab3.setUrl("ajax2.htm", null, true);
40238
40239 // Disabled tab
40240 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40241 tab4.disable();
40242
40243 jtabs.activate("jtabs-1");
40244  * </code></pre>
40245  * @constructor
40246  * Create a new TabPanel.
40247  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40248  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40249  */
40250 Roo.bootstrap.panel.Tabs = function(config){
40251     /**
40252     * The container element for this TabPanel.
40253     * @type Roo.Element
40254     */
40255     this.el = Roo.get(config.el);
40256     delete config.el;
40257     if(config){
40258         if(typeof config == "boolean"){
40259             this.tabPosition = config ? "bottom" : "top";
40260         }else{
40261             Roo.apply(this, config);
40262         }
40263     }
40264     
40265     if(this.tabPosition == "bottom"){
40266         // if tabs are at the bottom = create the body first.
40267         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40268         this.el.addClass("roo-tabs-bottom");
40269     }
40270     // next create the tabs holders
40271     
40272     if (this.tabPosition == "west"){
40273         
40274         var reg = this.region; // fake it..
40275         while (reg) {
40276             if (!reg.mgr.parent) {
40277                 break;
40278             }
40279             reg = reg.mgr.parent.region;
40280         }
40281         Roo.log("got nest?");
40282         Roo.log(reg);
40283         if (reg.mgr.getRegion('west')) {
40284             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40285             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40286             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40287             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40288             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40289         
40290             
40291         }
40292         
40293         
40294     } else {
40295      
40296         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40297         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40298         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40299         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40300     }
40301     
40302     
40303     if(Roo.isIE){
40304         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40305     }
40306     
40307     // finally - if tabs are at the top, then create the body last..
40308     if(this.tabPosition != "bottom"){
40309         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40310          * @type Roo.Element
40311          */
40312         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40313         this.el.addClass("roo-tabs-top");
40314     }
40315     this.items = [];
40316
40317     this.bodyEl.setStyle("position", "relative");
40318
40319     this.active = null;
40320     this.activateDelegate = this.activate.createDelegate(this);
40321
40322     this.addEvents({
40323         /**
40324          * @event tabchange
40325          * Fires when the active tab changes
40326          * @param {Roo.TabPanel} this
40327          * @param {Roo.TabPanelItem} activePanel The new active tab
40328          */
40329         "tabchange": true,
40330         /**
40331          * @event beforetabchange
40332          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40333          * @param {Roo.TabPanel} this
40334          * @param {Object} e Set cancel to true on this object to cancel the tab change
40335          * @param {Roo.TabPanelItem} tab The tab being changed to
40336          */
40337         "beforetabchange" : true
40338     });
40339
40340     Roo.EventManager.onWindowResize(this.onResize, this);
40341     this.cpad = this.el.getPadding("lr");
40342     this.hiddenCount = 0;
40343
40344
40345     // toolbar on the tabbar support...
40346     if (this.toolbar) {
40347         alert("no toolbar support yet");
40348         this.toolbar  = false;
40349         /*
40350         var tcfg = this.toolbar;
40351         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40352         this.toolbar = new Roo.Toolbar(tcfg);
40353         if (Roo.isSafari) {
40354             var tbl = tcfg.container.child('table', true);
40355             tbl.setAttribute('width', '100%');
40356         }
40357         */
40358         
40359     }
40360    
40361
40362
40363     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40364 };
40365
40366 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40367     /*
40368      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40369      */
40370     tabPosition : "top",
40371     /*
40372      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40373      */
40374     currentTabWidth : 0,
40375     /*
40376      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40377      */
40378     minTabWidth : 40,
40379     /*
40380      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40381      */
40382     maxTabWidth : 250,
40383     /*
40384      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40385      */
40386     preferredTabWidth : 175,
40387     /*
40388      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40389      */
40390     resizeTabs : false,
40391     /*
40392      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40393      */
40394     monitorResize : true,
40395     /*
40396      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40397      */
40398     toolbar : false,  // set by caller..
40399     
40400     region : false, /// set by caller
40401     
40402     disableTooltips : true, // not used yet...
40403
40404     /**
40405      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40406      * @param {String} id The id of the div to use <b>or create</b>
40407      * @param {String} text The text for the tab
40408      * @param {String} content (optional) Content to put in the TabPanelItem body
40409      * @param {Boolean} closable (optional) True to create a close icon on the tab
40410      * @return {Roo.TabPanelItem} The created TabPanelItem
40411      */
40412     addTab : function(id, text, content, closable, tpl)
40413     {
40414         var item = new Roo.bootstrap.panel.TabItem({
40415             panel: this,
40416             id : id,
40417             text : text,
40418             closable : closable,
40419             tpl : tpl
40420         });
40421         this.addTabItem(item);
40422         if(content){
40423             item.setContent(content);
40424         }
40425         return item;
40426     },
40427
40428     /**
40429      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40430      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40431      * @return {Roo.TabPanelItem}
40432      */
40433     getTab : function(id){
40434         return this.items[id];
40435     },
40436
40437     /**
40438      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40439      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40440      */
40441     hideTab : function(id){
40442         var t = this.items[id];
40443         if(!t.isHidden()){
40444            t.setHidden(true);
40445            this.hiddenCount++;
40446            this.autoSizeTabs();
40447         }
40448     },
40449
40450     /**
40451      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40452      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40453      */
40454     unhideTab : function(id){
40455         var t = this.items[id];
40456         if(t.isHidden()){
40457            t.setHidden(false);
40458            this.hiddenCount--;
40459            this.autoSizeTabs();
40460         }
40461     },
40462
40463     /**
40464      * Adds an existing {@link Roo.TabPanelItem}.
40465      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40466      */
40467     addTabItem : function(item)
40468     {
40469         this.items[item.id] = item;
40470         this.items.push(item);
40471         this.autoSizeTabs();
40472       //  if(this.resizeTabs){
40473     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40474   //         this.autoSizeTabs();
40475 //        }else{
40476 //            item.autoSize();
40477        // }
40478     },
40479
40480     /**
40481      * Removes a {@link Roo.TabPanelItem}.
40482      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40483      */
40484     removeTab : function(id){
40485         var items = this.items;
40486         var tab = items[id];
40487         if(!tab) { return; }
40488         var index = items.indexOf(tab);
40489         if(this.active == tab && items.length > 1){
40490             var newTab = this.getNextAvailable(index);
40491             if(newTab) {
40492                 newTab.activate();
40493             }
40494         }
40495         this.stripEl.dom.removeChild(tab.pnode.dom);
40496         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40497             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40498         }
40499         items.splice(index, 1);
40500         delete this.items[tab.id];
40501         tab.fireEvent("close", tab);
40502         tab.purgeListeners();
40503         this.autoSizeTabs();
40504     },
40505
40506     getNextAvailable : function(start){
40507         var items = this.items;
40508         var index = start;
40509         // look for a next tab that will slide over to
40510         // replace the one being removed
40511         while(index < items.length){
40512             var item = items[++index];
40513             if(item && !item.isHidden()){
40514                 return item;
40515             }
40516         }
40517         // if one isn't found select the previous tab (on the left)
40518         index = start;
40519         while(index >= 0){
40520             var item = items[--index];
40521             if(item && !item.isHidden()){
40522                 return item;
40523             }
40524         }
40525         return null;
40526     },
40527
40528     /**
40529      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40530      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40531      */
40532     disableTab : function(id){
40533         var tab = this.items[id];
40534         if(tab && this.active != tab){
40535             tab.disable();
40536         }
40537     },
40538
40539     /**
40540      * Enables a {@link Roo.TabPanelItem} that is disabled.
40541      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40542      */
40543     enableTab : function(id){
40544         var tab = this.items[id];
40545         tab.enable();
40546     },
40547
40548     /**
40549      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40550      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40551      * @return {Roo.TabPanelItem} The TabPanelItem.
40552      */
40553     activate : function(id)
40554     {
40555         //Roo.log('activite:'  + id);
40556         
40557         var tab = this.items[id];
40558         if(!tab){
40559             return null;
40560         }
40561         if(tab == this.active || tab.disabled){
40562             return tab;
40563         }
40564         var e = {};
40565         this.fireEvent("beforetabchange", this, e, tab);
40566         if(e.cancel !== true && !tab.disabled){
40567             if(this.active){
40568                 this.active.hide();
40569             }
40570             this.active = this.items[id];
40571             this.active.show();
40572             this.fireEvent("tabchange", this, this.active);
40573         }
40574         return tab;
40575     },
40576
40577     /**
40578      * Gets the active {@link Roo.TabPanelItem}.
40579      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40580      */
40581     getActiveTab : function(){
40582         return this.active;
40583     },
40584
40585     /**
40586      * Updates the tab body element to fit the height of the container element
40587      * for overflow scrolling
40588      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40589      */
40590     syncHeight : function(targetHeight){
40591         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40592         var bm = this.bodyEl.getMargins();
40593         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40594         this.bodyEl.setHeight(newHeight);
40595         return newHeight;
40596     },
40597
40598     onResize : function(){
40599         if(this.monitorResize){
40600             this.autoSizeTabs();
40601         }
40602     },
40603
40604     /**
40605      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40606      */
40607     beginUpdate : function(){
40608         this.updating = true;
40609     },
40610
40611     /**
40612      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40613      */
40614     endUpdate : function(){
40615         this.updating = false;
40616         this.autoSizeTabs();
40617     },
40618
40619     /**
40620      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40621      */
40622     autoSizeTabs : function()
40623     {
40624         var count = this.items.length;
40625         var vcount = count - this.hiddenCount;
40626         
40627         if (vcount < 2) {
40628             this.stripEl.hide();
40629         } else {
40630             this.stripEl.show();
40631         }
40632         
40633         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40634             return;
40635         }
40636         
40637         
40638         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40639         var availWidth = Math.floor(w / vcount);
40640         var b = this.stripBody;
40641         if(b.getWidth() > w){
40642             var tabs = this.items;
40643             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40644             if(availWidth < this.minTabWidth){
40645                 /*if(!this.sleft){    // incomplete scrolling code
40646                     this.createScrollButtons();
40647                 }
40648                 this.showScroll();
40649                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40650             }
40651         }else{
40652             if(this.currentTabWidth < this.preferredTabWidth){
40653                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40654             }
40655         }
40656     },
40657
40658     /**
40659      * Returns the number of tabs in this TabPanel.
40660      * @return {Number}
40661      */
40662      getCount : function(){
40663          return this.items.length;
40664      },
40665
40666     /**
40667      * Resizes all the tabs to the passed width
40668      * @param {Number} The new width
40669      */
40670     setTabWidth : function(width){
40671         this.currentTabWidth = width;
40672         for(var i = 0, len = this.items.length; i < len; i++) {
40673                 if(!this.items[i].isHidden()) {
40674                 this.items[i].setWidth(width);
40675             }
40676         }
40677     },
40678
40679     /**
40680      * Destroys this TabPanel
40681      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40682      */
40683     destroy : function(removeEl){
40684         Roo.EventManager.removeResizeListener(this.onResize, this);
40685         for(var i = 0, len = this.items.length; i < len; i++){
40686             this.items[i].purgeListeners();
40687         }
40688         if(removeEl === true){
40689             this.el.update("");
40690             this.el.remove();
40691         }
40692     },
40693     
40694     createStrip : function(container)
40695     {
40696         var strip = document.createElement("nav");
40697         strip.className = Roo.bootstrap.version == 4 ?
40698             "navbar-light bg-light" : 
40699             "navbar navbar-default"; //"x-tabs-wrap";
40700         container.appendChild(strip);
40701         return strip;
40702     },
40703     
40704     createStripList : function(strip)
40705     {
40706         // div wrapper for retard IE
40707         // returns the "tr" element.
40708         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40709         //'<div class="x-tabs-strip-wrap">'+
40710           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40711           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40712         return strip.firstChild; //.firstChild.firstChild.firstChild;
40713     },
40714     createBody : function(container)
40715     {
40716         var body = document.createElement("div");
40717         Roo.id(body, "tab-body");
40718         //Roo.fly(body).addClass("x-tabs-body");
40719         Roo.fly(body).addClass("tab-content");
40720         container.appendChild(body);
40721         return body;
40722     },
40723     createItemBody :function(bodyEl, id){
40724         var body = Roo.getDom(id);
40725         if(!body){
40726             body = document.createElement("div");
40727             body.id = id;
40728         }
40729         //Roo.fly(body).addClass("x-tabs-item-body");
40730         Roo.fly(body).addClass("tab-pane");
40731          bodyEl.insertBefore(body, bodyEl.firstChild);
40732         return body;
40733     },
40734     /** @private */
40735     createStripElements :  function(stripEl, text, closable, tpl)
40736     {
40737         var td = document.createElement("li"); // was td..
40738         td.className = 'nav-item';
40739         
40740         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40741         
40742         
40743         stripEl.appendChild(td);
40744         /*if(closable){
40745             td.className = "x-tabs-closable";
40746             if(!this.closeTpl){
40747                 this.closeTpl = new Roo.Template(
40748                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40749                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40750                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40751                 );
40752             }
40753             var el = this.closeTpl.overwrite(td, {"text": text});
40754             var close = el.getElementsByTagName("div")[0];
40755             var inner = el.getElementsByTagName("em")[0];
40756             return {"el": el, "close": close, "inner": inner};
40757         } else {
40758         */
40759         // not sure what this is..
40760 //            if(!this.tabTpl){
40761                 //this.tabTpl = new Roo.Template(
40762                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40763                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40764                 //);
40765 //                this.tabTpl = new Roo.Template(
40766 //                   '<a href="#">' +
40767 //                   '<span unselectable="on"' +
40768 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40769 //                            ' >{text}</span></a>'
40770 //                );
40771 //                
40772 //            }
40773
40774
40775             var template = tpl || this.tabTpl || false;
40776             
40777             if(!template){
40778                 template =  new Roo.Template(
40779                         Roo.bootstrap.version == 4 ? 
40780                             (
40781                                 '<a class="nav-link" href="#" unselectable="on"' +
40782                                      (this.disableTooltips ? '' : ' title="{text}"') +
40783                                      ' >{text}</a>'
40784                             ) : (
40785                                 '<a class="nav-link" href="#">' +
40786                                 '<span unselectable="on"' +
40787                                          (this.disableTooltips ? '' : ' title="{text}"') +
40788                                     ' >{text}</span></a>'
40789                             )
40790                 );
40791             }
40792             
40793             switch (typeof(template)) {
40794                 case 'object' :
40795                     break;
40796                 case 'string' :
40797                     template = new Roo.Template(template);
40798                     break;
40799                 default :
40800                     break;
40801             }
40802             
40803             var el = template.overwrite(td, {"text": text});
40804             
40805             var inner = el.getElementsByTagName("span")[0];
40806             
40807             return {"el": el, "inner": inner};
40808             
40809     }
40810         
40811     
40812 });
40813
40814 /**
40815  * @class Roo.TabPanelItem
40816  * @extends Roo.util.Observable
40817  * Represents an individual item (tab plus body) in a TabPanel.
40818  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40819  * @param {String} id The id of this TabPanelItem
40820  * @param {String} text The text for the tab of this TabPanelItem
40821  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40822  */
40823 Roo.bootstrap.panel.TabItem = function(config){
40824     /**
40825      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40826      * @type Roo.TabPanel
40827      */
40828     this.tabPanel = config.panel;
40829     /**
40830      * The id for this TabPanelItem
40831      * @type String
40832      */
40833     this.id = config.id;
40834     /** @private */
40835     this.disabled = false;
40836     /** @private */
40837     this.text = config.text;
40838     /** @private */
40839     this.loaded = false;
40840     this.closable = config.closable;
40841
40842     /**
40843      * The body element for this TabPanelItem.
40844      * @type Roo.Element
40845      */
40846     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40847     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40848     this.bodyEl.setStyle("display", "block");
40849     this.bodyEl.setStyle("zoom", "1");
40850     //this.hideAction();
40851
40852     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40853     /** @private */
40854     this.el = Roo.get(els.el);
40855     this.inner = Roo.get(els.inner, true);
40856      this.textEl = Roo.bootstrap.version == 4 ?
40857         this.el : Roo.get(this.el.dom.firstChild, true);
40858
40859     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40860     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40861
40862     
40863 //    this.el.on("mousedown", this.onTabMouseDown, this);
40864     this.el.on("click", this.onTabClick, this);
40865     /** @private */
40866     if(config.closable){
40867         var c = Roo.get(els.close, true);
40868         c.dom.title = this.closeText;
40869         c.addClassOnOver("close-over");
40870         c.on("click", this.closeClick, this);
40871      }
40872
40873     this.addEvents({
40874          /**
40875          * @event activate
40876          * Fires when this tab becomes the active tab.
40877          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40878          * @param {Roo.TabPanelItem} this
40879          */
40880         "activate": true,
40881         /**
40882          * @event beforeclose
40883          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40884          * @param {Roo.TabPanelItem} this
40885          * @param {Object} e Set cancel to true on this object to cancel the close.
40886          */
40887         "beforeclose": true,
40888         /**
40889          * @event close
40890          * Fires when this tab is closed.
40891          * @param {Roo.TabPanelItem} this
40892          */
40893          "close": true,
40894         /**
40895          * @event deactivate
40896          * Fires when this tab is no longer the active tab.
40897          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40898          * @param {Roo.TabPanelItem} this
40899          */
40900          "deactivate" : true
40901     });
40902     this.hidden = false;
40903
40904     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40905 };
40906
40907 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40908            {
40909     purgeListeners : function(){
40910        Roo.util.Observable.prototype.purgeListeners.call(this);
40911        this.el.removeAllListeners();
40912     },
40913     /**
40914      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40915      */
40916     show : function(){
40917         this.status_node.addClass("active");
40918         this.showAction();
40919         if(Roo.isOpera){
40920             this.tabPanel.stripWrap.repaint();
40921         }
40922         this.fireEvent("activate", this.tabPanel, this);
40923     },
40924
40925     /**
40926      * Returns true if this tab is the active tab.
40927      * @return {Boolean}
40928      */
40929     isActive : function(){
40930         return this.tabPanel.getActiveTab() == this;
40931     },
40932
40933     /**
40934      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40935      */
40936     hide : function(){
40937         this.status_node.removeClass("active");
40938         this.hideAction();
40939         this.fireEvent("deactivate", this.tabPanel, this);
40940     },
40941
40942     hideAction : function(){
40943         this.bodyEl.hide();
40944         this.bodyEl.setStyle("position", "absolute");
40945         this.bodyEl.setLeft("-20000px");
40946         this.bodyEl.setTop("-20000px");
40947     },
40948
40949     showAction : function(){
40950         this.bodyEl.setStyle("position", "relative");
40951         this.bodyEl.setTop("");
40952         this.bodyEl.setLeft("");
40953         this.bodyEl.show();
40954     },
40955
40956     /**
40957      * Set the tooltip for the tab.
40958      * @param {String} tooltip The tab's tooltip
40959      */
40960     setTooltip : function(text){
40961         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40962             this.textEl.dom.qtip = text;
40963             this.textEl.dom.removeAttribute('title');
40964         }else{
40965             this.textEl.dom.title = text;
40966         }
40967     },
40968
40969     onTabClick : function(e){
40970         e.preventDefault();
40971         this.tabPanel.activate(this.id);
40972     },
40973
40974     onTabMouseDown : function(e){
40975         e.preventDefault();
40976         this.tabPanel.activate(this.id);
40977     },
40978 /*
40979     getWidth : function(){
40980         return this.inner.getWidth();
40981     },
40982
40983     setWidth : function(width){
40984         var iwidth = width - this.linode.getPadding("lr");
40985         this.inner.setWidth(iwidth);
40986         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40987         this.linode.setWidth(width);
40988     },
40989 */
40990     /**
40991      * Show or hide the tab
40992      * @param {Boolean} hidden True to hide or false to show.
40993      */
40994     setHidden : function(hidden){
40995         this.hidden = hidden;
40996         this.linode.setStyle("display", hidden ? "none" : "");
40997     },
40998
40999     /**
41000      * Returns true if this tab is "hidden"
41001      * @return {Boolean}
41002      */
41003     isHidden : function(){
41004         return this.hidden;
41005     },
41006
41007     /**
41008      * Returns the text for this tab
41009      * @return {String}
41010      */
41011     getText : function(){
41012         return this.text;
41013     },
41014     /*
41015     autoSize : function(){
41016         //this.el.beginMeasure();
41017         this.textEl.setWidth(1);
41018         /*
41019          *  #2804 [new] Tabs in Roojs
41020          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41021          */
41022         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41023         //this.el.endMeasure();
41024     //},
41025
41026     /**
41027      * Sets the text for the tab (Note: this also sets the tooltip text)
41028      * @param {String} text The tab's text and tooltip
41029      */
41030     setText : function(text){
41031         this.text = text;
41032         this.textEl.update(text);
41033         this.setTooltip(text);
41034         //if(!this.tabPanel.resizeTabs){
41035         //    this.autoSize();
41036         //}
41037     },
41038     /**
41039      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41040      */
41041     activate : function(){
41042         this.tabPanel.activate(this.id);
41043     },
41044
41045     /**
41046      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41047      */
41048     disable : function(){
41049         if(this.tabPanel.active != this){
41050             this.disabled = true;
41051             this.status_node.addClass("disabled");
41052         }
41053     },
41054
41055     /**
41056      * Enables this TabPanelItem if it was previously disabled.
41057      */
41058     enable : function(){
41059         this.disabled = false;
41060         this.status_node.removeClass("disabled");
41061     },
41062
41063     /**
41064      * Sets the content for this TabPanelItem.
41065      * @param {String} content The content
41066      * @param {Boolean} loadScripts true to look for and load scripts
41067      */
41068     setContent : function(content, loadScripts){
41069         this.bodyEl.update(content, loadScripts);
41070     },
41071
41072     /**
41073      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41074      * @return {Roo.UpdateManager} The UpdateManager
41075      */
41076     getUpdateManager : function(){
41077         return this.bodyEl.getUpdateManager();
41078     },
41079
41080     /**
41081      * Set a URL to be used to load the content for this TabPanelItem.
41082      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41083      * @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)
41084      * @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)
41085      * @return {Roo.UpdateManager} The UpdateManager
41086      */
41087     setUrl : function(url, params, loadOnce){
41088         if(this.refreshDelegate){
41089             this.un('activate', this.refreshDelegate);
41090         }
41091         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41092         this.on("activate", this.refreshDelegate);
41093         return this.bodyEl.getUpdateManager();
41094     },
41095
41096     /** @private */
41097     _handleRefresh : function(url, params, loadOnce){
41098         if(!loadOnce || !this.loaded){
41099             var updater = this.bodyEl.getUpdateManager();
41100             updater.update(url, params, this._setLoaded.createDelegate(this));
41101         }
41102     },
41103
41104     /**
41105      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41106      *   Will fail silently if the setUrl method has not been called.
41107      *   This does not activate the panel, just updates its content.
41108      */
41109     refresh : function(){
41110         if(this.refreshDelegate){
41111            this.loaded = false;
41112            this.refreshDelegate();
41113         }
41114     },
41115
41116     /** @private */
41117     _setLoaded : function(){
41118         this.loaded = true;
41119     },
41120
41121     /** @private */
41122     closeClick : function(e){
41123         var o = {};
41124         e.stopEvent();
41125         this.fireEvent("beforeclose", this, o);
41126         if(o.cancel !== true){
41127             this.tabPanel.removeTab(this.id);
41128         }
41129     },
41130     /**
41131      * The text displayed in the tooltip for the close icon.
41132      * @type String
41133      */
41134     closeText : "Close this tab"
41135 });
41136 /**
41137 *    This script refer to:
41138 *    Title: International Telephone Input
41139 *    Author: Jack O'Connor
41140 *    Code version:  v12.1.12
41141 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41142 **/
41143
41144 Roo.bootstrap.PhoneInputData = function() {
41145     var d = [
41146       [
41147         "Afghanistan (‫افغانستان‬‎)",
41148         "af",
41149         "93"
41150       ],
41151       [
41152         "Albania (Shqipëri)",
41153         "al",
41154         "355"
41155       ],
41156       [
41157         "Algeria (‫الجزائر‬‎)",
41158         "dz",
41159         "213"
41160       ],
41161       [
41162         "American Samoa",
41163         "as",
41164         "1684"
41165       ],
41166       [
41167         "Andorra",
41168         "ad",
41169         "376"
41170       ],
41171       [
41172         "Angola",
41173         "ao",
41174         "244"
41175       ],
41176       [
41177         "Anguilla",
41178         "ai",
41179         "1264"
41180       ],
41181       [
41182         "Antigua and Barbuda",
41183         "ag",
41184         "1268"
41185       ],
41186       [
41187         "Argentina",
41188         "ar",
41189         "54"
41190       ],
41191       [
41192         "Armenia (Հայաստան)",
41193         "am",
41194         "374"
41195       ],
41196       [
41197         "Aruba",
41198         "aw",
41199         "297"
41200       ],
41201       [
41202         "Australia",
41203         "au",
41204         "61",
41205         0
41206       ],
41207       [
41208         "Austria (Österreich)",
41209         "at",
41210         "43"
41211       ],
41212       [
41213         "Azerbaijan (Azərbaycan)",
41214         "az",
41215         "994"
41216       ],
41217       [
41218         "Bahamas",
41219         "bs",
41220         "1242"
41221       ],
41222       [
41223         "Bahrain (‫البحرين‬‎)",
41224         "bh",
41225         "973"
41226       ],
41227       [
41228         "Bangladesh (বাংলাদেশ)",
41229         "bd",
41230         "880"
41231       ],
41232       [
41233         "Barbados",
41234         "bb",
41235         "1246"
41236       ],
41237       [
41238         "Belarus (Беларусь)",
41239         "by",
41240         "375"
41241       ],
41242       [
41243         "Belgium (België)",
41244         "be",
41245         "32"
41246       ],
41247       [
41248         "Belize",
41249         "bz",
41250         "501"
41251       ],
41252       [
41253         "Benin (Bénin)",
41254         "bj",
41255         "229"
41256       ],
41257       [
41258         "Bermuda",
41259         "bm",
41260         "1441"
41261       ],
41262       [
41263         "Bhutan (འབྲུག)",
41264         "bt",
41265         "975"
41266       ],
41267       [
41268         "Bolivia",
41269         "bo",
41270         "591"
41271       ],
41272       [
41273         "Bosnia and Herzegovina (Босна и Херцеговина)",
41274         "ba",
41275         "387"
41276       ],
41277       [
41278         "Botswana",
41279         "bw",
41280         "267"
41281       ],
41282       [
41283         "Brazil (Brasil)",
41284         "br",
41285         "55"
41286       ],
41287       [
41288         "British Indian Ocean Territory",
41289         "io",
41290         "246"
41291       ],
41292       [
41293         "British Virgin Islands",
41294         "vg",
41295         "1284"
41296       ],
41297       [
41298         "Brunei",
41299         "bn",
41300         "673"
41301       ],
41302       [
41303         "Bulgaria (България)",
41304         "bg",
41305         "359"
41306       ],
41307       [
41308         "Burkina Faso",
41309         "bf",
41310         "226"
41311       ],
41312       [
41313         "Burundi (Uburundi)",
41314         "bi",
41315         "257"
41316       ],
41317       [
41318         "Cambodia (កម្ពុជា)",
41319         "kh",
41320         "855"
41321       ],
41322       [
41323         "Cameroon (Cameroun)",
41324         "cm",
41325         "237"
41326       ],
41327       [
41328         "Canada",
41329         "ca",
41330         "1",
41331         1,
41332         ["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"]
41333       ],
41334       [
41335         "Cape Verde (Kabu Verdi)",
41336         "cv",
41337         "238"
41338       ],
41339       [
41340         "Caribbean Netherlands",
41341         "bq",
41342         "599",
41343         1
41344       ],
41345       [
41346         "Cayman Islands",
41347         "ky",
41348         "1345"
41349       ],
41350       [
41351         "Central African Republic (République centrafricaine)",
41352         "cf",
41353         "236"
41354       ],
41355       [
41356         "Chad (Tchad)",
41357         "td",
41358         "235"
41359       ],
41360       [
41361         "Chile",
41362         "cl",
41363         "56"
41364       ],
41365       [
41366         "China (中国)",
41367         "cn",
41368         "86"
41369       ],
41370       [
41371         "Christmas Island",
41372         "cx",
41373         "61",
41374         2
41375       ],
41376       [
41377         "Cocos (Keeling) Islands",
41378         "cc",
41379         "61",
41380         1
41381       ],
41382       [
41383         "Colombia",
41384         "co",
41385         "57"
41386       ],
41387       [
41388         "Comoros (‫جزر القمر‬‎)",
41389         "km",
41390         "269"
41391       ],
41392       [
41393         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41394         "cd",
41395         "243"
41396       ],
41397       [
41398         "Congo (Republic) (Congo-Brazzaville)",
41399         "cg",
41400         "242"
41401       ],
41402       [
41403         "Cook Islands",
41404         "ck",
41405         "682"
41406       ],
41407       [
41408         "Costa Rica",
41409         "cr",
41410         "506"
41411       ],
41412       [
41413         "Côte d’Ivoire",
41414         "ci",
41415         "225"
41416       ],
41417       [
41418         "Croatia (Hrvatska)",
41419         "hr",
41420         "385"
41421       ],
41422       [
41423         "Cuba",
41424         "cu",
41425         "53"
41426       ],
41427       [
41428         "Curaçao",
41429         "cw",
41430         "599",
41431         0
41432       ],
41433       [
41434         "Cyprus (Κύπρος)",
41435         "cy",
41436         "357"
41437       ],
41438       [
41439         "Czech Republic (Česká republika)",
41440         "cz",
41441         "420"
41442       ],
41443       [
41444         "Denmark (Danmark)",
41445         "dk",
41446         "45"
41447       ],
41448       [
41449         "Djibouti",
41450         "dj",
41451         "253"
41452       ],
41453       [
41454         "Dominica",
41455         "dm",
41456         "1767"
41457       ],
41458       [
41459         "Dominican Republic (República Dominicana)",
41460         "do",
41461         "1",
41462         2,
41463         ["809", "829", "849"]
41464       ],
41465       [
41466         "Ecuador",
41467         "ec",
41468         "593"
41469       ],
41470       [
41471         "Egypt (‫مصر‬‎)",
41472         "eg",
41473         "20"
41474       ],
41475       [
41476         "El Salvador",
41477         "sv",
41478         "503"
41479       ],
41480       [
41481         "Equatorial Guinea (Guinea Ecuatorial)",
41482         "gq",
41483         "240"
41484       ],
41485       [
41486         "Eritrea",
41487         "er",
41488         "291"
41489       ],
41490       [
41491         "Estonia (Eesti)",
41492         "ee",
41493         "372"
41494       ],
41495       [
41496         "Ethiopia",
41497         "et",
41498         "251"
41499       ],
41500       [
41501         "Falkland Islands (Islas Malvinas)",
41502         "fk",
41503         "500"
41504       ],
41505       [
41506         "Faroe Islands (Føroyar)",
41507         "fo",
41508         "298"
41509       ],
41510       [
41511         "Fiji",
41512         "fj",
41513         "679"
41514       ],
41515       [
41516         "Finland (Suomi)",
41517         "fi",
41518         "358",
41519         0
41520       ],
41521       [
41522         "France",
41523         "fr",
41524         "33"
41525       ],
41526       [
41527         "French Guiana (Guyane française)",
41528         "gf",
41529         "594"
41530       ],
41531       [
41532         "French Polynesia (Polynésie française)",
41533         "pf",
41534         "689"
41535       ],
41536       [
41537         "Gabon",
41538         "ga",
41539         "241"
41540       ],
41541       [
41542         "Gambia",
41543         "gm",
41544         "220"
41545       ],
41546       [
41547         "Georgia (საქართველო)",
41548         "ge",
41549         "995"
41550       ],
41551       [
41552         "Germany (Deutschland)",
41553         "de",
41554         "49"
41555       ],
41556       [
41557         "Ghana (Gaana)",
41558         "gh",
41559         "233"
41560       ],
41561       [
41562         "Gibraltar",
41563         "gi",
41564         "350"
41565       ],
41566       [
41567         "Greece (Ελλάδα)",
41568         "gr",
41569         "30"
41570       ],
41571       [
41572         "Greenland (Kalaallit Nunaat)",
41573         "gl",
41574         "299"
41575       ],
41576       [
41577         "Grenada",
41578         "gd",
41579         "1473"
41580       ],
41581       [
41582         "Guadeloupe",
41583         "gp",
41584         "590",
41585         0
41586       ],
41587       [
41588         "Guam",
41589         "gu",
41590         "1671"
41591       ],
41592       [
41593         "Guatemala",
41594         "gt",
41595         "502"
41596       ],
41597       [
41598         "Guernsey",
41599         "gg",
41600         "44",
41601         1
41602       ],
41603       [
41604         "Guinea (Guinée)",
41605         "gn",
41606         "224"
41607       ],
41608       [
41609         "Guinea-Bissau (Guiné Bissau)",
41610         "gw",
41611         "245"
41612       ],
41613       [
41614         "Guyana",
41615         "gy",
41616         "592"
41617       ],
41618       [
41619         "Haiti",
41620         "ht",
41621         "509"
41622       ],
41623       [
41624         "Honduras",
41625         "hn",
41626         "504"
41627       ],
41628       [
41629         "Hong Kong (香港)",
41630         "hk",
41631         "852"
41632       ],
41633       [
41634         "Hungary (Magyarország)",
41635         "hu",
41636         "36"
41637       ],
41638       [
41639         "Iceland (Ísland)",
41640         "is",
41641         "354"
41642       ],
41643       [
41644         "India (भारत)",
41645         "in",
41646         "91"
41647       ],
41648       [
41649         "Indonesia",
41650         "id",
41651         "62"
41652       ],
41653       [
41654         "Iran (‫ایران‬‎)",
41655         "ir",
41656         "98"
41657       ],
41658       [
41659         "Iraq (‫العراق‬‎)",
41660         "iq",
41661         "964"
41662       ],
41663       [
41664         "Ireland",
41665         "ie",
41666         "353"
41667       ],
41668       [
41669         "Isle of Man",
41670         "im",
41671         "44",
41672         2
41673       ],
41674       [
41675         "Israel (‫ישראל‬‎)",
41676         "il",
41677         "972"
41678       ],
41679       [
41680         "Italy (Italia)",
41681         "it",
41682         "39",
41683         0
41684       ],
41685       [
41686         "Jamaica",
41687         "jm",
41688         "1876"
41689       ],
41690       [
41691         "Japan (日本)",
41692         "jp",
41693         "81"
41694       ],
41695       [
41696         "Jersey",
41697         "je",
41698         "44",
41699         3
41700       ],
41701       [
41702         "Jordan (‫الأردن‬‎)",
41703         "jo",
41704         "962"
41705       ],
41706       [
41707         "Kazakhstan (Казахстан)",
41708         "kz",
41709         "7",
41710         1
41711       ],
41712       [
41713         "Kenya",
41714         "ke",
41715         "254"
41716       ],
41717       [
41718         "Kiribati",
41719         "ki",
41720         "686"
41721       ],
41722       [
41723         "Kosovo",
41724         "xk",
41725         "383"
41726       ],
41727       [
41728         "Kuwait (‫الكويت‬‎)",
41729         "kw",
41730         "965"
41731       ],
41732       [
41733         "Kyrgyzstan (Кыргызстан)",
41734         "kg",
41735         "996"
41736       ],
41737       [
41738         "Laos (ລາວ)",
41739         "la",
41740         "856"
41741       ],
41742       [
41743         "Latvia (Latvija)",
41744         "lv",
41745         "371"
41746       ],
41747       [
41748         "Lebanon (‫لبنان‬‎)",
41749         "lb",
41750         "961"
41751       ],
41752       [
41753         "Lesotho",
41754         "ls",
41755         "266"
41756       ],
41757       [
41758         "Liberia",
41759         "lr",
41760         "231"
41761       ],
41762       [
41763         "Libya (‫ليبيا‬‎)",
41764         "ly",
41765         "218"
41766       ],
41767       [
41768         "Liechtenstein",
41769         "li",
41770         "423"
41771       ],
41772       [
41773         "Lithuania (Lietuva)",
41774         "lt",
41775         "370"
41776       ],
41777       [
41778         "Luxembourg",
41779         "lu",
41780         "352"
41781       ],
41782       [
41783         "Macau (澳門)",
41784         "mo",
41785         "853"
41786       ],
41787       [
41788         "Macedonia (FYROM) (Македонија)",
41789         "mk",
41790         "389"
41791       ],
41792       [
41793         "Madagascar (Madagasikara)",
41794         "mg",
41795         "261"
41796       ],
41797       [
41798         "Malawi",
41799         "mw",
41800         "265"
41801       ],
41802       [
41803         "Malaysia",
41804         "my",
41805         "60"
41806       ],
41807       [
41808         "Maldives",
41809         "mv",
41810         "960"
41811       ],
41812       [
41813         "Mali",
41814         "ml",
41815         "223"
41816       ],
41817       [
41818         "Malta",
41819         "mt",
41820         "356"
41821       ],
41822       [
41823         "Marshall Islands",
41824         "mh",
41825         "692"
41826       ],
41827       [
41828         "Martinique",
41829         "mq",
41830         "596"
41831       ],
41832       [
41833         "Mauritania (‫موريتانيا‬‎)",
41834         "mr",
41835         "222"
41836       ],
41837       [
41838         "Mauritius (Moris)",
41839         "mu",
41840         "230"
41841       ],
41842       [
41843         "Mayotte",
41844         "yt",
41845         "262",
41846         1
41847       ],
41848       [
41849         "Mexico (México)",
41850         "mx",
41851         "52"
41852       ],
41853       [
41854         "Micronesia",
41855         "fm",
41856         "691"
41857       ],
41858       [
41859         "Moldova (Republica Moldova)",
41860         "md",
41861         "373"
41862       ],
41863       [
41864         "Monaco",
41865         "mc",
41866         "377"
41867       ],
41868       [
41869         "Mongolia (Монгол)",
41870         "mn",
41871         "976"
41872       ],
41873       [
41874         "Montenegro (Crna Gora)",
41875         "me",
41876         "382"
41877       ],
41878       [
41879         "Montserrat",
41880         "ms",
41881         "1664"
41882       ],
41883       [
41884         "Morocco (‫المغرب‬‎)",
41885         "ma",
41886         "212",
41887         0
41888       ],
41889       [
41890         "Mozambique (Moçambique)",
41891         "mz",
41892         "258"
41893       ],
41894       [
41895         "Myanmar (Burma) (မြန်မာ)",
41896         "mm",
41897         "95"
41898       ],
41899       [
41900         "Namibia (Namibië)",
41901         "na",
41902         "264"
41903       ],
41904       [
41905         "Nauru",
41906         "nr",
41907         "674"
41908       ],
41909       [
41910         "Nepal (नेपाल)",
41911         "np",
41912         "977"
41913       ],
41914       [
41915         "Netherlands (Nederland)",
41916         "nl",
41917         "31"
41918       ],
41919       [
41920         "New Caledonia (Nouvelle-Calédonie)",
41921         "nc",
41922         "687"
41923       ],
41924       [
41925         "New Zealand",
41926         "nz",
41927         "64"
41928       ],
41929       [
41930         "Nicaragua",
41931         "ni",
41932         "505"
41933       ],
41934       [
41935         "Niger (Nijar)",
41936         "ne",
41937         "227"
41938       ],
41939       [
41940         "Nigeria",
41941         "ng",
41942         "234"
41943       ],
41944       [
41945         "Niue",
41946         "nu",
41947         "683"
41948       ],
41949       [
41950         "Norfolk Island",
41951         "nf",
41952         "672"
41953       ],
41954       [
41955         "North Korea (조선 민주주의 인민 공화국)",
41956         "kp",
41957         "850"
41958       ],
41959       [
41960         "Northern Mariana Islands",
41961         "mp",
41962         "1670"
41963       ],
41964       [
41965         "Norway (Norge)",
41966         "no",
41967         "47",
41968         0
41969       ],
41970       [
41971         "Oman (‫عُمان‬‎)",
41972         "om",
41973         "968"
41974       ],
41975       [
41976         "Pakistan (‫پاکستان‬‎)",
41977         "pk",
41978         "92"
41979       ],
41980       [
41981         "Palau",
41982         "pw",
41983         "680"
41984       ],
41985       [
41986         "Palestine (‫فلسطين‬‎)",
41987         "ps",
41988         "970"
41989       ],
41990       [
41991         "Panama (Panamá)",
41992         "pa",
41993         "507"
41994       ],
41995       [
41996         "Papua New Guinea",
41997         "pg",
41998         "675"
41999       ],
42000       [
42001         "Paraguay",
42002         "py",
42003         "595"
42004       ],
42005       [
42006         "Peru (Perú)",
42007         "pe",
42008         "51"
42009       ],
42010       [
42011         "Philippines",
42012         "ph",
42013         "63"
42014       ],
42015       [
42016         "Poland (Polska)",
42017         "pl",
42018         "48"
42019       ],
42020       [
42021         "Portugal",
42022         "pt",
42023         "351"
42024       ],
42025       [
42026         "Puerto Rico",
42027         "pr",
42028         "1",
42029         3,
42030         ["787", "939"]
42031       ],
42032       [
42033         "Qatar (‫قطر‬‎)",
42034         "qa",
42035         "974"
42036       ],
42037       [
42038         "Réunion (La Réunion)",
42039         "re",
42040         "262",
42041         0
42042       ],
42043       [
42044         "Romania (România)",
42045         "ro",
42046         "40"
42047       ],
42048       [
42049         "Russia (Россия)",
42050         "ru",
42051         "7",
42052         0
42053       ],
42054       [
42055         "Rwanda",
42056         "rw",
42057         "250"
42058       ],
42059       [
42060         "Saint Barthélemy",
42061         "bl",
42062         "590",
42063         1
42064       ],
42065       [
42066         "Saint Helena",
42067         "sh",
42068         "290"
42069       ],
42070       [
42071         "Saint Kitts and Nevis",
42072         "kn",
42073         "1869"
42074       ],
42075       [
42076         "Saint Lucia",
42077         "lc",
42078         "1758"
42079       ],
42080       [
42081         "Saint Martin (Saint-Martin (partie française))",
42082         "mf",
42083         "590",
42084         2
42085       ],
42086       [
42087         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42088         "pm",
42089         "508"
42090       ],
42091       [
42092         "Saint Vincent and the Grenadines",
42093         "vc",
42094         "1784"
42095       ],
42096       [
42097         "Samoa",
42098         "ws",
42099         "685"
42100       ],
42101       [
42102         "San Marino",
42103         "sm",
42104         "378"
42105       ],
42106       [
42107         "São Tomé and Príncipe (São Tomé e Príncipe)",
42108         "st",
42109         "239"
42110       ],
42111       [
42112         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42113         "sa",
42114         "966"
42115       ],
42116       [
42117         "Senegal (Sénégal)",
42118         "sn",
42119         "221"
42120       ],
42121       [
42122         "Serbia (Србија)",
42123         "rs",
42124         "381"
42125       ],
42126       [
42127         "Seychelles",
42128         "sc",
42129         "248"
42130       ],
42131       [
42132         "Sierra Leone",
42133         "sl",
42134         "232"
42135       ],
42136       [
42137         "Singapore",
42138         "sg",
42139         "65"
42140       ],
42141       [
42142         "Sint Maarten",
42143         "sx",
42144         "1721"
42145       ],
42146       [
42147         "Slovakia (Slovensko)",
42148         "sk",
42149         "421"
42150       ],
42151       [
42152         "Slovenia (Slovenija)",
42153         "si",
42154         "386"
42155       ],
42156       [
42157         "Solomon Islands",
42158         "sb",
42159         "677"
42160       ],
42161       [
42162         "Somalia (Soomaaliya)",
42163         "so",
42164         "252"
42165       ],
42166       [
42167         "South Africa",
42168         "za",
42169         "27"
42170       ],
42171       [
42172         "South Korea (대한민국)",
42173         "kr",
42174         "82"
42175       ],
42176       [
42177         "South Sudan (‫جنوب السودان‬‎)",
42178         "ss",
42179         "211"
42180       ],
42181       [
42182         "Spain (España)",
42183         "es",
42184         "34"
42185       ],
42186       [
42187         "Sri Lanka (ශ්‍රී ලංකාව)",
42188         "lk",
42189         "94"
42190       ],
42191       [
42192         "Sudan (‫السودان‬‎)",
42193         "sd",
42194         "249"
42195       ],
42196       [
42197         "Suriname",
42198         "sr",
42199         "597"
42200       ],
42201       [
42202         "Svalbard and Jan Mayen",
42203         "sj",
42204         "47",
42205         1
42206       ],
42207       [
42208         "Swaziland",
42209         "sz",
42210         "268"
42211       ],
42212       [
42213         "Sweden (Sverige)",
42214         "se",
42215         "46"
42216       ],
42217       [
42218         "Switzerland (Schweiz)",
42219         "ch",
42220         "41"
42221       ],
42222       [
42223         "Syria (‫سوريا‬‎)",
42224         "sy",
42225         "963"
42226       ],
42227       [
42228         "Taiwan (台灣)",
42229         "tw",
42230         "886"
42231       ],
42232       [
42233         "Tajikistan",
42234         "tj",
42235         "992"
42236       ],
42237       [
42238         "Tanzania",
42239         "tz",
42240         "255"
42241       ],
42242       [
42243         "Thailand (ไทย)",
42244         "th",
42245         "66"
42246       ],
42247       [
42248         "Timor-Leste",
42249         "tl",
42250         "670"
42251       ],
42252       [
42253         "Togo",
42254         "tg",
42255         "228"
42256       ],
42257       [
42258         "Tokelau",
42259         "tk",
42260         "690"
42261       ],
42262       [
42263         "Tonga",
42264         "to",
42265         "676"
42266       ],
42267       [
42268         "Trinidad and Tobago",
42269         "tt",
42270         "1868"
42271       ],
42272       [
42273         "Tunisia (‫تونس‬‎)",
42274         "tn",
42275         "216"
42276       ],
42277       [
42278         "Turkey (Türkiye)",
42279         "tr",
42280         "90"
42281       ],
42282       [
42283         "Turkmenistan",
42284         "tm",
42285         "993"
42286       ],
42287       [
42288         "Turks and Caicos Islands",
42289         "tc",
42290         "1649"
42291       ],
42292       [
42293         "Tuvalu",
42294         "tv",
42295         "688"
42296       ],
42297       [
42298         "U.S. Virgin Islands",
42299         "vi",
42300         "1340"
42301       ],
42302       [
42303         "Uganda",
42304         "ug",
42305         "256"
42306       ],
42307       [
42308         "Ukraine (Україна)",
42309         "ua",
42310         "380"
42311       ],
42312       [
42313         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42314         "ae",
42315         "971"
42316       ],
42317       [
42318         "United Kingdom",
42319         "gb",
42320         "44",
42321         0
42322       ],
42323       [
42324         "United States",
42325         "us",
42326         "1",
42327         0
42328       ],
42329       [
42330         "Uruguay",
42331         "uy",
42332         "598"
42333       ],
42334       [
42335         "Uzbekistan (Oʻzbekiston)",
42336         "uz",
42337         "998"
42338       ],
42339       [
42340         "Vanuatu",
42341         "vu",
42342         "678"
42343       ],
42344       [
42345         "Vatican City (Città del Vaticano)",
42346         "va",
42347         "39",
42348         1
42349       ],
42350       [
42351         "Venezuela",
42352         "ve",
42353         "58"
42354       ],
42355       [
42356         "Vietnam (Việt Nam)",
42357         "vn",
42358         "84"
42359       ],
42360       [
42361         "Wallis and Futuna (Wallis-et-Futuna)",
42362         "wf",
42363         "681"
42364       ],
42365       [
42366         "Western Sahara (‫الصحراء الغربية‬‎)",
42367         "eh",
42368         "212",
42369         1
42370       ],
42371       [
42372         "Yemen (‫اليمن‬‎)",
42373         "ye",
42374         "967"
42375       ],
42376       [
42377         "Zambia",
42378         "zm",
42379         "260"
42380       ],
42381       [
42382         "Zimbabwe",
42383         "zw",
42384         "263"
42385       ],
42386       [
42387         "Åland Islands",
42388         "ax",
42389         "358",
42390         1
42391       ]
42392   ];
42393   
42394   return d;
42395 }/**
42396 *    This script refer to:
42397 *    Title: International Telephone Input
42398 *    Author: Jack O'Connor
42399 *    Code version:  v12.1.12
42400 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42401 **/
42402
42403 /**
42404  * @class Roo.bootstrap.PhoneInput
42405  * @extends Roo.bootstrap.TriggerField
42406  * An input with International dial-code selection
42407  
42408  * @cfg {String} defaultDialCode default '+852'
42409  * @cfg {Array} preferedCountries default []
42410   
42411  * @constructor
42412  * Create a new PhoneInput.
42413  * @param {Object} config Configuration options
42414  */
42415
42416 Roo.bootstrap.PhoneInput = function(config) {
42417     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42418 };
42419
42420 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42421         
42422         listWidth: undefined,
42423         
42424         selectedClass: 'active',
42425         
42426         invalidClass : "has-warning",
42427         
42428         validClass: 'has-success',
42429         
42430         allowed: '0123456789',
42431         
42432         max_length: 15,
42433         
42434         /**
42435          * @cfg {String} defaultDialCode The default dial code when initializing the input
42436          */
42437         defaultDialCode: '+852',
42438         
42439         /**
42440          * @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
42441          */
42442         preferedCountries: false,
42443         
42444         getAutoCreate : function()
42445         {
42446             var data = Roo.bootstrap.PhoneInputData();
42447             var align = this.labelAlign || this.parentLabelAlign();
42448             var id = Roo.id();
42449             
42450             this.allCountries = [];
42451             this.dialCodeMapping = [];
42452             
42453             for (var i = 0; i < data.length; i++) {
42454               var c = data[i];
42455               this.allCountries[i] = {
42456                 name: c[0],
42457                 iso2: c[1],
42458                 dialCode: c[2],
42459                 priority: c[3] || 0,
42460                 areaCodes: c[4] || null
42461               };
42462               this.dialCodeMapping[c[2]] = {
42463                   name: c[0],
42464                   iso2: c[1],
42465                   priority: c[3] || 0,
42466                   areaCodes: c[4] || null
42467               };
42468             }
42469             
42470             var cfg = {
42471                 cls: 'form-group',
42472                 cn: []
42473             };
42474             
42475             var input =  {
42476                 tag: 'input',
42477                 id : id,
42478                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42479                 maxlength: this.max_length,
42480                 cls : 'form-control tel-input',
42481                 autocomplete: 'new-password'
42482             };
42483             
42484             var hiddenInput = {
42485                 tag: 'input',
42486                 type: 'hidden',
42487                 cls: 'hidden-tel-input'
42488             };
42489             
42490             if (this.name) {
42491                 hiddenInput.name = this.name;
42492             }
42493             
42494             if (this.disabled) {
42495                 input.disabled = true;
42496             }
42497             
42498             var flag_container = {
42499                 tag: 'div',
42500                 cls: 'flag-box',
42501                 cn: [
42502                     {
42503                         tag: 'div',
42504                         cls: 'flag'
42505                     },
42506                     {
42507                         tag: 'div',
42508                         cls: 'caret'
42509                     }
42510                 ]
42511             };
42512             
42513             var box = {
42514                 tag: 'div',
42515                 cls: this.hasFeedback ? 'has-feedback' : '',
42516                 cn: [
42517                     hiddenInput,
42518                     input,
42519                     {
42520                         tag: 'input',
42521                         cls: 'dial-code-holder',
42522                         disabled: true
42523                     }
42524                 ]
42525             };
42526             
42527             var container = {
42528                 cls: 'roo-select2-container input-group',
42529                 cn: [
42530                     flag_container,
42531                     box
42532                 ]
42533             };
42534             
42535             if (this.fieldLabel.length) {
42536                 var indicator = {
42537                     tag: 'i',
42538                     tooltip: 'This field is required'
42539                 };
42540                 
42541                 var label = {
42542                     tag: 'label',
42543                     'for':  id,
42544                     cls: 'control-label',
42545                     cn: []
42546                 };
42547                 
42548                 var label_text = {
42549                     tag: 'span',
42550                     html: this.fieldLabel
42551                 };
42552                 
42553                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42554                 label.cn = [
42555                     indicator,
42556                     label_text
42557                 ];
42558                 
42559                 if(this.indicatorpos == 'right') {
42560                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42561                     label.cn = [
42562                         label_text,
42563                         indicator
42564                     ];
42565                 }
42566                 
42567                 if(align == 'left') {
42568                     container = {
42569                         tag: 'div',
42570                         cn: [
42571                             container
42572                         ]
42573                     };
42574                     
42575                     if(this.labelWidth > 12){
42576                         label.style = "width: " + this.labelWidth + 'px';
42577                     }
42578                     if(this.labelWidth < 13 && this.labelmd == 0){
42579                         this.labelmd = this.labelWidth;
42580                     }
42581                     if(this.labellg > 0){
42582                         label.cls += ' col-lg-' + this.labellg;
42583                         input.cls += ' col-lg-' + (12 - this.labellg);
42584                     }
42585                     if(this.labelmd > 0){
42586                         label.cls += ' col-md-' + this.labelmd;
42587                         container.cls += ' col-md-' + (12 - this.labelmd);
42588                     }
42589                     if(this.labelsm > 0){
42590                         label.cls += ' col-sm-' + this.labelsm;
42591                         container.cls += ' col-sm-' + (12 - this.labelsm);
42592                     }
42593                     if(this.labelxs > 0){
42594                         label.cls += ' col-xs-' + this.labelxs;
42595                         container.cls += ' col-xs-' + (12 - this.labelxs);
42596                     }
42597                 }
42598             }
42599             
42600             cfg.cn = [
42601                 label,
42602                 container
42603             ];
42604             
42605             var settings = this;
42606             
42607             ['xs','sm','md','lg'].map(function(size){
42608                 if (settings[size]) {
42609                     cfg.cls += ' col-' + size + '-' + settings[size];
42610                 }
42611             });
42612             
42613             this.store = new Roo.data.Store({
42614                 proxy : new Roo.data.MemoryProxy({}),
42615                 reader : new Roo.data.JsonReader({
42616                     fields : [
42617                         {
42618                             'name' : 'name',
42619                             'type' : 'string'
42620                         },
42621                         {
42622                             'name' : 'iso2',
42623                             'type' : 'string'
42624                         },
42625                         {
42626                             'name' : 'dialCode',
42627                             'type' : 'string'
42628                         },
42629                         {
42630                             'name' : 'priority',
42631                             'type' : 'string'
42632                         },
42633                         {
42634                             'name' : 'areaCodes',
42635                             'type' : 'string'
42636                         }
42637                     ]
42638                 })
42639             });
42640             
42641             if(!this.preferedCountries) {
42642                 this.preferedCountries = [
42643                     'hk',
42644                     'gb',
42645                     'us'
42646                 ];
42647             }
42648             
42649             var p = this.preferedCountries.reverse();
42650             
42651             if(p) {
42652                 for (var i = 0; i < p.length; i++) {
42653                     for (var j = 0; j < this.allCountries.length; j++) {
42654                         if(this.allCountries[j].iso2 == p[i]) {
42655                             var t = this.allCountries[j];
42656                             this.allCountries.splice(j,1);
42657                             this.allCountries.unshift(t);
42658                         }
42659                     } 
42660                 }
42661             }
42662             
42663             this.store.proxy.data = {
42664                 success: true,
42665                 data: this.allCountries
42666             };
42667             
42668             return cfg;
42669         },
42670         
42671         initEvents : function()
42672         {
42673             this.createList();
42674             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42675             
42676             this.indicator = this.indicatorEl();
42677             this.flag = this.flagEl();
42678             this.dialCodeHolder = this.dialCodeHolderEl();
42679             
42680             this.trigger = this.el.select('div.flag-box',true).first();
42681             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42682             
42683             var _this = this;
42684             
42685             (function(){
42686                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42687                 _this.list.setWidth(lw);
42688             }).defer(100);
42689             
42690             this.list.on('mouseover', this.onViewOver, this);
42691             this.list.on('mousemove', this.onViewMove, this);
42692             this.inputEl().on("keyup", this.onKeyUp, this);
42693             this.inputEl().on("keypress", this.onKeyPress, this);
42694             
42695             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42696
42697             this.view = new Roo.View(this.list, this.tpl, {
42698                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42699             });
42700             
42701             this.view.on('click', this.onViewClick, this);
42702             this.setValue(this.defaultDialCode);
42703         },
42704         
42705         onTriggerClick : function(e)
42706         {
42707             Roo.log('trigger click');
42708             if(this.disabled){
42709                 return;
42710             }
42711             
42712             if(this.isExpanded()){
42713                 this.collapse();
42714                 this.hasFocus = false;
42715             }else {
42716                 this.store.load({});
42717                 this.hasFocus = true;
42718                 this.expand();
42719             }
42720         },
42721         
42722         isExpanded : function()
42723         {
42724             return this.list.isVisible();
42725         },
42726         
42727         collapse : function()
42728         {
42729             if(!this.isExpanded()){
42730                 return;
42731             }
42732             this.list.hide();
42733             Roo.get(document).un('mousedown', this.collapseIf, this);
42734             Roo.get(document).un('mousewheel', this.collapseIf, this);
42735             this.fireEvent('collapse', this);
42736             this.validate();
42737         },
42738         
42739         expand : function()
42740         {
42741             Roo.log('expand');
42742
42743             if(this.isExpanded() || !this.hasFocus){
42744                 return;
42745             }
42746             
42747             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42748             this.list.setWidth(lw);
42749             
42750             this.list.show();
42751             this.restrictHeight();
42752             
42753             Roo.get(document).on('mousedown', this.collapseIf, this);
42754             Roo.get(document).on('mousewheel', this.collapseIf, this);
42755             
42756             this.fireEvent('expand', this);
42757         },
42758         
42759         restrictHeight : function()
42760         {
42761             this.list.alignTo(this.inputEl(), this.listAlign);
42762             this.list.alignTo(this.inputEl(), this.listAlign);
42763         },
42764         
42765         onViewOver : function(e, t)
42766         {
42767             if(this.inKeyMode){
42768                 return;
42769             }
42770             var item = this.view.findItemFromChild(t);
42771             
42772             if(item){
42773                 var index = this.view.indexOf(item);
42774                 this.select(index, false);
42775             }
42776         },
42777
42778         // private
42779         onViewClick : function(view, doFocus, el, e)
42780         {
42781             var index = this.view.getSelectedIndexes()[0];
42782             
42783             var r = this.store.getAt(index);
42784             
42785             if(r){
42786                 this.onSelect(r, index);
42787             }
42788             if(doFocus !== false && !this.blockFocus){
42789                 this.inputEl().focus();
42790             }
42791         },
42792         
42793         onViewMove : function(e, t)
42794         {
42795             this.inKeyMode = false;
42796         },
42797         
42798         select : function(index, scrollIntoView)
42799         {
42800             this.selectedIndex = index;
42801             this.view.select(index);
42802             if(scrollIntoView !== false){
42803                 var el = this.view.getNode(index);
42804                 if(el){
42805                     this.list.scrollChildIntoView(el, false);
42806                 }
42807             }
42808         },
42809         
42810         createList : function()
42811         {
42812             this.list = Roo.get(document.body).createChild({
42813                 tag: 'ul',
42814                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42815                 style: 'display:none'
42816             });
42817             
42818             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42819         },
42820         
42821         collapseIf : function(e)
42822         {
42823             var in_combo  = e.within(this.el);
42824             var in_list =  e.within(this.list);
42825             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42826             
42827             if (in_combo || in_list || is_list) {
42828                 return;
42829             }
42830             this.collapse();
42831         },
42832         
42833         onSelect : function(record, index)
42834         {
42835             if(this.fireEvent('beforeselect', this, record, index) !== false){
42836                 
42837                 this.setFlagClass(record.data.iso2);
42838                 this.setDialCode(record.data.dialCode);
42839                 this.hasFocus = false;
42840                 this.collapse();
42841                 this.fireEvent('select', this, record, index);
42842             }
42843         },
42844         
42845         flagEl : function()
42846         {
42847             var flag = this.el.select('div.flag',true).first();
42848             if(!flag){
42849                 return false;
42850             }
42851             return flag;
42852         },
42853         
42854         dialCodeHolderEl : function()
42855         {
42856             var d = this.el.select('input.dial-code-holder',true).first();
42857             if(!d){
42858                 return false;
42859             }
42860             return d;
42861         },
42862         
42863         setDialCode : function(v)
42864         {
42865             this.dialCodeHolder.dom.value = '+'+v;
42866         },
42867         
42868         setFlagClass : function(n)
42869         {
42870             this.flag.dom.className = 'flag '+n;
42871         },
42872         
42873         getValue : function()
42874         {
42875             var v = this.inputEl().getValue();
42876             if(this.dialCodeHolder) {
42877                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42878             }
42879             return v;
42880         },
42881         
42882         setValue : function(v)
42883         {
42884             var d = this.getDialCode(v);
42885             
42886             //invalid dial code
42887             if(v.length == 0 || !d || d.length == 0) {
42888                 if(this.rendered){
42889                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42890                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42891                 }
42892                 return;
42893             }
42894             
42895             //valid dial code
42896             this.setFlagClass(this.dialCodeMapping[d].iso2);
42897             this.setDialCode(d);
42898             this.inputEl().dom.value = v.replace('+'+d,'');
42899             this.hiddenEl().dom.value = this.getValue();
42900             
42901             this.validate();
42902         },
42903         
42904         getDialCode : function(v)
42905         {
42906             v = v ||  '';
42907             
42908             if (v.length == 0) {
42909                 return this.dialCodeHolder.dom.value;
42910             }
42911             
42912             var dialCode = "";
42913             if (v.charAt(0) != "+") {
42914                 return false;
42915             }
42916             var numericChars = "";
42917             for (var i = 1; i < v.length; i++) {
42918               var c = v.charAt(i);
42919               if (!isNaN(c)) {
42920                 numericChars += c;
42921                 if (this.dialCodeMapping[numericChars]) {
42922                   dialCode = v.substr(1, i);
42923                 }
42924                 if (numericChars.length == 4) {
42925                   break;
42926                 }
42927               }
42928             }
42929             return dialCode;
42930         },
42931         
42932         reset : function()
42933         {
42934             this.setValue(this.defaultDialCode);
42935             this.validate();
42936         },
42937         
42938         hiddenEl : function()
42939         {
42940             return this.el.select('input.hidden-tel-input',true).first();
42941         },
42942         
42943         // after setting val
42944         onKeyUp : function(e){
42945             this.setValue(this.getValue());
42946         },
42947         
42948         onKeyPress : function(e){
42949             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42950                 e.stopEvent();
42951             }
42952         }
42953         
42954 });
42955 /**
42956  * @class Roo.bootstrap.MoneyField
42957  * @extends Roo.bootstrap.ComboBox
42958  * Bootstrap MoneyField class
42959  * 
42960  * @constructor
42961  * Create a new MoneyField.
42962  * @param {Object} config Configuration options
42963  */
42964
42965 Roo.bootstrap.MoneyField = function(config) {
42966     
42967     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42968     
42969 };
42970
42971 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42972     
42973     /**
42974      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42975      */
42976     allowDecimals : true,
42977     /**
42978      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42979      */
42980     decimalSeparator : ".",
42981     /**
42982      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42983      */
42984     decimalPrecision : 0,
42985     /**
42986      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42987      */
42988     allowNegative : true,
42989     /**
42990      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42991      */
42992     allowZero: true,
42993     /**
42994      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42995      */
42996     minValue : Number.NEGATIVE_INFINITY,
42997     /**
42998      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42999      */
43000     maxValue : Number.MAX_VALUE,
43001     /**
43002      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43003      */
43004     minText : "The minimum value for this field is {0}",
43005     /**
43006      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43007      */
43008     maxText : "The maximum value for this field is {0}",
43009     /**
43010      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43011      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43012      */
43013     nanText : "{0} is not a valid number",
43014     /**
43015      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43016      */
43017     castInt : true,
43018     /**
43019      * @cfg {String} defaults currency of the MoneyField
43020      * value should be in lkey
43021      */
43022     defaultCurrency : false,
43023     /**
43024      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43025      */
43026     thousandsDelimiter : false,
43027     /**
43028      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43029      */
43030     max_length: false,
43031     
43032     inputlg : 9,
43033     inputmd : 9,
43034     inputsm : 9,
43035     inputxs : 6,
43036     
43037     store : false,
43038     
43039     getAutoCreate : function()
43040     {
43041         var align = this.labelAlign || this.parentLabelAlign();
43042         
43043         var id = Roo.id();
43044
43045         var cfg = {
43046             cls: 'form-group',
43047             cn: []
43048         };
43049
43050         var input =  {
43051             tag: 'input',
43052             id : id,
43053             cls : 'form-control roo-money-amount-input',
43054             autocomplete: 'new-password'
43055         };
43056         
43057         var hiddenInput = {
43058             tag: 'input',
43059             type: 'hidden',
43060             id: Roo.id(),
43061             cls: 'hidden-number-input'
43062         };
43063         
43064         if(this.max_length) {
43065             input.maxlength = this.max_length; 
43066         }
43067         
43068         if (this.name) {
43069             hiddenInput.name = this.name;
43070         }
43071
43072         if (this.disabled) {
43073             input.disabled = true;
43074         }
43075
43076         var clg = 12 - this.inputlg;
43077         var cmd = 12 - this.inputmd;
43078         var csm = 12 - this.inputsm;
43079         var cxs = 12 - this.inputxs;
43080         
43081         var container = {
43082             tag : 'div',
43083             cls : 'row roo-money-field',
43084             cn : [
43085                 {
43086                     tag : 'div',
43087                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43088                     cn : [
43089                         {
43090                             tag : 'div',
43091                             cls: 'roo-select2-container input-group',
43092                             cn: [
43093                                 {
43094                                     tag : 'input',
43095                                     cls : 'form-control roo-money-currency-input',
43096                                     autocomplete: 'new-password',
43097                                     readOnly : 1,
43098                                     name : this.currencyName
43099                                 },
43100                                 {
43101                                     tag :'span',
43102                                     cls : 'input-group-addon',
43103                                     cn : [
43104                                         {
43105                                             tag: 'span',
43106                                             cls: 'caret'
43107                                         }
43108                                     ]
43109                                 }
43110                             ]
43111                         }
43112                     ]
43113                 },
43114                 {
43115                     tag : 'div',
43116                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43117                     cn : [
43118                         {
43119                             tag: 'div',
43120                             cls: this.hasFeedback ? 'has-feedback' : '',
43121                             cn: [
43122                                 input
43123                             ]
43124                         }
43125                     ]
43126                 }
43127             ]
43128             
43129         };
43130         
43131         if (this.fieldLabel.length) {
43132             var indicator = {
43133                 tag: 'i',
43134                 tooltip: 'This field is required'
43135             };
43136
43137             var label = {
43138                 tag: 'label',
43139                 'for':  id,
43140                 cls: 'control-label',
43141                 cn: []
43142             };
43143
43144             var label_text = {
43145                 tag: 'span',
43146                 html: this.fieldLabel
43147             };
43148
43149             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43150             label.cn = [
43151                 indicator,
43152                 label_text
43153             ];
43154
43155             if(this.indicatorpos == 'right') {
43156                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43157                 label.cn = [
43158                     label_text,
43159                     indicator
43160                 ];
43161             }
43162
43163             if(align == 'left') {
43164                 container = {
43165                     tag: 'div',
43166                     cn: [
43167                         container
43168                     ]
43169                 };
43170
43171                 if(this.labelWidth > 12){
43172                     label.style = "width: " + this.labelWidth + 'px';
43173                 }
43174                 if(this.labelWidth < 13 && this.labelmd == 0){
43175                     this.labelmd = this.labelWidth;
43176                 }
43177                 if(this.labellg > 0){
43178                     label.cls += ' col-lg-' + this.labellg;
43179                     input.cls += ' col-lg-' + (12 - this.labellg);
43180                 }
43181                 if(this.labelmd > 0){
43182                     label.cls += ' col-md-' + this.labelmd;
43183                     container.cls += ' col-md-' + (12 - this.labelmd);
43184                 }
43185                 if(this.labelsm > 0){
43186                     label.cls += ' col-sm-' + this.labelsm;
43187                     container.cls += ' col-sm-' + (12 - this.labelsm);
43188                 }
43189                 if(this.labelxs > 0){
43190                     label.cls += ' col-xs-' + this.labelxs;
43191                     container.cls += ' col-xs-' + (12 - this.labelxs);
43192                 }
43193             }
43194         }
43195
43196         cfg.cn = [
43197             label,
43198             container,
43199             hiddenInput
43200         ];
43201         
43202         var settings = this;
43203
43204         ['xs','sm','md','lg'].map(function(size){
43205             if (settings[size]) {
43206                 cfg.cls += ' col-' + size + '-' + settings[size];
43207             }
43208         });
43209         
43210         return cfg;
43211     },
43212     
43213     initEvents : function()
43214     {
43215         this.indicator = this.indicatorEl();
43216         
43217         this.initCurrencyEvent();
43218         
43219         this.initNumberEvent();
43220     },
43221     
43222     initCurrencyEvent : function()
43223     {
43224         if (!this.store) {
43225             throw "can not find store for combo";
43226         }
43227         
43228         this.store = Roo.factory(this.store, Roo.data);
43229         this.store.parent = this;
43230         
43231         this.createList();
43232         
43233         this.triggerEl = this.el.select('.input-group-addon', true).first();
43234         
43235         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43236         
43237         var _this = this;
43238         
43239         (function(){
43240             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43241             _this.list.setWidth(lw);
43242         }).defer(100);
43243         
43244         this.list.on('mouseover', this.onViewOver, this);
43245         this.list.on('mousemove', this.onViewMove, this);
43246         this.list.on('scroll', this.onViewScroll, this);
43247         
43248         if(!this.tpl){
43249             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43250         }
43251         
43252         this.view = new Roo.View(this.list, this.tpl, {
43253             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43254         });
43255         
43256         this.view.on('click', this.onViewClick, this);
43257         
43258         this.store.on('beforeload', this.onBeforeLoad, this);
43259         this.store.on('load', this.onLoad, this);
43260         this.store.on('loadexception', this.onLoadException, this);
43261         
43262         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43263             "up" : function(e){
43264                 this.inKeyMode = true;
43265                 this.selectPrev();
43266             },
43267
43268             "down" : function(e){
43269                 if(!this.isExpanded()){
43270                     this.onTriggerClick();
43271                 }else{
43272                     this.inKeyMode = true;
43273                     this.selectNext();
43274                 }
43275             },
43276
43277             "enter" : function(e){
43278                 this.collapse();
43279                 
43280                 if(this.fireEvent("specialkey", this, e)){
43281                     this.onViewClick(false);
43282                 }
43283                 
43284                 return true;
43285             },
43286
43287             "esc" : function(e){
43288                 this.collapse();
43289             },
43290
43291             "tab" : function(e){
43292                 this.collapse();
43293                 
43294                 if(this.fireEvent("specialkey", this, e)){
43295                     this.onViewClick(false);
43296                 }
43297                 
43298                 return true;
43299             },
43300
43301             scope : this,
43302
43303             doRelay : function(foo, bar, hname){
43304                 if(hname == 'down' || this.scope.isExpanded()){
43305                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43306                 }
43307                 return true;
43308             },
43309
43310             forceKeyDown: true
43311         });
43312         
43313         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43314         
43315     },
43316     
43317     initNumberEvent : function(e)
43318     {
43319         this.inputEl().on("keydown" , this.fireKey,  this);
43320         this.inputEl().on("focus", this.onFocus,  this);
43321         this.inputEl().on("blur", this.onBlur,  this);
43322         
43323         this.inputEl().relayEvent('keyup', this);
43324         
43325         if(this.indicator){
43326             this.indicator.addClass('invisible');
43327         }
43328  
43329         this.originalValue = this.getValue();
43330         
43331         if(this.validationEvent == 'keyup'){
43332             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43333             this.inputEl().on('keyup', this.filterValidation, this);
43334         }
43335         else if(this.validationEvent !== false){
43336             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43337         }
43338         
43339         if(this.selectOnFocus){
43340             this.on("focus", this.preFocus, this);
43341             
43342         }
43343         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43344             this.inputEl().on("keypress", this.filterKeys, this);
43345         } else {
43346             this.inputEl().relayEvent('keypress', this);
43347         }
43348         
43349         var allowed = "0123456789";
43350         
43351         if(this.allowDecimals){
43352             allowed += this.decimalSeparator;
43353         }
43354         
43355         if(this.allowNegative){
43356             allowed += "-";
43357         }
43358         
43359         if(this.thousandsDelimiter) {
43360             allowed += ",";
43361         }
43362         
43363         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43364         
43365         var keyPress = function(e){
43366             
43367             var k = e.getKey();
43368             
43369             var c = e.getCharCode();
43370             
43371             if(
43372                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43373                     allowed.indexOf(String.fromCharCode(c)) === -1
43374             ){
43375                 e.stopEvent();
43376                 return;
43377             }
43378             
43379             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43380                 return;
43381             }
43382             
43383             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43384                 e.stopEvent();
43385             }
43386         };
43387         
43388         this.inputEl().on("keypress", keyPress, this);
43389         
43390     },
43391     
43392     onTriggerClick : function(e)
43393     {   
43394         if(this.disabled){
43395             return;
43396         }
43397         
43398         this.page = 0;
43399         this.loadNext = false;
43400         
43401         if(this.isExpanded()){
43402             this.collapse();
43403             return;
43404         }
43405         
43406         this.hasFocus = true;
43407         
43408         if(this.triggerAction == 'all') {
43409             this.doQuery(this.allQuery, true);
43410             return;
43411         }
43412         
43413         this.doQuery(this.getRawValue());
43414     },
43415     
43416     getCurrency : function()
43417     {   
43418         var v = this.currencyEl().getValue();
43419         
43420         return v;
43421     },
43422     
43423     restrictHeight : function()
43424     {
43425         this.list.alignTo(this.currencyEl(), this.listAlign);
43426         this.list.alignTo(this.currencyEl(), this.listAlign);
43427     },
43428     
43429     onViewClick : function(view, doFocus, el, e)
43430     {
43431         var index = this.view.getSelectedIndexes()[0];
43432         
43433         var r = this.store.getAt(index);
43434         
43435         if(r){
43436             this.onSelect(r, index);
43437         }
43438     },
43439     
43440     onSelect : function(record, index){
43441         
43442         if(this.fireEvent('beforeselect', this, record, index) !== false){
43443         
43444             this.setFromCurrencyData(index > -1 ? record.data : false);
43445             
43446             this.collapse();
43447             
43448             this.fireEvent('select', this, record, index);
43449         }
43450     },
43451     
43452     setFromCurrencyData : function(o)
43453     {
43454         var currency = '';
43455         
43456         this.lastCurrency = o;
43457         
43458         if (this.currencyField) {
43459             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43460         } else {
43461             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43462         }
43463         
43464         this.lastSelectionText = currency;
43465         
43466         //setting default currency
43467         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43468             this.setCurrency(this.defaultCurrency);
43469             return;
43470         }
43471         
43472         this.setCurrency(currency);
43473     },
43474     
43475     setFromData : function(o)
43476     {
43477         var c = {};
43478         
43479         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43480         
43481         this.setFromCurrencyData(c);
43482         
43483         var value = '';
43484         
43485         if (this.name) {
43486             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43487         } else {
43488             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43489         }
43490         
43491         this.setValue(value);
43492         
43493     },
43494     
43495     setCurrency : function(v)
43496     {   
43497         this.currencyValue = v;
43498         
43499         if(this.rendered){
43500             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43501             this.validate();
43502         }
43503     },
43504     
43505     setValue : function(v)
43506     {
43507         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43508         
43509         this.value = v;
43510         
43511         if(this.rendered){
43512             
43513             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43514             
43515             this.inputEl().dom.value = (v == '') ? '' :
43516                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43517             
43518             if(!this.allowZero && v === '0') {
43519                 this.hiddenEl().dom.value = '';
43520                 this.inputEl().dom.value = '';
43521             }
43522             
43523             this.validate();
43524         }
43525     },
43526     
43527     getRawValue : function()
43528     {
43529         var v = this.inputEl().getValue();
43530         
43531         return v;
43532     },
43533     
43534     getValue : function()
43535     {
43536         return this.fixPrecision(this.parseValue(this.getRawValue()));
43537     },
43538     
43539     parseValue : function(value)
43540     {
43541         if(this.thousandsDelimiter) {
43542             value += "";
43543             r = new RegExp(",", "g");
43544             value = value.replace(r, "");
43545         }
43546         
43547         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43548         return isNaN(value) ? '' : value;
43549         
43550     },
43551     
43552     fixPrecision : function(value)
43553     {
43554         if(this.thousandsDelimiter) {
43555             value += "";
43556             r = new RegExp(",", "g");
43557             value = value.replace(r, "");
43558         }
43559         
43560         var nan = isNaN(value);
43561         
43562         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43563             return nan ? '' : value;
43564         }
43565         return parseFloat(value).toFixed(this.decimalPrecision);
43566     },
43567     
43568     decimalPrecisionFcn : function(v)
43569     {
43570         return Math.floor(v);
43571     },
43572     
43573     validateValue : function(value)
43574     {
43575         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43576             return false;
43577         }
43578         
43579         var num = this.parseValue(value);
43580         
43581         if(isNaN(num)){
43582             this.markInvalid(String.format(this.nanText, value));
43583             return false;
43584         }
43585         
43586         if(num < this.minValue){
43587             this.markInvalid(String.format(this.minText, this.minValue));
43588             return false;
43589         }
43590         
43591         if(num > this.maxValue){
43592             this.markInvalid(String.format(this.maxText, this.maxValue));
43593             return false;
43594         }
43595         
43596         return true;
43597     },
43598     
43599     validate : function()
43600     {
43601         if(this.disabled || this.allowBlank){
43602             this.markValid();
43603             return true;
43604         }
43605         
43606         var currency = this.getCurrency();
43607         
43608         if(this.validateValue(this.getRawValue()) && currency.length){
43609             this.markValid();
43610             return true;
43611         }
43612         
43613         this.markInvalid();
43614         return false;
43615     },
43616     
43617     getName: function()
43618     {
43619         return this.name;
43620     },
43621     
43622     beforeBlur : function()
43623     {
43624         if(!this.castInt){
43625             return;
43626         }
43627         
43628         var v = this.parseValue(this.getRawValue());
43629         
43630         if(v || v == 0){
43631             this.setValue(v);
43632         }
43633     },
43634     
43635     onBlur : function()
43636     {
43637         this.beforeBlur();
43638         
43639         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43640             //this.el.removeClass(this.focusClass);
43641         }
43642         
43643         this.hasFocus = false;
43644         
43645         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43646             this.validate();
43647         }
43648         
43649         var v = this.getValue();
43650         
43651         if(String(v) !== String(this.startValue)){
43652             this.fireEvent('change', this, v, this.startValue);
43653         }
43654         
43655         this.fireEvent("blur", this);
43656     },
43657     
43658     inputEl : function()
43659     {
43660         return this.el.select('.roo-money-amount-input', true).first();
43661     },
43662     
43663     currencyEl : function()
43664     {
43665         return this.el.select('.roo-money-currency-input', true).first();
43666     },
43667     
43668     hiddenEl : function()
43669     {
43670         return this.el.select('input.hidden-number-input',true).first();
43671     }
43672     
43673 });/**
43674  * @class Roo.bootstrap.BezierSignature
43675  * @extends Roo.bootstrap.Component
43676  * Bootstrap BezierSignature class
43677  * This script refer to:
43678  *    Title: Signature Pad
43679  *    Author: szimek
43680  *    Availability: https://github.com/szimek/signature_pad
43681  *
43682  * @constructor
43683  * Create a new BezierSignature
43684  * @param {Object} config The config object
43685  */
43686
43687 Roo.bootstrap.BezierSignature = function(config){
43688     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43689     this.addEvents({
43690         "resize" : true
43691     });
43692 };
43693
43694 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43695 {
43696      
43697     curve_data: [],
43698     
43699     is_empty: true,
43700     
43701     mouse_btn_down: true,
43702     
43703     /**
43704      * @cfg {int} canvas height
43705      */
43706     canvas_height: '200px',
43707     
43708     /**
43709      * @cfg {float|function} Radius of a single dot.
43710      */ 
43711     dot_size: false,
43712     
43713     /**
43714      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43715      */
43716     min_width: 0.5,
43717     
43718     /**
43719      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43720      */
43721     max_width: 2.5,
43722     
43723     /**
43724      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43725      */
43726     throttle: 16,
43727     
43728     /**
43729      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43730      */
43731     min_distance: 5,
43732     
43733     /**
43734      * @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.
43735      */
43736     bg_color: 'rgba(0, 0, 0, 0)',
43737     
43738     /**
43739      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43740      */
43741     dot_color: 'black',
43742     
43743     /**
43744      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43745      */ 
43746     velocity_filter_weight: 0.7,
43747     
43748     /**
43749      * @cfg {function} Callback when stroke begin. 
43750      */
43751     onBegin: false,
43752     
43753     /**
43754      * @cfg {function} Callback when stroke end.
43755      */
43756     onEnd: false,
43757     
43758     getAutoCreate : function()
43759     {
43760         var cls = 'roo-signature column';
43761         
43762         if(this.cls){
43763             cls += ' ' + this.cls;
43764         }
43765         
43766         var col_sizes = [
43767             'lg',
43768             'md',
43769             'sm',
43770             'xs'
43771         ];
43772         
43773         for(var i = 0; i < col_sizes.length; i++) {
43774             if(this[col_sizes[i]]) {
43775                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43776             }
43777         }
43778         
43779         var cfg = {
43780             tag: 'div',
43781             cls: cls,
43782             cn: [
43783                 {
43784                     tag: 'div',
43785                     cls: 'roo-signature-body',
43786                     cn: [
43787                         {
43788                             tag: 'canvas',
43789                             cls: 'roo-signature-body-canvas',
43790                             height: this.canvas_height,
43791                             width: this.canvas_width
43792                         }
43793                     ]
43794                 },
43795                 {
43796                     tag: 'input',
43797                     type: 'file',
43798                     style: 'display: none'
43799                 }
43800             ]
43801         };
43802         
43803         return cfg;
43804     },
43805     
43806     initEvents: function() 
43807     {
43808         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43809         
43810         var canvas = this.canvasEl();
43811         
43812         // mouse && touch event swapping...
43813         canvas.dom.style.touchAction = 'none';
43814         canvas.dom.style.msTouchAction = 'none';
43815         
43816         this.mouse_btn_down = false;
43817         canvas.on('mousedown', this._handleMouseDown, this);
43818         canvas.on('mousemove', this._handleMouseMove, this);
43819         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43820         
43821         if (window.PointerEvent) {
43822             canvas.on('pointerdown', this._handleMouseDown, this);
43823             canvas.on('pointermove', this._handleMouseMove, this);
43824             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43825         }
43826         
43827         if ('ontouchstart' in window) {
43828             canvas.on('touchstart', this._handleTouchStart, this);
43829             canvas.on('touchmove', this._handleTouchMove, this);
43830             canvas.on('touchend', this._handleTouchEnd, this);
43831         }
43832         
43833         Roo.EventManager.onWindowResize(this.resize, this, true);
43834         
43835         // file input event
43836         this.fileEl().on('change', this.uploadImage, this);
43837         
43838         this.clear();
43839         
43840         this.resize();
43841     },
43842     
43843     resize: function(){
43844         
43845         var canvas = this.canvasEl().dom;
43846         var ctx = this.canvasElCtx();
43847         var img_data = false;
43848         
43849         if(canvas.width > 0) {
43850             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43851         }
43852         // setting canvas width will clean img data
43853         canvas.width = 0;
43854         
43855         var style = window.getComputedStyle ? 
43856             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43857             
43858         var padding_left = parseInt(style.paddingLeft) || 0;
43859         var padding_right = parseInt(style.paddingRight) || 0;
43860         
43861         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43862         
43863         if(img_data) {
43864             ctx.putImageData(img_data, 0, 0);
43865         }
43866     },
43867     
43868     _handleMouseDown: function(e)
43869     {
43870         if (e.browserEvent.which === 1) {
43871             this.mouse_btn_down = true;
43872             this.strokeBegin(e);
43873         }
43874     },
43875     
43876     _handleMouseMove: function (e)
43877     {
43878         if (this.mouse_btn_down) {
43879             this.strokeMoveUpdate(e);
43880         }
43881     },
43882     
43883     _handleMouseUp: function (e)
43884     {
43885         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43886             this.mouse_btn_down = false;
43887             this.strokeEnd(e);
43888         }
43889     },
43890     
43891     _handleTouchStart: function (e) {
43892         
43893         e.preventDefault();
43894         if (e.browserEvent.targetTouches.length === 1) {
43895             // var touch = e.browserEvent.changedTouches[0];
43896             // this.strokeBegin(touch);
43897             
43898              this.strokeBegin(e); // assume e catching the correct xy...
43899         }
43900     },
43901     
43902     _handleTouchMove: function (e) {
43903         e.preventDefault();
43904         // var touch = event.targetTouches[0];
43905         // _this._strokeMoveUpdate(touch);
43906         this.strokeMoveUpdate(e);
43907     },
43908     
43909     _handleTouchEnd: function (e) {
43910         var wasCanvasTouched = e.target === this.canvasEl().dom;
43911         if (wasCanvasTouched) {
43912             e.preventDefault();
43913             // var touch = event.changedTouches[0];
43914             // _this._strokeEnd(touch);
43915             this.strokeEnd(e);
43916         }
43917     },
43918     
43919     reset: function () {
43920         this._lastPoints = [];
43921         this._lastVelocity = 0;
43922         this._lastWidth = (this.min_width + this.max_width) / 2;
43923         this.canvasElCtx().fillStyle = this.dot_color;
43924     },
43925     
43926     strokeMoveUpdate: function(e)
43927     {
43928         this.strokeUpdate(e);
43929         
43930         if (this.throttle) {
43931             this.throttleStroke(this.strokeUpdate, this.throttle);
43932         }
43933         else {
43934             this.strokeUpdate(e);
43935         }
43936     },
43937     
43938     strokeBegin: function(e)
43939     {
43940         var newPointGroup = {
43941             color: this.dot_color,
43942             points: []
43943         };
43944         
43945         if (typeof this.onBegin === 'function') {
43946             this.onBegin(e);
43947         }
43948         
43949         this.curve_data.push(newPointGroup);
43950         this.reset();
43951         this.strokeUpdate(e);
43952     },
43953     
43954     strokeUpdate: function(e)
43955     {
43956         var rect = this.canvasEl().dom.getBoundingClientRect();
43957         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43958         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43959         var lastPoints = lastPointGroup.points;
43960         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43961         var isLastPointTooClose = lastPoint
43962             ? point.distanceTo(lastPoint) <= this.min_distance
43963             : false;
43964         var color = lastPointGroup.color;
43965         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43966             var curve = this.addPoint(point);
43967             if (!lastPoint) {
43968                 this.drawDot({color: color, point: point});
43969             }
43970             else if (curve) {
43971                 this.drawCurve({color: color, curve: curve});
43972             }
43973             lastPoints.push({
43974                 time: point.time,
43975                 x: point.x,
43976                 y: point.y
43977             });
43978         }
43979     },
43980     
43981     strokeEnd: function(e)
43982     {
43983         this.strokeUpdate(e);
43984         if (typeof this.onEnd === 'function') {
43985             this.onEnd(e);
43986         }
43987     },
43988     
43989     addPoint:  function (point) {
43990         var _lastPoints = this._lastPoints;
43991         _lastPoints.push(point);
43992         if (_lastPoints.length > 2) {
43993             if (_lastPoints.length === 3) {
43994                 _lastPoints.unshift(_lastPoints[0]);
43995             }
43996             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43997             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43998             _lastPoints.shift();
43999             return curve;
44000         }
44001         return null;
44002     },
44003     
44004     calculateCurveWidths: function (startPoint, endPoint) {
44005         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44006             (1 - this.velocity_filter_weight) * this._lastVelocity;
44007
44008         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44009         var widths = {
44010             end: newWidth,
44011             start: this._lastWidth
44012         };
44013         
44014         this._lastVelocity = velocity;
44015         this._lastWidth = newWidth;
44016         return widths;
44017     },
44018     
44019     drawDot: function (_a) {
44020         var color = _a.color, point = _a.point;
44021         var ctx = this.canvasElCtx();
44022         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44023         ctx.beginPath();
44024         this.drawCurveSegment(point.x, point.y, width);
44025         ctx.closePath();
44026         ctx.fillStyle = color;
44027         ctx.fill();
44028     },
44029     
44030     drawCurve: function (_a) {
44031         var color = _a.color, curve = _a.curve;
44032         var ctx = this.canvasElCtx();
44033         var widthDelta = curve.endWidth - curve.startWidth;
44034         var drawSteps = Math.floor(curve.length()) * 2;
44035         ctx.beginPath();
44036         ctx.fillStyle = color;
44037         for (var i = 0; i < drawSteps; i += 1) {
44038         var t = i / drawSteps;
44039         var tt = t * t;
44040         var ttt = tt * t;
44041         var u = 1 - t;
44042         var uu = u * u;
44043         var uuu = uu * u;
44044         var x = uuu * curve.startPoint.x;
44045         x += 3 * uu * t * curve.control1.x;
44046         x += 3 * u * tt * curve.control2.x;
44047         x += ttt * curve.endPoint.x;
44048         var y = uuu * curve.startPoint.y;
44049         y += 3 * uu * t * curve.control1.y;
44050         y += 3 * u * tt * curve.control2.y;
44051         y += ttt * curve.endPoint.y;
44052         var width = curve.startWidth + ttt * widthDelta;
44053         this.drawCurveSegment(x, y, width);
44054         }
44055         ctx.closePath();
44056         ctx.fill();
44057     },
44058     
44059     drawCurveSegment: function (x, y, width) {
44060         var ctx = this.canvasElCtx();
44061         ctx.moveTo(x, y);
44062         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44063         this.is_empty = false;
44064     },
44065     
44066     clear: function()
44067     {
44068         var ctx = this.canvasElCtx();
44069         var canvas = this.canvasEl().dom;
44070         ctx.fillStyle = this.bg_color;
44071         ctx.clearRect(0, 0, canvas.width, canvas.height);
44072         ctx.fillRect(0, 0, canvas.width, canvas.height);
44073         this.curve_data = [];
44074         this.reset();
44075         this.is_empty = true;
44076     },
44077     
44078     fileEl: function()
44079     {
44080         return  this.el.select('input',true).first();
44081     },
44082     
44083     canvasEl: function()
44084     {
44085         return this.el.select('canvas',true).first();
44086     },
44087     
44088     canvasElCtx: function()
44089     {
44090         return this.el.select('canvas',true).first().dom.getContext('2d');
44091     },
44092     
44093     getImage: function(type)
44094     {
44095         if(this.is_empty) {
44096             return false;
44097         }
44098         
44099         // encryption ?
44100         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44101     },
44102     
44103     drawFromImage: function(img_src)
44104     {
44105         var img = new Image();
44106         
44107         img.onload = function(){
44108             this.canvasElCtx().drawImage(img, 0, 0);
44109         }.bind(this);
44110         
44111         img.src = img_src;
44112         
44113         this.is_empty = false;
44114     },
44115     
44116     selectImage: function()
44117     {
44118         this.fileEl().dom.click();
44119     },
44120     
44121     uploadImage: function(e)
44122     {
44123         var reader = new FileReader();
44124         
44125         reader.onload = function(e){
44126             var img = new Image();
44127             img.onload = function(){
44128                 this.reset();
44129                 this.canvasElCtx().drawImage(img, 0, 0);
44130             }.bind(this);
44131             img.src = e.target.result;
44132         }.bind(this);
44133         
44134         reader.readAsDataURL(e.target.files[0]);
44135     },
44136     
44137     // Bezier Point Constructor
44138     Point: (function () {
44139         function Point(x, y, time) {
44140             this.x = x;
44141             this.y = y;
44142             this.time = time || Date.now();
44143         }
44144         Point.prototype.distanceTo = function (start) {
44145             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44146         };
44147         Point.prototype.equals = function (other) {
44148             return this.x === other.x && this.y === other.y && this.time === other.time;
44149         };
44150         Point.prototype.velocityFrom = function (start) {
44151             return this.time !== start.time
44152             ? this.distanceTo(start) / (this.time - start.time)
44153             : 0;
44154         };
44155         return Point;
44156     }()),
44157     
44158     
44159     // Bezier Constructor
44160     Bezier: (function () {
44161         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44162             this.startPoint = startPoint;
44163             this.control2 = control2;
44164             this.control1 = control1;
44165             this.endPoint = endPoint;
44166             this.startWidth = startWidth;
44167             this.endWidth = endWidth;
44168         }
44169         Bezier.fromPoints = function (points, widths, scope) {
44170             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44171             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44172             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44173         };
44174         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44175             var dx1 = s1.x - s2.x;
44176             var dy1 = s1.y - s2.y;
44177             var dx2 = s2.x - s3.x;
44178             var dy2 = s2.y - s3.y;
44179             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44180             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44181             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44182             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44183             var dxm = m1.x - m2.x;
44184             var dym = m1.y - m2.y;
44185             var k = l2 / (l1 + l2);
44186             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44187             var tx = s2.x - cm.x;
44188             var ty = s2.y - cm.y;
44189             return {
44190                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44191                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44192             };
44193         };
44194         Bezier.prototype.length = function () {
44195             var steps = 10;
44196             var length = 0;
44197             var px;
44198             var py;
44199             for (var i = 0; i <= steps; i += 1) {
44200                 var t = i / steps;
44201                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44202                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44203                 if (i > 0) {
44204                     var xdiff = cx - px;
44205                     var ydiff = cy - py;
44206                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44207                 }
44208                 px = cx;
44209                 py = cy;
44210             }
44211             return length;
44212         };
44213         Bezier.prototype.point = function (t, start, c1, c2, end) {
44214             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44215             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44216             + (3.0 * c2 * (1.0 - t) * t * t)
44217             + (end * t * t * t);
44218         };
44219         return Bezier;
44220     }()),
44221     
44222     throttleStroke: function(fn, wait) {
44223       if (wait === void 0) { wait = 250; }
44224       var previous = 0;
44225       var timeout = null;
44226       var result;
44227       var storedContext;
44228       var storedArgs;
44229       var later = function () {
44230           previous = Date.now();
44231           timeout = null;
44232           result = fn.apply(storedContext, storedArgs);
44233           if (!timeout) {
44234               storedContext = null;
44235               storedArgs = [];
44236           }
44237       };
44238       return function wrapper() {
44239           var args = [];
44240           for (var _i = 0; _i < arguments.length; _i++) {
44241               args[_i] = arguments[_i];
44242           }
44243           var now = Date.now();
44244           var remaining = wait - (now - previous);
44245           storedContext = this;
44246           storedArgs = args;
44247           if (remaining <= 0 || remaining > wait) {
44248               if (timeout) {
44249                   clearTimeout(timeout);
44250                   timeout = null;
44251               }
44252               previous = now;
44253               result = fn.apply(storedContext, storedArgs);
44254               if (!timeout) {
44255                   storedContext = null;
44256                   storedArgs = [];
44257               }
44258           }
44259           else if (!timeout) {
44260               timeout = window.setTimeout(later, remaining);
44261           }
44262           return result;
44263       };
44264   }
44265   
44266 });
44267
44268  
44269
44270