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     headerEl : false,
19656     contentEl : false,
19657     
19658     
19659     getChildContainer : function()
19660     {
19661         return this.contentEl;
19662         
19663     },
19664     getPopoverHeader : function()
19665     {
19666         this.title = true; // flag not to hide it..
19667         return this.headerEl
19668     },
19669     
19670     
19671     getAutoCreate : function(){
19672          
19673         var cfg = {
19674            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19675            style: 'display:block',
19676            cn : [
19677                 {
19678                     cls : 'arrow'
19679                 },
19680                 {
19681                     cls : 'popover-inner ',
19682                     cn : [
19683                         {
19684                             tag: 'h3',
19685                             cls: 'popover-title popover-header',
19686                             html : this.title === false ? '' : this.title
19687                         },
19688                         {
19689                             cls : 'popover-content popover-body '  + (this.cls || ''),
19690                             html : this.html || ''
19691                         }
19692                     ]
19693                     
19694                 }
19695            ]
19696         };
19697         
19698         return cfg;
19699     },
19700     /**
19701      * @param {string} the title
19702      */
19703     setTitle: function(str)
19704     {
19705         this.title = str;
19706         if (this.el) {
19707             this.headerEl.dom.innerHTML = str;
19708         }
19709         
19710     },
19711     /**
19712      * @param {string} the body content
19713      */
19714     setContent: function(str)
19715     {
19716         this.html = str;
19717         if (this.contentEl) {
19718             this.contentEl.dom.innerHTML = str;
19719         }
19720         
19721     },
19722     // as it get's added to the bottom of the page.
19723     onRender : function(ct, position)
19724     {
19725         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19726         
19727         this.contentEl = this.el.select('.popover-content',true).first();
19728         this.headerEl =  this.el.select('.popover-title',true).first();
19729         
19730         if(!this.el){
19731             var cfg = Roo.apply({},  this.getAutoCreate());
19732             cfg.id = Roo.id();
19733             
19734             if (this.cls) {
19735                 cfg.cls += ' ' + this.cls;
19736             }
19737             if (this.style) {
19738                 cfg.style = this.style;
19739             }
19740             //Roo.log("adding to ");
19741             this.el = Roo.get(document.body).createChild(cfg, position);
19742 //            Roo.log(this.el);
19743         }
19744         
19745         var nitems = [];
19746         if(typeof(this.items) != 'undefined'){
19747             var items = this.items;
19748             delete this.items;
19749
19750             for(var i =0;i < items.length;i++) {
19751                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19752             }
19753         }
19754
19755         this.items = nitems;
19756         
19757         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19758         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19759         
19760         
19761         
19762         this.initEvents();
19763     },
19764     
19765     resizeMask : function()
19766     {
19767         this.maskEl.setSize(
19768             Roo.lib.Dom.getViewWidth(true),
19769             Roo.lib.Dom.getViewHeight(true)
19770         );
19771     },
19772     
19773     initEvents : function()
19774     {
19775         
19776         if (!this.modal) { 
19777             Roo.bootstrap.Popover.register(this);
19778         }
19779          
19780         
19781         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19782         this.el.enableDisplayMode('block');
19783         this.el.hide();
19784         if (this.over === false && !this.parent()) {
19785             return; 
19786         }
19787         if (this.triggers === false) {
19788             return;
19789         }
19790          
19791         // support parent
19792         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19793         var triggers = this.trigger ? this.trigger.split(' ') : [];
19794         Roo.each(triggers, function(trigger) {
19795         
19796             if (trigger == 'click') {
19797                 on_el.on('click', this.toggle, this);
19798             } else if (trigger != 'manual') {
19799                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19800                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19801       
19802                 on_el.on(eventIn  ,this.enter, this);
19803                 on_el.on(eventOut, this.leave, this);
19804             }
19805         }, this);
19806         
19807     },
19808     
19809     
19810     // private
19811     timeout : null,
19812     hoverState : null,
19813     
19814     toggle : function () {
19815         this.hoverState == 'in' ? this.leave() : this.enter();
19816     },
19817     
19818     enter : function () {
19819         
19820         clearTimeout(this.timeout);
19821     
19822         this.hoverState = 'in';
19823     
19824         if (!this.delay || !this.delay.show) {
19825             this.show();
19826             return;
19827         }
19828         var _t = this;
19829         this.timeout = setTimeout(function () {
19830             if (_t.hoverState == 'in') {
19831                 _t.show();
19832             }
19833         }, this.delay.show)
19834     },
19835     
19836     leave : function() {
19837         clearTimeout(this.timeout);
19838     
19839         this.hoverState = 'out';
19840     
19841         if (!this.delay || !this.delay.hide) {
19842             this.hide();
19843             return;
19844         }
19845         var _t = this;
19846         this.timeout = setTimeout(function () {
19847             if (_t.hoverState == 'out') {
19848                 _t.hide();
19849             }
19850         }, this.delay.hide)
19851     },
19852     /**
19853      * Show the popover
19854      * @param {Roo.Element|string|false} - element to align and point to.
19855      */
19856     show : function (on_el)
19857     {
19858         
19859         on_el = on_el || false; // default to false
19860         if (!on_el) {
19861             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19862                 on_el = this.parent().el;
19863             } else if (this.over) {
19864                 Roo.get(this.over);
19865             }
19866             
19867         }
19868         
19869         if (!this.el) {
19870             this.render(document.body);
19871         }
19872         
19873         
19874         this.el.removeClass([
19875             'fade','top','bottom', 'left', 'right','in',
19876             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19877         ]);
19878         
19879         if (this.title === false) {
19880             this.headerEl.hide();
19881         }
19882         
19883         
19884         var placement = typeof this.placement == 'function' ?
19885             this.placement.call(this, this.el, on_el) :
19886             this.placement;
19887             
19888         /*
19889         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19890         
19891         // I think  'auto right' - but 
19892         
19893         var autoPlace = autoToken.test(placement);
19894         if (autoPlace) {
19895             placement = placement.replace(autoToken, '') || 'top';
19896         }
19897         */
19898         
19899         
19900         this.el.show();
19901         this.el.dom.style.display='block';
19902         
19903         //this.el.appendTo(on_el);
19904         
19905         var p = this.getPosition();
19906         var box = this.el.getBox();
19907         
19908         
19909         var align = Roo.bootstrap.Popover.alignment[placement];
19910         this.el.addClass(align[2]);
19911
19912 //        Roo.log(align);
19913
19914         if (on_el) {
19915             this.el.alignTo(on_el, align[0],align[1]);
19916         } else {
19917             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19918             var es = this.el.getSize();
19919             var x = Roo.lib.Dom.getViewWidth()/2;
19920             var y = Roo.lib.Dom.getViewHeight()/2;
19921             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19922             
19923         }
19924
19925         
19926         //var arrow = this.el.select('.arrow',true).first();
19927         //arrow.set(align[2], 
19928         
19929         this.el.addClass('in');
19930         
19931         
19932         if (this.el.hasClass('fade')) {
19933             // fade it?
19934         }
19935         
19936         this.hoverState = 'in';
19937         
19938         if (this.modal) {
19939             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19940             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19941             this.maskEl.dom.style.display = 'block';
19942             this.maskEl.addClass('show');
19943         }
19944         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19945
19946         
19947         
19948         this.fireEvent('show', this);
19949         
19950     },
19951     hide : function()
19952     {
19953         this.el.setXY([0,0]);
19954         this.el.removeClass('in');
19955         this.el.hide();
19956         this.hoverState = null;
19957         this.maskEl.hide(); // always..
19958         this.fireEvent('hide', this);
19959     }
19960     
19961 });
19962
19963
19964 Roo.apply(Roo.bootstrap.Popover, {
19965
19966     alignment : {
19967         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19968         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19969         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19970         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19971     },
19972     
19973     zIndex : 20001,
19974
19975     clickHander : false,
19976     
19977
19978     onMouseDown : function(e)
19979     {
19980         if (!e.getTarget(".roo-popover")) {
19981             this.hideAll();
19982         }
19983          
19984     },
19985     
19986     popups : [],
19987     
19988     register : function(popup)
19989     {
19990         if (!Roo.bootstrap.Popover.clickHandler) {
19991             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19992         }
19993         // hide other popups.
19994         this.hideAll();
19995         this.popups.push(popup);
19996     },
19997     hideAll : function()
19998     {
19999         this.popups.forEach(function(p) {
20000             p.hide();
20001         });
20002     }
20003
20004 });/*
20005  * - LGPL
20006  *
20007  * Card header - holder for the card header elements.
20008  * 
20009  */
20010
20011 /**
20012  * @class Roo.bootstrap.PopoverNav
20013  * @extends Roo.bootstrap.NavGroup
20014  * Bootstrap Popover header navigation class
20015  * @constructor
20016  * Create a new Popover Header Navigation 
20017  * @param {Object} config The config object
20018  */
20019
20020 Roo.bootstrap.PopoverNav = function(config){
20021     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20022 };
20023
20024 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavGroup,  {
20025     
20026     
20027     container_method : 'getPopoverHeader' 
20028     
20029      
20030     
20031     
20032    
20033 });
20034
20035  
20036
20037  /*
20038  * - LGPL
20039  *
20040  * Progress
20041  * 
20042  */
20043
20044 /**
20045  * @class Roo.bootstrap.Progress
20046  * @extends Roo.bootstrap.Component
20047  * Bootstrap Progress class
20048  * @cfg {Boolean} striped striped of the progress bar
20049  * @cfg {Boolean} active animated of the progress bar
20050  * 
20051  * 
20052  * @constructor
20053  * Create a new Progress
20054  * @param {Object} config The config object
20055  */
20056
20057 Roo.bootstrap.Progress = function(config){
20058     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20059 };
20060
20061 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20062     
20063     striped : false,
20064     active: false,
20065     
20066     getAutoCreate : function(){
20067         var cfg = {
20068             tag: 'div',
20069             cls: 'progress'
20070         };
20071         
20072         
20073         if(this.striped){
20074             cfg.cls += ' progress-striped';
20075         }
20076       
20077         if(this.active){
20078             cfg.cls += ' active';
20079         }
20080         
20081         
20082         return cfg;
20083     }
20084    
20085 });
20086
20087  
20088
20089  /*
20090  * - LGPL
20091  *
20092  * ProgressBar
20093  * 
20094  */
20095
20096 /**
20097  * @class Roo.bootstrap.ProgressBar
20098  * @extends Roo.bootstrap.Component
20099  * Bootstrap ProgressBar class
20100  * @cfg {Number} aria_valuenow aria-value now
20101  * @cfg {Number} aria_valuemin aria-value min
20102  * @cfg {Number} aria_valuemax aria-value max
20103  * @cfg {String} label label for the progress bar
20104  * @cfg {String} panel (success | info | warning | danger )
20105  * @cfg {String} role role of the progress bar
20106  * @cfg {String} sr_only text
20107  * 
20108  * 
20109  * @constructor
20110  * Create a new ProgressBar
20111  * @param {Object} config The config object
20112  */
20113
20114 Roo.bootstrap.ProgressBar = function(config){
20115     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20116 };
20117
20118 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20119     
20120     aria_valuenow : 0,
20121     aria_valuemin : 0,
20122     aria_valuemax : 100,
20123     label : false,
20124     panel : false,
20125     role : false,
20126     sr_only: false,
20127     
20128     getAutoCreate : function()
20129     {
20130         
20131         var cfg = {
20132             tag: 'div',
20133             cls: 'progress-bar',
20134             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20135         };
20136         
20137         if(this.sr_only){
20138             cfg.cn = {
20139                 tag: 'span',
20140                 cls: 'sr-only',
20141                 html: this.sr_only
20142             }
20143         }
20144         
20145         if(this.role){
20146             cfg.role = this.role;
20147         }
20148         
20149         if(this.aria_valuenow){
20150             cfg['aria-valuenow'] = this.aria_valuenow;
20151         }
20152         
20153         if(this.aria_valuemin){
20154             cfg['aria-valuemin'] = this.aria_valuemin;
20155         }
20156         
20157         if(this.aria_valuemax){
20158             cfg['aria-valuemax'] = this.aria_valuemax;
20159         }
20160         
20161         if(this.label && !this.sr_only){
20162             cfg.html = this.label;
20163         }
20164         
20165         if(this.panel){
20166             cfg.cls += ' progress-bar-' + this.panel;
20167         }
20168         
20169         return cfg;
20170     },
20171     
20172     update : function(aria_valuenow)
20173     {
20174         this.aria_valuenow = aria_valuenow;
20175         
20176         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20177     }
20178    
20179 });
20180
20181  
20182
20183  /*
20184  * - LGPL
20185  *
20186  * column
20187  * 
20188  */
20189
20190 /**
20191  * @class Roo.bootstrap.TabGroup
20192  * @extends Roo.bootstrap.Column
20193  * Bootstrap Column class
20194  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20195  * @cfg {Boolean} carousel true to make the group behave like a carousel
20196  * @cfg {Boolean} bullets show bullets for the panels
20197  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20198  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20199  * @cfg {Boolean} showarrow (true|false) show arrow default true
20200  * 
20201  * @constructor
20202  * Create a new TabGroup
20203  * @param {Object} config The config object
20204  */
20205
20206 Roo.bootstrap.TabGroup = function(config){
20207     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20208     if (!this.navId) {
20209         this.navId = Roo.id();
20210     }
20211     this.tabs = [];
20212     Roo.bootstrap.TabGroup.register(this);
20213     
20214 };
20215
20216 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20217     
20218     carousel : false,
20219     transition : false,
20220     bullets : 0,
20221     timer : 0,
20222     autoslide : false,
20223     slideFn : false,
20224     slideOnTouch : false,
20225     showarrow : true,
20226     
20227     getAutoCreate : function()
20228     {
20229         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20230         
20231         cfg.cls += ' tab-content';
20232         
20233         if (this.carousel) {
20234             cfg.cls += ' carousel slide';
20235             
20236             cfg.cn = [{
20237                cls : 'carousel-inner',
20238                cn : []
20239             }];
20240         
20241             if(this.bullets  && !Roo.isTouch){
20242                 
20243                 var bullets = {
20244                     cls : 'carousel-bullets',
20245                     cn : []
20246                 };
20247                
20248                 if(this.bullets_cls){
20249                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20250                 }
20251                 
20252                 bullets.cn.push({
20253                     cls : 'clear'
20254                 });
20255                 
20256                 cfg.cn[0].cn.push(bullets);
20257             }
20258             
20259             if(this.showarrow){
20260                 cfg.cn[0].cn.push({
20261                     tag : 'div',
20262                     class : 'carousel-arrow',
20263                     cn : [
20264                         {
20265                             tag : 'div',
20266                             class : 'carousel-prev',
20267                             cn : [
20268                                 {
20269                                     tag : 'i',
20270                                     class : 'fa fa-chevron-left'
20271                                 }
20272                             ]
20273                         },
20274                         {
20275                             tag : 'div',
20276                             class : 'carousel-next',
20277                             cn : [
20278                                 {
20279                                     tag : 'i',
20280                                     class : 'fa fa-chevron-right'
20281                                 }
20282                             ]
20283                         }
20284                     ]
20285                 });
20286             }
20287             
20288         }
20289         
20290         return cfg;
20291     },
20292     
20293     initEvents:  function()
20294     {
20295 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20296 //            this.el.on("touchstart", this.onTouchStart, this);
20297 //        }
20298         
20299         if(this.autoslide){
20300             var _this = this;
20301             
20302             this.slideFn = window.setInterval(function() {
20303                 _this.showPanelNext();
20304             }, this.timer);
20305         }
20306         
20307         if(this.showarrow){
20308             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20309             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20310         }
20311         
20312         
20313     },
20314     
20315 //    onTouchStart : function(e, el, o)
20316 //    {
20317 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20318 //            return;
20319 //        }
20320 //        
20321 //        this.showPanelNext();
20322 //    },
20323     
20324     
20325     getChildContainer : function()
20326     {
20327         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20328     },
20329     
20330     /**
20331     * register a Navigation item
20332     * @param {Roo.bootstrap.NavItem} the navitem to add
20333     */
20334     register : function(item)
20335     {
20336         this.tabs.push( item);
20337         item.navId = this.navId; // not really needed..
20338         this.addBullet();
20339     
20340     },
20341     
20342     getActivePanel : function()
20343     {
20344         var r = false;
20345         Roo.each(this.tabs, function(t) {
20346             if (t.active) {
20347                 r = t;
20348                 return false;
20349             }
20350             return null;
20351         });
20352         return r;
20353         
20354     },
20355     getPanelByName : function(n)
20356     {
20357         var r = false;
20358         Roo.each(this.tabs, function(t) {
20359             if (t.tabId == n) {
20360                 r = t;
20361                 return false;
20362             }
20363             return null;
20364         });
20365         return r;
20366     },
20367     indexOfPanel : function(p)
20368     {
20369         var r = false;
20370         Roo.each(this.tabs, function(t,i) {
20371             if (t.tabId == p.tabId) {
20372                 r = i;
20373                 return false;
20374             }
20375             return null;
20376         });
20377         return r;
20378     },
20379     /**
20380      * show a specific panel
20381      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20382      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20383      */
20384     showPanel : function (pan)
20385     {
20386         if(this.transition || typeof(pan) == 'undefined'){
20387             Roo.log("waiting for the transitionend");
20388             return false;
20389         }
20390         
20391         if (typeof(pan) == 'number') {
20392             pan = this.tabs[pan];
20393         }
20394         
20395         if (typeof(pan) == 'string') {
20396             pan = this.getPanelByName(pan);
20397         }
20398         
20399         var cur = this.getActivePanel();
20400         
20401         if(!pan || !cur){
20402             Roo.log('pan or acitve pan is undefined');
20403             return false;
20404         }
20405         
20406         if (pan.tabId == this.getActivePanel().tabId) {
20407             return true;
20408         }
20409         
20410         if (false === cur.fireEvent('beforedeactivate')) {
20411             return false;
20412         }
20413         
20414         if(this.bullets > 0 && !Roo.isTouch){
20415             this.setActiveBullet(this.indexOfPanel(pan));
20416         }
20417         
20418         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20419             
20420             //class="carousel-item carousel-item-next carousel-item-left"
20421             
20422             this.transition = true;
20423             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20424             var lr = dir == 'next' ? 'left' : 'right';
20425             pan.el.addClass(dir); // or prev
20426             pan.el.addClass('carousel-item-' + dir); // or prev
20427             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20428             cur.el.addClass(lr); // or right
20429             pan.el.addClass(lr);
20430             cur.el.addClass('carousel-item-' +lr); // or right
20431             pan.el.addClass('carousel-item-' +lr);
20432             
20433             
20434             var _this = this;
20435             cur.el.on('transitionend', function() {
20436                 Roo.log("trans end?");
20437                 
20438                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20439                 pan.setActive(true);
20440                 
20441                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20442                 cur.setActive(false);
20443                 
20444                 _this.transition = false;
20445                 
20446             }, this, { single:  true } );
20447             
20448             return true;
20449         }
20450         
20451         cur.setActive(false);
20452         pan.setActive(true);
20453         
20454         return true;
20455         
20456     },
20457     showPanelNext : function()
20458     {
20459         var i = this.indexOfPanel(this.getActivePanel());
20460         
20461         if (i >= this.tabs.length - 1 && !this.autoslide) {
20462             return;
20463         }
20464         
20465         if (i >= this.tabs.length - 1 && this.autoslide) {
20466             i = -1;
20467         }
20468         
20469         this.showPanel(this.tabs[i+1]);
20470     },
20471     
20472     showPanelPrev : function()
20473     {
20474         var i = this.indexOfPanel(this.getActivePanel());
20475         
20476         if (i  < 1 && !this.autoslide) {
20477             return;
20478         }
20479         
20480         if (i < 1 && this.autoslide) {
20481             i = this.tabs.length;
20482         }
20483         
20484         this.showPanel(this.tabs[i-1]);
20485     },
20486     
20487     
20488     addBullet: function()
20489     {
20490         if(!this.bullets || Roo.isTouch){
20491             return;
20492         }
20493         var ctr = this.el.select('.carousel-bullets',true).first();
20494         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20495         var bullet = ctr.createChild({
20496             cls : 'bullet bullet-' + i
20497         },ctr.dom.lastChild);
20498         
20499         
20500         var _this = this;
20501         
20502         bullet.on('click', (function(e, el, o, ii, t){
20503
20504             e.preventDefault();
20505
20506             this.showPanel(ii);
20507
20508             if(this.autoslide && this.slideFn){
20509                 clearInterval(this.slideFn);
20510                 this.slideFn = window.setInterval(function() {
20511                     _this.showPanelNext();
20512                 }, this.timer);
20513             }
20514
20515         }).createDelegate(this, [i, bullet], true));
20516                 
20517         
20518     },
20519      
20520     setActiveBullet : function(i)
20521     {
20522         if(Roo.isTouch){
20523             return;
20524         }
20525         
20526         Roo.each(this.el.select('.bullet', true).elements, function(el){
20527             el.removeClass('selected');
20528         });
20529
20530         var bullet = this.el.select('.bullet-' + i, true).first();
20531         
20532         if(!bullet){
20533             return;
20534         }
20535         
20536         bullet.addClass('selected');
20537     }
20538     
20539     
20540   
20541 });
20542
20543  
20544
20545  
20546  
20547 Roo.apply(Roo.bootstrap.TabGroup, {
20548     
20549     groups: {},
20550      /**
20551     * register a Navigation Group
20552     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20553     */
20554     register : function(navgrp)
20555     {
20556         this.groups[navgrp.navId] = navgrp;
20557         
20558     },
20559     /**
20560     * fetch a Navigation Group based on the navigation ID
20561     * if one does not exist , it will get created.
20562     * @param {string} the navgroup to add
20563     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20564     */
20565     get: function(navId) {
20566         if (typeof(this.groups[navId]) == 'undefined') {
20567             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20568         }
20569         return this.groups[navId] ;
20570     }
20571     
20572     
20573     
20574 });
20575
20576  /*
20577  * - LGPL
20578  *
20579  * TabPanel
20580  * 
20581  */
20582
20583 /**
20584  * @class Roo.bootstrap.TabPanel
20585  * @extends Roo.bootstrap.Component
20586  * Bootstrap TabPanel class
20587  * @cfg {Boolean} active panel active
20588  * @cfg {String} html panel content
20589  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20590  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20591  * @cfg {String} href click to link..
20592  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20593  * 
20594  * 
20595  * @constructor
20596  * Create a new TabPanel
20597  * @param {Object} config The config object
20598  */
20599
20600 Roo.bootstrap.TabPanel = function(config){
20601     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20602     this.addEvents({
20603         /**
20604              * @event changed
20605              * Fires when the active status changes
20606              * @param {Roo.bootstrap.TabPanel} this
20607              * @param {Boolean} state the new state
20608             
20609          */
20610         'changed': true,
20611         /**
20612              * @event beforedeactivate
20613              * Fires before a tab is de-activated - can be used to do validation on a form.
20614              * @param {Roo.bootstrap.TabPanel} this
20615              * @return {Boolean} false if there is an error
20616             
20617          */
20618         'beforedeactivate': true
20619      });
20620     
20621     this.tabId = this.tabId || Roo.id();
20622   
20623 };
20624
20625 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20626     
20627     active: false,
20628     html: false,
20629     tabId: false,
20630     navId : false,
20631     href : '',
20632     touchSlide : false,
20633     getAutoCreate : function(){
20634         
20635         
20636         var cfg = {
20637             tag: 'div',
20638             // item is needed for carousel - not sure if it has any effect otherwise
20639             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20640             html: this.html || ''
20641         };
20642         
20643         if(this.active){
20644             cfg.cls += ' active';
20645         }
20646         
20647         if(this.tabId){
20648             cfg.tabId = this.tabId;
20649         }
20650         
20651         
20652         
20653         return cfg;
20654     },
20655     
20656     initEvents:  function()
20657     {
20658         var p = this.parent();
20659         
20660         this.navId = this.navId || p.navId;
20661         
20662         if (typeof(this.navId) != 'undefined') {
20663             // not really needed.. but just in case.. parent should be a NavGroup.
20664             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20665             
20666             tg.register(this);
20667             
20668             var i = tg.tabs.length - 1;
20669             
20670             if(this.active && tg.bullets > 0 && i < tg.bullets){
20671                 tg.setActiveBullet(i);
20672             }
20673         }
20674         
20675         this.el.on('click', this.onClick, this);
20676         
20677         if(Roo.isTouch && this.touchSlide){
20678             this.el.on("touchstart", this.onTouchStart, this);
20679             this.el.on("touchmove", this.onTouchMove, this);
20680             this.el.on("touchend", this.onTouchEnd, this);
20681         }
20682         
20683     },
20684     
20685     onRender : function(ct, position)
20686     {
20687         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20688     },
20689     
20690     setActive : function(state)
20691     {
20692         Roo.log("panel - set active " + this.tabId + "=" + state);
20693         
20694         this.active = state;
20695         if (!state) {
20696             this.el.removeClass('active');
20697             
20698         } else  if (!this.el.hasClass('active')) {
20699             this.el.addClass('active');
20700         }
20701         
20702         this.fireEvent('changed', this, state);
20703     },
20704     
20705     onClick : function(e)
20706     {
20707         e.preventDefault();
20708         
20709         if(!this.href.length){
20710             return;
20711         }
20712         
20713         window.location.href = this.href;
20714     },
20715     
20716     startX : 0,
20717     startY : 0,
20718     endX : 0,
20719     endY : 0,
20720     swiping : false,
20721     
20722     onTouchStart : function(e)
20723     {
20724         this.swiping = false;
20725         
20726         this.startX = e.browserEvent.touches[0].clientX;
20727         this.startY = e.browserEvent.touches[0].clientY;
20728     },
20729     
20730     onTouchMove : function(e)
20731     {
20732         this.swiping = true;
20733         
20734         this.endX = e.browserEvent.touches[0].clientX;
20735         this.endY = e.browserEvent.touches[0].clientY;
20736     },
20737     
20738     onTouchEnd : function(e)
20739     {
20740         if(!this.swiping){
20741             this.onClick(e);
20742             return;
20743         }
20744         
20745         var tabGroup = this.parent();
20746         
20747         if(this.endX > this.startX){ // swiping right
20748             tabGroup.showPanelPrev();
20749             return;
20750         }
20751         
20752         if(this.startX > this.endX){ // swiping left
20753             tabGroup.showPanelNext();
20754             return;
20755         }
20756     }
20757     
20758     
20759 });
20760  
20761
20762  
20763
20764  /*
20765  * - LGPL
20766  *
20767  * DateField
20768  * 
20769  */
20770
20771 /**
20772  * @class Roo.bootstrap.DateField
20773  * @extends Roo.bootstrap.Input
20774  * Bootstrap DateField class
20775  * @cfg {Number} weekStart default 0
20776  * @cfg {String} viewMode default empty, (months|years)
20777  * @cfg {String} minViewMode default empty, (months|years)
20778  * @cfg {Number} startDate default -Infinity
20779  * @cfg {Number} endDate default Infinity
20780  * @cfg {Boolean} todayHighlight default false
20781  * @cfg {Boolean} todayBtn default false
20782  * @cfg {Boolean} calendarWeeks default false
20783  * @cfg {Object} daysOfWeekDisabled default empty
20784  * @cfg {Boolean} singleMode default false (true | false)
20785  * 
20786  * @cfg {Boolean} keyboardNavigation default true
20787  * @cfg {String} language default en
20788  * 
20789  * @constructor
20790  * Create a new DateField
20791  * @param {Object} config The config object
20792  */
20793
20794 Roo.bootstrap.DateField = function(config){
20795     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20796      this.addEvents({
20797             /**
20798              * @event show
20799              * Fires when this field show.
20800              * @param {Roo.bootstrap.DateField} this
20801              * @param {Mixed} date The date value
20802              */
20803             show : true,
20804             /**
20805              * @event show
20806              * Fires when this field hide.
20807              * @param {Roo.bootstrap.DateField} this
20808              * @param {Mixed} date The date value
20809              */
20810             hide : true,
20811             /**
20812              * @event select
20813              * Fires when select a date.
20814              * @param {Roo.bootstrap.DateField} this
20815              * @param {Mixed} date The date value
20816              */
20817             select : true,
20818             /**
20819              * @event beforeselect
20820              * Fires when before select a date.
20821              * @param {Roo.bootstrap.DateField} this
20822              * @param {Mixed} date The date value
20823              */
20824             beforeselect : true
20825         });
20826 };
20827
20828 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20829     
20830     /**
20831      * @cfg {String} format
20832      * The default date format string which can be overriden for localization support.  The format must be
20833      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20834      */
20835     format : "m/d/y",
20836     /**
20837      * @cfg {String} altFormats
20838      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20839      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20840      */
20841     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20842     
20843     weekStart : 0,
20844     
20845     viewMode : '',
20846     
20847     minViewMode : '',
20848     
20849     todayHighlight : false,
20850     
20851     todayBtn: false,
20852     
20853     language: 'en',
20854     
20855     keyboardNavigation: true,
20856     
20857     calendarWeeks: false,
20858     
20859     startDate: -Infinity,
20860     
20861     endDate: Infinity,
20862     
20863     daysOfWeekDisabled: [],
20864     
20865     _events: [],
20866     
20867     singleMode : false,
20868     
20869     UTCDate: function()
20870     {
20871         return new Date(Date.UTC.apply(Date, arguments));
20872     },
20873     
20874     UTCToday: function()
20875     {
20876         var today = new Date();
20877         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20878     },
20879     
20880     getDate: function() {
20881             var d = this.getUTCDate();
20882             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20883     },
20884     
20885     getUTCDate: function() {
20886             return this.date;
20887     },
20888     
20889     setDate: function(d) {
20890             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20891     },
20892     
20893     setUTCDate: function(d) {
20894             this.date = d;
20895             this.setValue(this.formatDate(this.date));
20896     },
20897         
20898     onRender: function(ct, position)
20899     {
20900         
20901         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20902         
20903         this.language = this.language || 'en';
20904         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20905         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20906         
20907         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20908         this.format = this.format || 'm/d/y';
20909         this.isInline = false;
20910         this.isInput = true;
20911         this.component = this.el.select('.add-on', true).first() || false;
20912         this.component = (this.component && this.component.length === 0) ? false : this.component;
20913         this.hasInput = this.component && this.inputEl().length;
20914         
20915         if (typeof(this.minViewMode === 'string')) {
20916             switch (this.minViewMode) {
20917                 case 'months':
20918                     this.minViewMode = 1;
20919                     break;
20920                 case 'years':
20921                     this.minViewMode = 2;
20922                     break;
20923                 default:
20924                     this.minViewMode = 0;
20925                     break;
20926             }
20927         }
20928         
20929         if (typeof(this.viewMode === 'string')) {
20930             switch (this.viewMode) {
20931                 case 'months':
20932                     this.viewMode = 1;
20933                     break;
20934                 case 'years':
20935                     this.viewMode = 2;
20936                     break;
20937                 default:
20938                     this.viewMode = 0;
20939                     break;
20940             }
20941         }
20942                 
20943         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20944         
20945 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20946         
20947         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20948         
20949         this.picker().on('mousedown', this.onMousedown, this);
20950         this.picker().on('click', this.onClick, this);
20951         
20952         this.picker().addClass('datepicker-dropdown');
20953         
20954         this.startViewMode = this.viewMode;
20955         
20956         if(this.singleMode){
20957             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20958                 v.setVisibilityMode(Roo.Element.DISPLAY);
20959                 v.hide();
20960             });
20961             
20962             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20963                 v.setStyle('width', '189px');
20964             });
20965         }
20966         
20967         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20968             if(!this.calendarWeeks){
20969                 v.remove();
20970                 return;
20971             }
20972             
20973             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20974             v.attr('colspan', function(i, val){
20975                 return parseInt(val) + 1;
20976             });
20977         });
20978                         
20979         
20980         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20981         
20982         this.setStartDate(this.startDate);
20983         this.setEndDate(this.endDate);
20984         
20985         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20986         
20987         this.fillDow();
20988         this.fillMonths();
20989         this.update();
20990         this.showMode();
20991         
20992         if(this.isInline) {
20993             this.showPopup();
20994         }
20995     },
20996     
20997     picker : function()
20998     {
20999         return this.pickerEl;
21000 //        return this.el.select('.datepicker', true).first();
21001     },
21002     
21003     fillDow: function()
21004     {
21005         var dowCnt = this.weekStart;
21006         
21007         var dow = {
21008             tag: 'tr',
21009             cn: [
21010                 
21011             ]
21012         };
21013         
21014         if(this.calendarWeeks){
21015             dow.cn.push({
21016                 tag: 'th',
21017                 cls: 'cw',
21018                 html: '&nbsp;'
21019             })
21020         }
21021         
21022         while (dowCnt < this.weekStart + 7) {
21023             dow.cn.push({
21024                 tag: 'th',
21025                 cls: 'dow',
21026                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21027             });
21028         }
21029         
21030         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21031     },
21032     
21033     fillMonths: function()
21034     {    
21035         var i = 0;
21036         var months = this.picker().select('>.datepicker-months td', true).first();
21037         
21038         months.dom.innerHTML = '';
21039         
21040         while (i < 12) {
21041             var month = {
21042                 tag: 'span',
21043                 cls: 'month',
21044                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21045             };
21046             
21047             months.createChild(month);
21048         }
21049         
21050     },
21051     
21052     update: function()
21053     {
21054         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;
21055         
21056         if (this.date < this.startDate) {
21057             this.viewDate = new Date(this.startDate);
21058         } else if (this.date > this.endDate) {
21059             this.viewDate = new Date(this.endDate);
21060         } else {
21061             this.viewDate = new Date(this.date);
21062         }
21063         
21064         this.fill();
21065     },
21066     
21067     fill: function() 
21068     {
21069         var d = new Date(this.viewDate),
21070                 year = d.getUTCFullYear(),
21071                 month = d.getUTCMonth(),
21072                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21073                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21074                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21075                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21076                 currentDate = this.date && this.date.valueOf(),
21077                 today = this.UTCToday();
21078         
21079         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21080         
21081 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21082         
21083 //        this.picker.select('>tfoot th.today').
21084 //                                              .text(dates[this.language].today)
21085 //                                              .toggle(this.todayBtn !== false);
21086     
21087         this.updateNavArrows();
21088         this.fillMonths();
21089                                                 
21090         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21091         
21092         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21093          
21094         prevMonth.setUTCDate(day);
21095         
21096         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21097         
21098         var nextMonth = new Date(prevMonth);
21099         
21100         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21101         
21102         nextMonth = nextMonth.valueOf();
21103         
21104         var fillMonths = false;
21105         
21106         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21107         
21108         while(prevMonth.valueOf() <= nextMonth) {
21109             var clsName = '';
21110             
21111             if (prevMonth.getUTCDay() === this.weekStart) {
21112                 if(fillMonths){
21113                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21114                 }
21115                     
21116                 fillMonths = {
21117                     tag: 'tr',
21118                     cn: []
21119                 };
21120                 
21121                 if(this.calendarWeeks){
21122                     // ISO 8601: First week contains first thursday.
21123                     // ISO also states week starts on Monday, but we can be more abstract here.
21124                     var
21125                     // Start of current week: based on weekstart/current date
21126                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21127                     // Thursday of this week
21128                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21129                     // First Thursday of year, year from thursday
21130                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21131                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21132                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21133                     
21134                     fillMonths.cn.push({
21135                         tag: 'td',
21136                         cls: 'cw',
21137                         html: calWeek
21138                     });
21139                 }
21140             }
21141             
21142             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21143                 clsName += ' old';
21144             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21145                 clsName += ' new';
21146             }
21147             if (this.todayHighlight &&
21148                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21149                 prevMonth.getUTCMonth() == today.getMonth() &&
21150                 prevMonth.getUTCDate() == today.getDate()) {
21151                 clsName += ' today';
21152             }
21153             
21154             if (currentDate && prevMonth.valueOf() === currentDate) {
21155                 clsName += ' active';
21156             }
21157             
21158             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21159                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21160                     clsName += ' disabled';
21161             }
21162             
21163             fillMonths.cn.push({
21164                 tag: 'td',
21165                 cls: 'day ' + clsName,
21166                 html: prevMonth.getDate()
21167             });
21168             
21169             prevMonth.setDate(prevMonth.getDate()+1);
21170         }
21171           
21172         var currentYear = this.date && this.date.getUTCFullYear();
21173         var currentMonth = this.date && this.date.getUTCMonth();
21174         
21175         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21176         
21177         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21178             v.removeClass('active');
21179             
21180             if(currentYear === year && k === currentMonth){
21181                 v.addClass('active');
21182             }
21183             
21184             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21185                 v.addClass('disabled');
21186             }
21187             
21188         });
21189         
21190         
21191         year = parseInt(year/10, 10) * 10;
21192         
21193         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21194         
21195         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21196         
21197         year -= 1;
21198         for (var i = -1; i < 11; i++) {
21199             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21200                 tag: 'span',
21201                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21202                 html: year
21203             });
21204             
21205             year += 1;
21206         }
21207     },
21208     
21209     showMode: function(dir) 
21210     {
21211         if (dir) {
21212             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21213         }
21214         
21215         Roo.each(this.picker().select('>div',true).elements, function(v){
21216             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21217             v.hide();
21218         });
21219         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21220     },
21221     
21222     place: function()
21223     {
21224         if(this.isInline) {
21225             return;
21226         }
21227         
21228         this.picker().removeClass(['bottom', 'top']);
21229         
21230         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21231             /*
21232              * place to the top of element!
21233              *
21234              */
21235             
21236             this.picker().addClass('top');
21237             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21238             
21239             return;
21240         }
21241         
21242         this.picker().addClass('bottom');
21243         
21244         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21245     },
21246     
21247     parseDate : function(value)
21248     {
21249         if(!value || value instanceof Date){
21250             return value;
21251         }
21252         var v = Date.parseDate(value, this.format);
21253         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21254             v = Date.parseDate(value, 'Y-m-d');
21255         }
21256         if(!v && this.altFormats){
21257             if(!this.altFormatsArray){
21258                 this.altFormatsArray = this.altFormats.split("|");
21259             }
21260             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21261                 v = Date.parseDate(value, this.altFormatsArray[i]);
21262             }
21263         }
21264         return v;
21265     },
21266     
21267     formatDate : function(date, fmt)
21268     {   
21269         return (!date || !(date instanceof Date)) ?
21270         date : date.dateFormat(fmt || this.format);
21271     },
21272     
21273     onFocus : function()
21274     {
21275         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21276         this.showPopup();
21277     },
21278     
21279     onBlur : function()
21280     {
21281         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21282         
21283         var d = this.inputEl().getValue();
21284         
21285         this.setValue(d);
21286                 
21287         this.hidePopup();
21288     },
21289     
21290     showPopup : function()
21291     {
21292         this.picker().show();
21293         this.update();
21294         this.place();
21295         
21296         this.fireEvent('showpopup', this, this.date);
21297     },
21298     
21299     hidePopup : function()
21300     {
21301         if(this.isInline) {
21302             return;
21303         }
21304         this.picker().hide();
21305         this.viewMode = this.startViewMode;
21306         this.showMode();
21307         
21308         this.fireEvent('hidepopup', this, this.date);
21309         
21310     },
21311     
21312     onMousedown: function(e)
21313     {
21314         e.stopPropagation();
21315         e.preventDefault();
21316     },
21317     
21318     keyup: function(e)
21319     {
21320         Roo.bootstrap.DateField.superclass.keyup.call(this);
21321         this.update();
21322     },
21323
21324     setValue: function(v)
21325     {
21326         if(this.fireEvent('beforeselect', this, v) !== false){
21327             var d = new Date(this.parseDate(v) ).clearTime();
21328         
21329             if(isNaN(d.getTime())){
21330                 this.date = this.viewDate = '';
21331                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21332                 return;
21333             }
21334
21335             v = this.formatDate(d);
21336
21337             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21338
21339             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21340
21341             this.update();
21342
21343             this.fireEvent('select', this, this.date);
21344         }
21345     },
21346     
21347     getValue: function()
21348     {
21349         return this.formatDate(this.date);
21350     },
21351     
21352     fireKey: function(e)
21353     {
21354         if (!this.picker().isVisible()){
21355             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21356                 this.showPopup();
21357             }
21358             return;
21359         }
21360         
21361         var dateChanged = false,
21362         dir, day, month,
21363         newDate, newViewDate;
21364         
21365         switch(e.keyCode){
21366             case 27: // escape
21367                 this.hidePopup();
21368                 e.preventDefault();
21369                 break;
21370             case 37: // left
21371             case 39: // right
21372                 if (!this.keyboardNavigation) {
21373                     break;
21374                 }
21375                 dir = e.keyCode == 37 ? -1 : 1;
21376                 
21377                 if (e.ctrlKey){
21378                     newDate = this.moveYear(this.date, dir);
21379                     newViewDate = this.moveYear(this.viewDate, dir);
21380                 } else if (e.shiftKey){
21381                     newDate = this.moveMonth(this.date, dir);
21382                     newViewDate = this.moveMonth(this.viewDate, dir);
21383                 } else {
21384                     newDate = new Date(this.date);
21385                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21386                     newViewDate = new Date(this.viewDate);
21387                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21388                 }
21389                 if (this.dateWithinRange(newDate)){
21390                     this.date = newDate;
21391                     this.viewDate = newViewDate;
21392                     this.setValue(this.formatDate(this.date));
21393 //                    this.update();
21394                     e.preventDefault();
21395                     dateChanged = true;
21396                 }
21397                 break;
21398             case 38: // up
21399             case 40: // down
21400                 if (!this.keyboardNavigation) {
21401                     break;
21402                 }
21403                 dir = e.keyCode == 38 ? -1 : 1;
21404                 if (e.ctrlKey){
21405                     newDate = this.moveYear(this.date, dir);
21406                     newViewDate = this.moveYear(this.viewDate, dir);
21407                 } else if (e.shiftKey){
21408                     newDate = this.moveMonth(this.date, dir);
21409                     newViewDate = this.moveMonth(this.viewDate, dir);
21410                 } else {
21411                     newDate = new Date(this.date);
21412                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21413                     newViewDate = new Date(this.viewDate);
21414                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21415                 }
21416                 if (this.dateWithinRange(newDate)){
21417                     this.date = newDate;
21418                     this.viewDate = newViewDate;
21419                     this.setValue(this.formatDate(this.date));
21420 //                    this.update();
21421                     e.preventDefault();
21422                     dateChanged = true;
21423                 }
21424                 break;
21425             case 13: // enter
21426                 this.setValue(this.formatDate(this.date));
21427                 this.hidePopup();
21428                 e.preventDefault();
21429                 break;
21430             case 9: // tab
21431                 this.setValue(this.formatDate(this.date));
21432                 this.hidePopup();
21433                 break;
21434             case 16: // shift
21435             case 17: // ctrl
21436             case 18: // alt
21437                 break;
21438             default :
21439                 this.hidePopup();
21440                 
21441         }
21442     },
21443     
21444     
21445     onClick: function(e) 
21446     {
21447         e.stopPropagation();
21448         e.preventDefault();
21449         
21450         var target = e.getTarget();
21451         
21452         if(target.nodeName.toLowerCase() === 'i'){
21453             target = Roo.get(target).dom.parentNode;
21454         }
21455         
21456         var nodeName = target.nodeName;
21457         var className = target.className;
21458         var html = target.innerHTML;
21459         //Roo.log(nodeName);
21460         
21461         switch(nodeName.toLowerCase()) {
21462             case 'th':
21463                 switch(className) {
21464                     case 'switch':
21465                         this.showMode(1);
21466                         break;
21467                     case 'prev':
21468                     case 'next':
21469                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21470                         switch(this.viewMode){
21471                                 case 0:
21472                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21473                                         break;
21474                                 case 1:
21475                                 case 2:
21476                                         this.viewDate = this.moveYear(this.viewDate, dir);
21477                                         break;
21478                         }
21479                         this.fill();
21480                         break;
21481                     case 'today':
21482                         var date = new Date();
21483                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21484 //                        this.fill()
21485                         this.setValue(this.formatDate(this.date));
21486                         
21487                         this.hidePopup();
21488                         break;
21489                 }
21490                 break;
21491             case 'span':
21492                 if (className.indexOf('disabled') < 0) {
21493                     this.viewDate.setUTCDate(1);
21494                     if (className.indexOf('month') > -1) {
21495                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21496                     } else {
21497                         var year = parseInt(html, 10) || 0;
21498                         this.viewDate.setUTCFullYear(year);
21499                         
21500                     }
21501                     
21502                     if(this.singleMode){
21503                         this.setValue(this.formatDate(this.viewDate));
21504                         this.hidePopup();
21505                         return;
21506                     }
21507                     
21508                     this.showMode(-1);
21509                     this.fill();
21510                 }
21511                 break;
21512                 
21513             case 'td':
21514                 //Roo.log(className);
21515                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21516                     var day = parseInt(html, 10) || 1;
21517                     var year = this.viewDate.getUTCFullYear(),
21518                         month = this.viewDate.getUTCMonth();
21519
21520                     if (className.indexOf('old') > -1) {
21521                         if(month === 0 ){
21522                             month = 11;
21523                             year -= 1;
21524                         }else{
21525                             month -= 1;
21526                         }
21527                     } else if (className.indexOf('new') > -1) {
21528                         if (month == 11) {
21529                             month = 0;
21530                             year += 1;
21531                         } else {
21532                             month += 1;
21533                         }
21534                     }
21535                     //Roo.log([year,month,day]);
21536                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21537                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21538 //                    this.fill();
21539                     //Roo.log(this.formatDate(this.date));
21540                     this.setValue(this.formatDate(this.date));
21541                     this.hidePopup();
21542                 }
21543                 break;
21544         }
21545     },
21546     
21547     setStartDate: function(startDate)
21548     {
21549         this.startDate = startDate || -Infinity;
21550         if (this.startDate !== -Infinity) {
21551             this.startDate = this.parseDate(this.startDate);
21552         }
21553         this.update();
21554         this.updateNavArrows();
21555     },
21556
21557     setEndDate: function(endDate)
21558     {
21559         this.endDate = endDate || Infinity;
21560         if (this.endDate !== Infinity) {
21561             this.endDate = this.parseDate(this.endDate);
21562         }
21563         this.update();
21564         this.updateNavArrows();
21565     },
21566     
21567     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21568     {
21569         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21570         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21571             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21572         }
21573         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21574             return parseInt(d, 10);
21575         });
21576         this.update();
21577         this.updateNavArrows();
21578     },
21579     
21580     updateNavArrows: function() 
21581     {
21582         if(this.singleMode){
21583             return;
21584         }
21585         
21586         var d = new Date(this.viewDate),
21587         year = d.getUTCFullYear(),
21588         month = d.getUTCMonth();
21589         
21590         Roo.each(this.picker().select('.prev', true).elements, function(v){
21591             v.show();
21592             switch (this.viewMode) {
21593                 case 0:
21594
21595                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21596                         v.hide();
21597                     }
21598                     break;
21599                 case 1:
21600                 case 2:
21601                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21602                         v.hide();
21603                     }
21604                     break;
21605             }
21606         });
21607         
21608         Roo.each(this.picker().select('.next', true).elements, function(v){
21609             v.show();
21610             switch (this.viewMode) {
21611                 case 0:
21612
21613                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21614                         v.hide();
21615                     }
21616                     break;
21617                 case 1:
21618                 case 2:
21619                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21620                         v.hide();
21621                     }
21622                     break;
21623             }
21624         })
21625     },
21626     
21627     moveMonth: function(date, dir)
21628     {
21629         if (!dir) {
21630             return date;
21631         }
21632         var new_date = new Date(date.valueOf()),
21633         day = new_date.getUTCDate(),
21634         month = new_date.getUTCMonth(),
21635         mag = Math.abs(dir),
21636         new_month, test;
21637         dir = dir > 0 ? 1 : -1;
21638         if (mag == 1){
21639             test = dir == -1
21640             // If going back one month, make sure month is not current month
21641             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21642             ? function(){
21643                 return new_date.getUTCMonth() == month;
21644             }
21645             // If going forward one month, make sure month is as expected
21646             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21647             : function(){
21648                 return new_date.getUTCMonth() != new_month;
21649             };
21650             new_month = month + dir;
21651             new_date.setUTCMonth(new_month);
21652             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21653             if (new_month < 0 || new_month > 11) {
21654                 new_month = (new_month + 12) % 12;
21655             }
21656         } else {
21657             // For magnitudes >1, move one month at a time...
21658             for (var i=0; i<mag; i++) {
21659                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21660                 new_date = this.moveMonth(new_date, dir);
21661             }
21662             // ...then reset the day, keeping it in the new month
21663             new_month = new_date.getUTCMonth();
21664             new_date.setUTCDate(day);
21665             test = function(){
21666                 return new_month != new_date.getUTCMonth();
21667             };
21668         }
21669         // Common date-resetting loop -- if date is beyond end of month, make it
21670         // end of month
21671         while (test()){
21672             new_date.setUTCDate(--day);
21673             new_date.setUTCMonth(new_month);
21674         }
21675         return new_date;
21676     },
21677
21678     moveYear: function(date, dir)
21679     {
21680         return this.moveMonth(date, dir*12);
21681     },
21682
21683     dateWithinRange: function(date)
21684     {
21685         return date >= this.startDate && date <= this.endDate;
21686     },
21687
21688     
21689     remove: function() 
21690     {
21691         this.picker().remove();
21692     },
21693     
21694     validateValue : function(value)
21695     {
21696         if(this.getVisibilityEl().hasClass('hidden')){
21697             return true;
21698         }
21699         
21700         if(value.length < 1)  {
21701             if(this.allowBlank){
21702                 return true;
21703             }
21704             return false;
21705         }
21706         
21707         if(value.length < this.minLength){
21708             return false;
21709         }
21710         if(value.length > this.maxLength){
21711             return false;
21712         }
21713         if(this.vtype){
21714             var vt = Roo.form.VTypes;
21715             if(!vt[this.vtype](value, this)){
21716                 return false;
21717             }
21718         }
21719         if(typeof this.validator == "function"){
21720             var msg = this.validator(value);
21721             if(msg !== true){
21722                 return false;
21723             }
21724         }
21725         
21726         if(this.regex && !this.regex.test(value)){
21727             return false;
21728         }
21729         
21730         if(typeof(this.parseDate(value)) == 'undefined'){
21731             return false;
21732         }
21733         
21734         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21735             return false;
21736         }      
21737         
21738         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21739             return false;
21740         } 
21741         
21742         
21743         return true;
21744     },
21745     
21746     reset : function()
21747     {
21748         this.date = this.viewDate = '';
21749         
21750         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21751     }
21752    
21753 });
21754
21755 Roo.apply(Roo.bootstrap.DateField,  {
21756     
21757     head : {
21758         tag: 'thead',
21759         cn: [
21760         {
21761             tag: 'tr',
21762             cn: [
21763             {
21764                 tag: 'th',
21765                 cls: 'prev',
21766                 html: '<i class="fa fa-arrow-left"/>'
21767             },
21768             {
21769                 tag: 'th',
21770                 cls: 'switch',
21771                 colspan: '5'
21772             },
21773             {
21774                 tag: 'th',
21775                 cls: 'next',
21776                 html: '<i class="fa fa-arrow-right"/>'
21777             }
21778
21779             ]
21780         }
21781         ]
21782     },
21783     
21784     content : {
21785         tag: 'tbody',
21786         cn: [
21787         {
21788             tag: 'tr',
21789             cn: [
21790             {
21791                 tag: 'td',
21792                 colspan: '7'
21793             }
21794             ]
21795         }
21796         ]
21797     },
21798     
21799     footer : {
21800         tag: 'tfoot',
21801         cn: [
21802         {
21803             tag: 'tr',
21804             cn: [
21805             {
21806                 tag: 'th',
21807                 colspan: '7',
21808                 cls: 'today'
21809             }
21810                     
21811             ]
21812         }
21813         ]
21814     },
21815     
21816     dates:{
21817         en: {
21818             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21819             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21820             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21821             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21822             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21823             today: "Today"
21824         }
21825     },
21826     
21827     modes: [
21828     {
21829         clsName: 'days',
21830         navFnc: 'Month',
21831         navStep: 1
21832     },
21833     {
21834         clsName: 'months',
21835         navFnc: 'FullYear',
21836         navStep: 1
21837     },
21838     {
21839         clsName: 'years',
21840         navFnc: 'FullYear',
21841         navStep: 10
21842     }]
21843 });
21844
21845 Roo.apply(Roo.bootstrap.DateField,  {
21846   
21847     template : {
21848         tag: 'div',
21849         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21850         cn: [
21851         {
21852             tag: 'div',
21853             cls: 'datepicker-days',
21854             cn: [
21855             {
21856                 tag: 'table',
21857                 cls: 'table-condensed',
21858                 cn:[
21859                 Roo.bootstrap.DateField.head,
21860                 {
21861                     tag: 'tbody'
21862                 },
21863                 Roo.bootstrap.DateField.footer
21864                 ]
21865             }
21866             ]
21867         },
21868         {
21869             tag: 'div',
21870             cls: 'datepicker-months',
21871             cn: [
21872             {
21873                 tag: 'table',
21874                 cls: 'table-condensed',
21875                 cn:[
21876                 Roo.bootstrap.DateField.head,
21877                 Roo.bootstrap.DateField.content,
21878                 Roo.bootstrap.DateField.footer
21879                 ]
21880             }
21881             ]
21882         },
21883         {
21884             tag: 'div',
21885             cls: 'datepicker-years',
21886             cn: [
21887             {
21888                 tag: 'table',
21889                 cls: 'table-condensed',
21890                 cn:[
21891                 Roo.bootstrap.DateField.head,
21892                 Roo.bootstrap.DateField.content,
21893                 Roo.bootstrap.DateField.footer
21894                 ]
21895             }
21896             ]
21897         }
21898         ]
21899     }
21900 });
21901
21902  
21903
21904  /*
21905  * - LGPL
21906  *
21907  * TimeField
21908  * 
21909  */
21910
21911 /**
21912  * @class Roo.bootstrap.TimeField
21913  * @extends Roo.bootstrap.Input
21914  * Bootstrap DateField class
21915  * 
21916  * 
21917  * @constructor
21918  * Create a new TimeField
21919  * @param {Object} config The config object
21920  */
21921
21922 Roo.bootstrap.TimeField = function(config){
21923     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21924     this.addEvents({
21925             /**
21926              * @event show
21927              * Fires when this field show.
21928              * @param {Roo.bootstrap.DateField} thisthis
21929              * @param {Mixed} date The date value
21930              */
21931             show : true,
21932             /**
21933              * @event show
21934              * Fires when this field hide.
21935              * @param {Roo.bootstrap.DateField} this
21936              * @param {Mixed} date The date value
21937              */
21938             hide : true,
21939             /**
21940              * @event select
21941              * Fires when select a date.
21942              * @param {Roo.bootstrap.DateField} this
21943              * @param {Mixed} date The date value
21944              */
21945             select : true
21946         });
21947 };
21948
21949 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21950     
21951     /**
21952      * @cfg {String} format
21953      * The default time format string which can be overriden for localization support.  The format must be
21954      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21955      */
21956     format : "H:i",
21957        
21958     onRender: function(ct, position)
21959     {
21960         
21961         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21962                 
21963         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21964         
21965         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21966         
21967         this.pop = this.picker().select('>.datepicker-time',true).first();
21968         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21969         
21970         this.picker().on('mousedown', this.onMousedown, this);
21971         this.picker().on('click', this.onClick, this);
21972         
21973         this.picker().addClass('datepicker-dropdown');
21974     
21975         this.fillTime();
21976         this.update();
21977             
21978         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21979         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21980         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21981         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21982         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21983         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21984
21985     },
21986     
21987     fireKey: function(e){
21988         if (!this.picker().isVisible()){
21989             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21990                 this.show();
21991             }
21992             return;
21993         }
21994
21995         e.preventDefault();
21996         
21997         switch(e.keyCode){
21998             case 27: // escape
21999                 this.hide();
22000                 break;
22001             case 37: // left
22002             case 39: // right
22003                 this.onTogglePeriod();
22004                 break;
22005             case 38: // up
22006                 this.onIncrementMinutes();
22007                 break;
22008             case 40: // down
22009                 this.onDecrementMinutes();
22010                 break;
22011             case 13: // enter
22012             case 9: // tab
22013                 this.setTime();
22014                 break;
22015         }
22016     },
22017     
22018     onClick: function(e) {
22019         e.stopPropagation();
22020         e.preventDefault();
22021     },
22022     
22023     picker : function()
22024     {
22025         return this.el.select('.datepicker', true).first();
22026     },
22027     
22028     fillTime: function()
22029     {    
22030         var time = this.pop.select('tbody', true).first();
22031         
22032         time.dom.innerHTML = '';
22033         
22034         time.createChild({
22035             tag: 'tr',
22036             cn: [
22037                 {
22038                     tag: 'td',
22039                     cn: [
22040                         {
22041                             tag: 'a',
22042                             href: '#',
22043                             cls: 'btn',
22044                             cn: [
22045                                 {
22046                                     tag: 'span',
22047                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
22048                                 }
22049                             ]
22050                         } 
22051                     ]
22052                 },
22053                 {
22054                     tag: 'td',
22055                     cls: 'separator'
22056                 },
22057                 {
22058                     tag: 'td',
22059                     cn: [
22060                         {
22061                             tag: 'a',
22062                             href: '#',
22063                             cls: 'btn',
22064                             cn: [
22065                                 {
22066                                     tag: 'span',
22067                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22068                                 }
22069                             ]
22070                         }
22071                     ]
22072                 },
22073                 {
22074                     tag: 'td',
22075                     cls: 'separator'
22076                 }
22077             ]
22078         });
22079         
22080         time.createChild({
22081             tag: 'tr',
22082             cn: [
22083                 {
22084                     tag: 'td',
22085                     cn: [
22086                         {
22087                             tag: 'span',
22088                             cls: 'timepicker-hour',
22089                             html: '00'
22090                         }  
22091                     ]
22092                 },
22093                 {
22094                     tag: 'td',
22095                     cls: 'separator',
22096                     html: ':'
22097                 },
22098                 {
22099                     tag: 'td',
22100                     cn: [
22101                         {
22102                             tag: 'span',
22103                             cls: 'timepicker-minute',
22104                             html: '00'
22105                         }  
22106                     ]
22107                 },
22108                 {
22109                     tag: 'td',
22110                     cls: 'separator'
22111                 },
22112                 {
22113                     tag: 'td',
22114                     cn: [
22115                         {
22116                             tag: 'button',
22117                             type: 'button',
22118                             cls: 'btn btn-primary period',
22119                             html: 'AM'
22120                             
22121                         }
22122                     ]
22123                 }
22124             ]
22125         });
22126         
22127         time.createChild({
22128             tag: 'tr',
22129             cn: [
22130                 {
22131                     tag: 'td',
22132                     cn: [
22133                         {
22134                             tag: 'a',
22135                             href: '#',
22136                             cls: 'btn',
22137                             cn: [
22138                                 {
22139                                     tag: 'span',
22140                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22141                                 }
22142                             ]
22143                         }
22144                     ]
22145                 },
22146                 {
22147                     tag: 'td',
22148                     cls: 'separator'
22149                 },
22150                 {
22151                     tag: 'td',
22152                     cn: [
22153                         {
22154                             tag: 'a',
22155                             href: '#',
22156                             cls: 'btn',
22157                             cn: [
22158                                 {
22159                                     tag: 'span',
22160                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22161                                 }
22162                             ]
22163                         }
22164                     ]
22165                 },
22166                 {
22167                     tag: 'td',
22168                     cls: 'separator'
22169                 }
22170             ]
22171         });
22172         
22173     },
22174     
22175     update: function()
22176     {
22177         
22178         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22179         
22180         this.fill();
22181     },
22182     
22183     fill: function() 
22184     {
22185         var hours = this.time.getHours();
22186         var minutes = this.time.getMinutes();
22187         var period = 'AM';
22188         
22189         if(hours > 11){
22190             period = 'PM';
22191         }
22192         
22193         if(hours == 0){
22194             hours = 12;
22195         }
22196         
22197         
22198         if(hours > 12){
22199             hours = hours - 12;
22200         }
22201         
22202         if(hours < 10){
22203             hours = '0' + hours;
22204         }
22205         
22206         if(minutes < 10){
22207             minutes = '0' + minutes;
22208         }
22209         
22210         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22211         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22212         this.pop.select('button', true).first().dom.innerHTML = period;
22213         
22214     },
22215     
22216     place: function()
22217     {   
22218         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22219         
22220         var cls = ['bottom'];
22221         
22222         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22223             cls.pop();
22224             cls.push('top');
22225         }
22226         
22227         cls.push('right');
22228         
22229         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22230             cls.pop();
22231             cls.push('left');
22232         }
22233         
22234         this.picker().addClass(cls.join('-'));
22235         
22236         var _this = this;
22237         
22238         Roo.each(cls, function(c){
22239             if(c == 'bottom'){
22240                 _this.picker().setTop(_this.inputEl().getHeight());
22241                 return;
22242             }
22243             if(c == 'top'){
22244                 _this.picker().setTop(0 - _this.picker().getHeight());
22245                 return;
22246             }
22247             
22248             if(c == 'left'){
22249                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22250                 return;
22251             }
22252             if(c == 'right'){
22253                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22254                 return;
22255             }
22256         });
22257         
22258     },
22259   
22260     onFocus : function()
22261     {
22262         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22263         this.show();
22264     },
22265     
22266     onBlur : function()
22267     {
22268         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22269         this.hide();
22270     },
22271     
22272     show : function()
22273     {
22274         this.picker().show();
22275         this.pop.show();
22276         this.update();
22277         this.place();
22278         
22279         this.fireEvent('show', this, this.date);
22280     },
22281     
22282     hide : function()
22283     {
22284         this.picker().hide();
22285         this.pop.hide();
22286         
22287         this.fireEvent('hide', this, this.date);
22288     },
22289     
22290     setTime : function()
22291     {
22292         this.hide();
22293         this.setValue(this.time.format(this.format));
22294         
22295         this.fireEvent('select', this, this.date);
22296         
22297         
22298     },
22299     
22300     onMousedown: function(e){
22301         e.stopPropagation();
22302         e.preventDefault();
22303     },
22304     
22305     onIncrementHours: function()
22306     {
22307         Roo.log('onIncrementHours');
22308         this.time = this.time.add(Date.HOUR, 1);
22309         this.update();
22310         
22311     },
22312     
22313     onDecrementHours: function()
22314     {
22315         Roo.log('onDecrementHours');
22316         this.time = this.time.add(Date.HOUR, -1);
22317         this.update();
22318     },
22319     
22320     onIncrementMinutes: function()
22321     {
22322         Roo.log('onIncrementMinutes');
22323         this.time = this.time.add(Date.MINUTE, 1);
22324         this.update();
22325     },
22326     
22327     onDecrementMinutes: function()
22328     {
22329         Roo.log('onDecrementMinutes');
22330         this.time = this.time.add(Date.MINUTE, -1);
22331         this.update();
22332     },
22333     
22334     onTogglePeriod: function()
22335     {
22336         Roo.log('onTogglePeriod');
22337         this.time = this.time.add(Date.HOUR, 12);
22338         this.update();
22339     }
22340     
22341    
22342 });
22343
22344 Roo.apply(Roo.bootstrap.TimeField,  {
22345     
22346     content : {
22347         tag: 'tbody',
22348         cn: [
22349             {
22350                 tag: 'tr',
22351                 cn: [
22352                 {
22353                     tag: 'td',
22354                     colspan: '7'
22355                 }
22356                 ]
22357             }
22358         ]
22359     },
22360     
22361     footer : {
22362         tag: 'tfoot',
22363         cn: [
22364             {
22365                 tag: 'tr',
22366                 cn: [
22367                 {
22368                     tag: 'th',
22369                     colspan: '7',
22370                     cls: '',
22371                     cn: [
22372                         {
22373                             tag: 'button',
22374                             cls: 'btn btn-info ok',
22375                             html: 'OK'
22376                         }
22377                     ]
22378                 }
22379
22380                 ]
22381             }
22382         ]
22383     }
22384 });
22385
22386 Roo.apply(Roo.bootstrap.TimeField,  {
22387   
22388     template : {
22389         tag: 'div',
22390         cls: 'datepicker dropdown-menu',
22391         cn: [
22392             {
22393                 tag: 'div',
22394                 cls: 'datepicker-time',
22395                 cn: [
22396                 {
22397                     tag: 'table',
22398                     cls: 'table-condensed',
22399                     cn:[
22400                     Roo.bootstrap.TimeField.content,
22401                     Roo.bootstrap.TimeField.footer
22402                     ]
22403                 }
22404                 ]
22405             }
22406         ]
22407     }
22408 });
22409
22410  
22411
22412  /*
22413  * - LGPL
22414  *
22415  * MonthField
22416  * 
22417  */
22418
22419 /**
22420  * @class Roo.bootstrap.MonthField
22421  * @extends Roo.bootstrap.Input
22422  * Bootstrap MonthField class
22423  * 
22424  * @cfg {String} language default en
22425  * 
22426  * @constructor
22427  * Create a new MonthField
22428  * @param {Object} config The config object
22429  */
22430
22431 Roo.bootstrap.MonthField = function(config){
22432     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22433     
22434     this.addEvents({
22435         /**
22436          * @event show
22437          * Fires when this field show.
22438          * @param {Roo.bootstrap.MonthField} this
22439          * @param {Mixed} date The date value
22440          */
22441         show : true,
22442         /**
22443          * @event show
22444          * Fires when this field hide.
22445          * @param {Roo.bootstrap.MonthField} this
22446          * @param {Mixed} date The date value
22447          */
22448         hide : true,
22449         /**
22450          * @event select
22451          * Fires when select a date.
22452          * @param {Roo.bootstrap.MonthField} this
22453          * @param {String} oldvalue The old value
22454          * @param {String} newvalue The new value
22455          */
22456         select : true
22457     });
22458 };
22459
22460 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22461     
22462     onRender: function(ct, position)
22463     {
22464         
22465         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22466         
22467         this.language = this.language || 'en';
22468         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22469         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22470         
22471         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22472         this.isInline = false;
22473         this.isInput = true;
22474         this.component = this.el.select('.add-on', true).first() || false;
22475         this.component = (this.component && this.component.length === 0) ? false : this.component;
22476         this.hasInput = this.component && this.inputEL().length;
22477         
22478         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22479         
22480         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22481         
22482         this.picker().on('mousedown', this.onMousedown, this);
22483         this.picker().on('click', this.onClick, this);
22484         
22485         this.picker().addClass('datepicker-dropdown');
22486         
22487         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22488             v.setStyle('width', '189px');
22489         });
22490         
22491         this.fillMonths();
22492         
22493         this.update();
22494         
22495         if(this.isInline) {
22496             this.show();
22497         }
22498         
22499     },
22500     
22501     setValue: function(v, suppressEvent)
22502     {   
22503         var o = this.getValue();
22504         
22505         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22506         
22507         this.update();
22508
22509         if(suppressEvent !== true){
22510             this.fireEvent('select', this, o, v);
22511         }
22512         
22513     },
22514     
22515     getValue: function()
22516     {
22517         return this.value;
22518     },
22519     
22520     onClick: function(e) 
22521     {
22522         e.stopPropagation();
22523         e.preventDefault();
22524         
22525         var target = e.getTarget();
22526         
22527         if(target.nodeName.toLowerCase() === 'i'){
22528             target = Roo.get(target).dom.parentNode;
22529         }
22530         
22531         var nodeName = target.nodeName;
22532         var className = target.className;
22533         var html = target.innerHTML;
22534         
22535         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22536             return;
22537         }
22538         
22539         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22540         
22541         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22542         
22543         this.hide();
22544                         
22545     },
22546     
22547     picker : function()
22548     {
22549         return this.pickerEl;
22550     },
22551     
22552     fillMonths: function()
22553     {    
22554         var i = 0;
22555         var months = this.picker().select('>.datepicker-months td', true).first();
22556         
22557         months.dom.innerHTML = '';
22558         
22559         while (i < 12) {
22560             var month = {
22561                 tag: 'span',
22562                 cls: 'month',
22563                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22564             };
22565             
22566             months.createChild(month);
22567         }
22568         
22569     },
22570     
22571     update: function()
22572     {
22573         var _this = this;
22574         
22575         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22576             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22577         }
22578         
22579         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22580             e.removeClass('active');
22581             
22582             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22583                 e.addClass('active');
22584             }
22585         })
22586     },
22587     
22588     place: function()
22589     {
22590         if(this.isInline) {
22591             return;
22592         }
22593         
22594         this.picker().removeClass(['bottom', 'top']);
22595         
22596         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22597             /*
22598              * place to the top of element!
22599              *
22600              */
22601             
22602             this.picker().addClass('top');
22603             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22604             
22605             return;
22606         }
22607         
22608         this.picker().addClass('bottom');
22609         
22610         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22611     },
22612     
22613     onFocus : function()
22614     {
22615         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22616         this.show();
22617     },
22618     
22619     onBlur : function()
22620     {
22621         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22622         
22623         var d = this.inputEl().getValue();
22624         
22625         this.setValue(d);
22626                 
22627         this.hide();
22628     },
22629     
22630     show : function()
22631     {
22632         this.picker().show();
22633         this.picker().select('>.datepicker-months', true).first().show();
22634         this.update();
22635         this.place();
22636         
22637         this.fireEvent('show', this, this.date);
22638     },
22639     
22640     hide : function()
22641     {
22642         if(this.isInline) {
22643             return;
22644         }
22645         this.picker().hide();
22646         this.fireEvent('hide', this, this.date);
22647         
22648     },
22649     
22650     onMousedown: function(e)
22651     {
22652         e.stopPropagation();
22653         e.preventDefault();
22654     },
22655     
22656     keyup: function(e)
22657     {
22658         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22659         this.update();
22660     },
22661
22662     fireKey: function(e)
22663     {
22664         if (!this.picker().isVisible()){
22665             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22666                 this.show();
22667             }
22668             return;
22669         }
22670         
22671         var dir;
22672         
22673         switch(e.keyCode){
22674             case 27: // escape
22675                 this.hide();
22676                 e.preventDefault();
22677                 break;
22678             case 37: // left
22679             case 39: // right
22680                 dir = e.keyCode == 37 ? -1 : 1;
22681                 
22682                 this.vIndex = this.vIndex + dir;
22683                 
22684                 if(this.vIndex < 0){
22685                     this.vIndex = 0;
22686                 }
22687                 
22688                 if(this.vIndex > 11){
22689                     this.vIndex = 11;
22690                 }
22691                 
22692                 if(isNaN(this.vIndex)){
22693                     this.vIndex = 0;
22694                 }
22695                 
22696                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22697                 
22698                 break;
22699             case 38: // up
22700             case 40: // down
22701                 
22702                 dir = e.keyCode == 38 ? -1 : 1;
22703                 
22704                 this.vIndex = this.vIndex + dir * 4;
22705                 
22706                 if(this.vIndex < 0){
22707                     this.vIndex = 0;
22708                 }
22709                 
22710                 if(this.vIndex > 11){
22711                     this.vIndex = 11;
22712                 }
22713                 
22714                 if(isNaN(this.vIndex)){
22715                     this.vIndex = 0;
22716                 }
22717                 
22718                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22719                 break;
22720                 
22721             case 13: // enter
22722                 
22723                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22724                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22725                 }
22726                 
22727                 this.hide();
22728                 e.preventDefault();
22729                 break;
22730             case 9: // tab
22731                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22732                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22733                 }
22734                 this.hide();
22735                 break;
22736             case 16: // shift
22737             case 17: // ctrl
22738             case 18: // alt
22739                 break;
22740             default :
22741                 this.hide();
22742                 
22743         }
22744     },
22745     
22746     remove: function() 
22747     {
22748         this.picker().remove();
22749     }
22750    
22751 });
22752
22753 Roo.apply(Roo.bootstrap.MonthField,  {
22754     
22755     content : {
22756         tag: 'tbody',
22757         cn: [
22758         {
22759             tag: 'tr',
22760             cn: [
22761             {
22762                 tag: 'td',
22763                 colspan: '7'
22764             }
22765             ]
22766         }
22767         ]
22768     },
22769     
22770     dates:{
22771         en: {
22772             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22773             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22774         }
22775     }
22776 });
22777
22778 Roo.apply(Roo.bootstrap.MonthField,  {
22779   
22780     template : {
22781         tag: 'div',
22782         cls: 'datepicker dropdown-menu roo-dynamic',
22783         cn: [
22784             {
22785                 tag: 'div',
22786                 cls: 'datepicker-months',
22787                 cn: [
22788                 {
22789                     tag: 'table',
22790                     cls: 'table-condensed',
22791                     cn:[
22792                         Roo.bootstrap.DateField.content
22793                     ]
22794                 }
22795                 ]
22796             }
22797         ]
22798     }
22799 });
22800
22801  
22802
22803  
22804  /*
22805  * - LGPL
22806  *
22807  * CheckBox
22808  * 
22809  */
22810
22811 /**
22812  * @class Roo.bootstrap.CheckBox
22813  * @extends Roo.bootstrap.Input
22814  * Bootstrap CheckBox class
22815  * 
22816  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22817  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22818  * @cfg {String} boxLabel The text that appears beside the checkbox
22819  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22820  * @cfg {Boolean} checked initnal the element
22821  * @cfg {Boolean} inline inline the element (default false)
22822  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22823  * @cfg {String} tooltip label tooltip
22824  * 
22825  * @constructor
22826  * Create a new CheckBox
22827  * @param {Object} config The config object
22828  */
22829
22830 Roo.bootstrap.CheckBox = function(config){
22831     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22832    
22833     this.addEvents({
22834         /**
22835         * @event check
22836         * Fires when the element is checked or unchecked.
22837         * @param {Roo.bootstrap.CheckBox} this This input
22838         * @param {Boolean} checked The new checked value
22839         */
22840        check : true,
22841        /**
22842         * @event click
22843         * Fires when the element is click.
22844         * @param {Roo.bootstrap.CheckBox} this This input
22845         */
22846        click : true
22847     });
22848     
22849 };
22850
22851 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22852   
22853     inputType: 'checkbox',
22854     inputValue: 1,
22855     valueOff: 0,
22856     boxLabel: false,
22857     checked: false,
22858     weight : false,
22859     inline: false,
22860     tooltip : '',
22861     
22862     // checkbox success does not make any sense really.. 
22863     invalidClass : "",
22864     validClass : "",
22865     
22866     
22867     getAutoCreate : function()
22868     {
22869         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22870         
22871         var id = Roo.id();
22872         
22873         var cfg = {};
22874         
22875         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22876         
22877         if(this.inline){
22878             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22879         }
22880         
22881         var input =  {
22882             tag: 'input',
22883             id : id,
22884             type : this.inputType,
22885             value : this.inputValue,
22886             cls : 'roo-' + this.inputType, //'form-box',
22887             placeholder : this.placeholder || ''
22888             
22889         };
22890         
22891         if(this.inputType != 'radio'){
22892             var hidden =  {
22893                 tag: 'input',
22894                 type : 'hidden',
22895                 cls : 'roo-hidden-value',
22896                 value : this.checked ? this.inputValue : this.valueOff
22897             };
22898         }
22899         
22900             
22901         if (this.weight) { // Validity check?
22902             cfg.cls += " " + this.inputType + "-" + this.weight;
22903         }
22904         
22905         if (this.disabled) {
22906             input.disabled=true;
22907         }
22908         
22909         if(this.checked){
22910             input.checked = this.checked;
22911         }
22912         
22913         if (this.name) {
22914             
22915             input.name = this.name;
22916             
22917             if(this.inputType != 'radio'){
22918                 hidden.name = this.name;
22919                 input.name = '_hidden_' + this.name;
22920             }
22921         }
22922         
22923         if (this.size) {
22924             input.cls += ' input-' + this.size;
22925         }
22926         
22927         var settings=this;
22928         
22929         ['xs','sm','md','lg'].map(function(size){
22930             if (settings[size]) {
22931                 cfg.cls += ' col-' + size + '-' + settings[size];
22932             }
22933         });
22934         
22935         var inputblock = input;
22936          
22937         if (this.before || this.after) {
22938             
22939             inputblock = {
22940                 cls : 'input-group',
22941                 cn :  [] 
22942             };
22943             
22944             if (this.before) {
22945                 inputblock.cn.push({
22946                     tag :'span',
22947                     cls : 'input-group-addon',
22948                     html : this.before
22949                 });
22950             }
22951             
22952             inputblock.cn.push(input);
22953             
22954             if(this.inputType != 'radio'){
22955                 inputblock.cn.push(hidden);
22956             }
22957             
22958             if (this.after) {
22959                 inputblock.cn.push({
22960                     tag :'span',
22961                     cls : 'input-group-addon',
22962                     html : this.after
22963                 });
22964             }
22965             
22966         }
22967         var boxLabelCfg = false;
22968         
22969         if(this.boxLabel){
22970            
22971             boxLabelCfg = {
22972                 tag: 'label',
22973                 //'for': id, // box label is handled by onclick - so no for...
22974                 cls: 'box-label',
22975                 html: this.boxLabel
22976             };
22977             if(this.tooltip){
22978                 boxLabelCfg.tooltip = this.tooltip;
22979             }
22980              
22981         }
22982         
22983         
22984         if (align ==='left' && this.fieldLabel.length) {
22985 //                Roo.log("left and has label");
22986             cfg.cn = [
22987                 {
22988                     tag: 'label',
22989                     'for' :  id,
22990                     cls : 'control-label',
22991                     html : this.fieldLabel
22992                 },
22993                 {
22994                     cls : "", 
22995                     cn: [
22996                         inputblock
22997                     ]
22998                 }
22999             ];
23000             
23001             if (boxLabelCfg) {
23002                 cfg.cn[1].cn.push(boxLabelCfg);
23003             }
23004             
23005             if(this.labelWidth > 12){
23006                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23007             }
23008             
23009             if(this.labelWidth < 13 && this.labelmd == 0){
23010                 this.labelmd = this.labelWidth;
23011             }
23012             
23013             if(this.labellg > 0){
23014                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23015                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23016             }
23017             
23018             if(this.labelmd > 0){
23019                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23020                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23021             }
23022             
23023             if(this.labelsm > 0){
23024                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23025                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23026             }
23027             
23028             if(this.labelxs > 0){
23029                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23030                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23031             }
23032             
23033         } else if ( this.fieldLabel.length) {
23034 //                Roo.log(" label");
23035                 cfg.cn = [
23036                    
23037                     {
23038                         tag: this.boxLabel ? 'span' : 'label',
23039                         'for': id,
23040                         cls: 'control-label box-input-label',
23041                         //cls : 'input-group-addon',
23042                         html : this.fieldLabel
23043                     },
23044                     
23045                     inputblock
23046                     
23047                 ];
23048                 if (boxLabelCfg) {
23049                     cfg.cn.push(boxLabelCfg);
23050                 }
23051
23052         } else {
23053             
23054 //                Roo.log(" no label && no align");
23055                 cfg.cn = [  inputblock ] ;
23056                 if (boxLabelCfg) {
23057                     cfg.cn.push(boxLabelCfg);
23058                 }
23059
23060                 
23061         }
23062         
23063        
23064         
23065         if(this.inputType != 'radio'){
23066             cfg.cn.push(hidden);
23067         }
23068         
23069         return cfg;
23070         
23071     },
23072     
23073     /**
23074      * return the real input element.
23075      */
23076     inputEl: function ()
23077     {
23078         return this.el.select('input.roo-' + this.inputType,true).first();
23079     },
23080     hiddenEl: function ()
23081     {
23082         return this.el.select('input.roo-hidden-value',true).first();
23083     },
23084     
23085     labelEl: function()
23086     {
23087         return this.el.select('label.control-label',true).first();
23088     },
23089     /* depricated... */
23090     
23091     label: function()
23092     {
23093         return this.labelEl();
23094     },
23095     
23096     boxLabelEl: function()
23097     {
23098         return this.el.select('label.box-label',true).first();
23099     },
23100     
23101     initEvents : function()
23102     {
23103 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23104         
23105         this.inputEl().on('click', this.onClick,  this);
23106         
23107         if (this.boxLabel) { 
23108             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23109         }
23110         
23111         this.startValue = this.getValue();
23112         
23113         if(this.groupId){
23114             Roo.bootstrap.CheckBox.register(this);
23115         }
23116     },
23117     
23118     onClick : function(e)
23119     {   
23120         if(this.fireEvent('click', this, e) !== false){
23121             this.setChecked(!this.checked);
23122         }
23123         
23124     },
23125     
23126     setChecked : function(state,suppressEvent)
23127     {
23128         this.startValue = this.getValue();
23129
23130         if(this.inputType == 'radio'){
23131             
23132             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23133                 e.dom.checked = false;
23134             });
23135             
23136             this.inputEl().dom.checked = true;
23137             
23138             this.inputEl().dom.value = this.inputValue;
23139             
23140             if(suppressEvent !== true){
23141                 this.fireEvent('check', this, true);
23142             }
23143             
23144             this.validate();
23145             
23146             return;
23147         }
23148         
23149         this.checked = state;
23150         
23151         this.inputEl().dom.checked = state;
23152         
23153         
23154         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23155         
23156         if(suppressEvent !== true){
23157             this.fireEvent('check', this, state);
23158         }
23159         
23160         this.validate();
23161     },
23162     
23163     getValue : function()
23164     {
23165         if(this.inputType == 'radio'){
23166             return this.getGroupValue();
23167         }
23168         
23169         return this.hiddenEl().dom.value;
23170         
23171     },
23172     
23173     getGroupValue : function()
23174     {
23175         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23176             return '';
23177         }
23178         
23179         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23180     },
23181     
23182     setValue : function(v,suppressEvent)
23183     {
23184         if(this.inputType == 'radio'){
23185             this.setGroupValue(v, suppressEvent);
23186             return;
23187         }
23188         
23189         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23190         
23191         this.validate();
23192     },
23193     
23194     setGroupValue : function(v, suppressEvent)
23195     {
23196         this.startValue = this.getValue();
23197         
23198         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23199             e.dom.checked = false;
23200             
23201             if(e.dom.value == v){
23202                 e.dom.checked = true;
23203             }
23204         });
23205         
23206         if(suppressEvent !== true){
23207             this.fireEvent('check', this, true);
23208         }
23209
23210         this.validate();
23211         
23212         return;
23213     },
23214     
23215     validate : function()
23216     {
23217         if(this.getVisibilityEl().hasClass('hidden')){
23218             return true;
23219         }
23220         
23221         if(
23222                 this.disabled || 
23223                 (this.inputType == 'radio' && this.validateRadio()) ||
23224                 (this.inputType == 'checkbox' && this.validateCheckbox())
23225         ){
23226             this.markValid();
23227             return true;
23228         }
23229         
23230         this.markInvalid();
23231         return false;
23232     },
23233     
23234     validateRadio : function()
23235     {
23236         if(this.getVisibilityEl().hasClass('hidden')){
23237             return true;
23238         }
23239         
23240         if(this.allowBlank){
23241             return true;
23242         }
23243         
23244         var valid = false;
23245         
23246         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23247             if(!e.dom.checked){
23248                 return;
23249             }
23250             
23251             valid = true;
23252             
23253             return false;
23254         });
23255         
23256         return valid;
23257     },
23258     
23259     validateCheckbox : function()
23260     {
23261         if(!this.groupId){
23262             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23263             //return (this.getValue() == this.inputValue) ? true : false;
23264         }
23265         
23266         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23267         
23268         if(!group){
23269             return false;
23270         }
23271         
23272         var r = false;
23273         
23274         for(var i in group){
23275             if(group[i].el.isVisible(true)){
23276                 r = false;
23277                 break;
23278             }
23279             
23280             r = true;
23281         }
23282         
23283         for(var i in group){
23284             if(r){
23285                 break;
23286             }
23287             
23288             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23289         }
23290         
23291         return r;
23292     },
23293     
23294     /**
23295      * Mark this field as valid
23296      */
23297     markValid : function()
23298     {
23299         var _this = this;
23300         
23301         this.fireEvent('valid', this);
23302         
23303         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23304         
23305         if(this.groupId){
23306             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23307         }
23308         
23309         if(label){
23310             label.markValid();
23311         }
23312
23313         if(this.inputType == 'radio'){
23314             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23315                 var fg = e.findParent('.form-group', false, true);
23316                 if (Roo.bootstrap.version == 3) {
23317                     fg.removeClass([_this.invalidClass, _this.validClass]);
23318                     fg.addClass(_this.validClass);
23319                 } else {
23320                     fg.removeClass(['is-valid', 'is-invalid']);
23321                     fg.addClass('is-valid');
23322                 }
23323             });
23324             
23325             return;
23326         }
23327
23328         if(!this.groupId){
23329             var fg = this.el.findParent('.form-group', false, true);
23330             if (Roo.bootstrap.version == 3) {
23331                 fg.removeClass([this.invalidClass, this.validClass]);
23332                 fg.addClass(this.validClass);
23333             } else {
23334                 fg.removeClass(['is-valid', 'is-invalid']);
23335                 fg.addClass('is-valid');
23336             }
23337             return;
23338         }
23339         
23340         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23341         
23342         if(!group){
23343             return;
23344         }
23345         
23346         for(var i in group){
23347             var fg = group[i].el.findParent('.form-group', false, true);
23348             if (Roo.bootstrap.version == 3) {
23349                 fg.removeClass([this.invalidClass, this.validClass]);
23350                 fg.addClass(this.validClass);
23351             } else {
23352                 fg.removeClass(['is-valid', 'is-invalid']);
23353                 fg.addClass('is-valid');
23354             }
23355         }
23356     },
23357     
23358      /**
23359      * Mark this field as invalid
23360      * @param {String} msg The validation message
23361      */
23362     markInvalid : function(msg)
23363     {
23364         if(this.allowBlank){
23365             return;
23366         }
23367         
23368         var _this = this;
23369         
23370         this.fireEvent('invalid', this, msg);
23371         
23372         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23373         
23374         if(this.groupId){
23375             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23376         }
23377         
23378         if(label){
23379             label.markInvalid();
23380         }
23381             
23382         if(this.inputType == 'radio'){
23383             
23384             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23385                 var fg = e.findParent('.form-group', false, true);
23386                 if (Roo.bootstrap.version == 3) {
23387                     fg.removeClass([_this.invalidClass, _this.validClass]);
23388                     fg.addClass(_this.invalidClass);
23389                 } else {
23390                     fg.removeClass(['is-invalid', 'is-valid']);
23391                     fg.addClass('is-invalid');
23392                 }
23393             });
23394             
23395             return;
23396         }
23397         
23398         if(!this.groupId){
23399             var fg = this.el.findParent('.form-group', false, true);
23400             if (Roo.bootstrap.version == 3) {
23401                 fg.removeClass([_this.invalidClass, _this.validClass]);
23402                 fg.addClass(_this.invalidClass);
23403             } else {
23404                 fg.removeClass(['is-invalid', 'is-valid']);
23405                 fg.addClass('is-invalid');
23406             }
23407             return;
23408         }
23409         
23410         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23411         
23412         if(!group){
23413             return;
23414         }
23415         
23416         for(var i in group){
23417             var fg = group[i].el.findParent('.form-group', false, true);
23418             if (Roo.bootstrap.version == 3) {
23419                 fg.removeClass([_this.invalidClass, _this.validClass]);
23420                 fg.addClass(_this.invalidClass);
23421             } else {
23422                 fg.removeClass(['is-invalid', 'is-valid']);
23423                 fg.addClass('is-invalid');
23424             }
23425         }
23426         
23427     },
23428     
23429     clearInvalid : function()
23430     {
23431         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23432         
23433         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23434         
23435         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23436         
23437         if (label && label.iconEl) {
23438             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23439             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23440         }
23441     },
23442     
23443     disable : function()
23444     {
23445         if(this.inputType != 'radio'){
23446             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23447             return;
23448         }
23449         
23450         var _this = this;
23451         
23452         if(this.rendered){
23453             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23454                 _this.getActionEl().addClass(this.disabledClass);
23455                 e.dom.disabled = true;
23456             });
23457         }
23458         
23459         this.disabled = true;
23460         this.fireEvent("disable", this);
23461         return this;
23462     },
23463
23464     enable : function()
23465     {
23466         if(this.inputType != 'radio'){
23467             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23468             return;
23469         }
23470         
23471         var _this = this;
23472         
23473         if(this.rendered){
23474             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23475                 _this.getActionEl().removeClass(this.disabledClass);
23476                 e.dom.disabled = false;
23477             });
23478         }
23479         
23480         this.disabled = false;
23481         this.fireEvent("enable", this);
23482         return this;
23483     },
23484     
23485     setBoxLabel : function(v)
23486     {
23487         this.boxLabel = v;
23488         
23489         if(this.rendered){
23490             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23491         }
23492     }
23493
23494 });
23495
23496 Roo.apply(Roo.bootstrap.CheckBox, {
23497     
23498     groups: {},
23499     
23500      /**
23501     * register a CheckBox Group
23502     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23503     */
23504     register : function(checkbox)
23505     {
23506         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23507             this.groups[checkbox.groupId] = {};
23508         }
23509         
23510         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23511             return;
23512         }
23513         
23514         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23515         
23516     },
23517     /**
23518     * fetch a CheckBox Group based on the group ID
23519     * @param {string} the group ID
23520     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23521     */
23522     get: function(groupId) {
23523         if (typeof(this.groups[groupId]) == 'undefined') {
23524             return false;
23525         }
23526         
23527         return this.groups[groupId] ;
23528     }
23529     
23530     
23531 });
23532 /*
23533  * - LGPL
23534  *
23535  * RadioItem
23536  * 
23537  */
23538
23539 /**
23540  * @class Roo.bootstrap.Radio
23541  * @extends Roo.bootstrap.Component
23542  * Bootstrap Radio class
23543  * @cfg {String} boxLabel - the label associated
23544  * @cfg {String} value - the value of radio
23545  * 
23546  * @constructor
23547  * Create a new Radio
23548  * @param {Object} config The config object
23549  */
23550 Roo.bootstrap.Radio = function(config){
23551     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23552     
23553 };
23554
23555 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23556     
23557     boxLabel : '',
23558     
23559     value : '',
23560     
23561     getAutoCreate : function()
23562     {
23563         var cfg = {
23564             tag : 'div',
23565             cls : 'form-group radio',
23566             cn : [
23567                 {
23568                     tag : 'label',
23569                     cls : 'box-label',
23570                     html : this.boxLabel
23571                 }
23572             ]
23573         };
23574         
23575         return cfg;
23576     },
23577     
23578     initEvents : function() 
23579     {
23580         this.parent().register(this);
23581         
23582         this.el.on('click', this.onClick, this);
23583         
23584     },
23585     
23586     onClick : function(e)
23587     {
23588         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23589             this.setChecked(true);
23590         }
23591     },
23592     
23593     setChecked : function(state, suppressEvent)
23594     {
23595         this.parent().setValue(this.value, suppressEvent);
23596         
23597     },
23598     
23599     setBoxLabel : function(v)
23600     {
23601         this.boxLabel = v;
23602         
23603         if(this.rendered){
23604             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23605         }
23606     }
23607     
23608 });
23609  
23610
23611  /*
23612  * - LGPL
23613  *
23614  * Input
23615  * 
23616  */
23617
23618 /**
23619  * @class Roo.bootstrap.SecurePass
23620  * @extends Roo.bootstrap.Input
23621  * Bootstrap SecurePass class
23622  *
23623  * 
23624  * @constructor
23625  * Create a new SecurePass
23626  * @param {Object} config The config object
23627  */
23628  
23629 Roo.bootstrap.SecurePass = function (config) {
23630     // these go here, so the translation tool can replace them..
23631     this.errors = {
23632         PwdEmpty: "Please type a password, and then retype it to confirm.",
23633         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23634         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23635         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23636         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23637         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23638         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23639         TooWeak: "Your password is Too Weak."
23640     },
23641     this.meterLabel = "Password strength:";
23642     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23643     this.meterClass = [
23644         "roo-password-meter-tooweak", 
23645         "roo-password-meter-weak", 
23646         "roo-password-meter-medium", 
23647         "roo-password-meter-strong", 
23648         "roo-password-meter-grey"
23649     ];
23650     
23651     this.errors = {};
23652     
23653     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23654 }
23655
23656 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23657     /**
23658      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23659      * {
23660      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23661      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23662      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23663      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23664      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23665      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23666      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23667      * })
23668      */
23669     // private
23670     
23671     meterWidth: 300,
23672     errorMsg :'',    
23673     errors: false,
23674     imageRoot: '/',
23675     /**
23676      * @cfg {String/Object} Label for the strength meter (defaults to
23677      * 'Password strength:')
23678      */
23679     // private
23680     meterLabel: '',
23681     /**
23682      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23683      * ['Weak', 'Medium', 'Strong'])
23684      */
23685     // private    
23686     pwdStrengths: false,    
23687     // private
23688     strength: 0,
23689     // private
23690     _lastPwd: null,
23691     // private
23692     kCapitalLetter: 0,
23693     kSmallLetter: 1,
23694     kDigit: 2,
23695     kPunctuation: 3,
23696     
23697     insecure: false,
23698     // private
23699     initEvents: function ()
23700     {
23701         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23702
23703         if (this.el.is('input[type=password]') && Roo.isSafari) {
23704             this.el.on('keydown', this.SafariOnKeyDown, this);
23705         }
23706
23707         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23708     },
23709     // private
23710     onRender: function (ct, position)
23711     {
23712         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23713         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23714         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23715
23716         this.trigger.createChild({
23717                    cn: [
23718                     {
23719                     //id: 'PwdMeter',
23720                     tag: 'div',
23721                     cls: 'roo-password-meter-grey col-xs-12',
23722                     style: {
23723                         //width: 0,
23724                         //width: this.meterWidth + 'px'                                                
23725                         }
23726                     },
23727                     {                            
23728                          cls: 'roo-password-meter-text'                          
23729                     }
23730                 ]            
23731         });
23732
23733          
23734         if (this.hideTrigger) {
23735             this.trigger.setDisplayed(false);
23736         }
23737         this.setSize(this.width || '', this.height || '');
23738     },
23739     // private
23740     onDestroy: function ()
23741     {
23742         if (this.trigger) {
23743             this.trigger.removeAllListeners();
23744             this.trigger.remove();
23745         }
23746         if (this.wrap) {
23747             this.wrap.remove();
23748         }
23749         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23750     },
23751     // private
23752     checkStrength: function ()
23753     {
23754         var pwd = this.inputEl().getValue();
23755         if (pwd == this._lastPwd) {
23756             return;
23757         }
23758
23759         var strength;
23760         if (this.ClientSideStrongPassword(pwd)) {
23761             strength = 3;
23762         } else if (this.ClientSideMediumPassword(pwd)) {
23763             strength = 2;
23764         } else if (this.ClientSideWeakPassword(pwd)) {
23765             strength = 1;
23766         } else {
23767             strength = 0;
23768         }
23769         
23770         Roo.log('strength1: ' + strength);
23771         
23772         //var pm = this.trigger.child('div/div/div').dom;
23773         var pm = this.trigger.child('div/div');
23774         pm.removeClass(this.meterClass);
23775         pm.addClass(this.meterClass[strength]);
23776                 
23777         
23778         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23779                 
23780         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23781         
23782         this._lastPwd = pwd;
23783     },
23784     reset: function ()
23785     {
23786         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23787         
23788         this._lastPwd = '';
23789         
23790         var pm = this.trigger.child('div/div');
23791         pm.removeClass(this.meterClass);
23792         pm.addClass('roo-password-meter-grey');        
23793         
23794         
23795         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23796         
23797         pt.innerHTML = '';
23798         this.inputEl().dom.type='password';
23799     },
23800     // private
23801     validateValue: function (value)
23802     {
23803         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23804             return false;
23805         }
23806         if (value.length == 0) {
23807             if (this.allowBlank) {
23808                 this.clearInvalid();
23809                 return true;
23810             }
23811
23812             this.markInvalid(this.errors.PwdEmpty);
23813             this.errorMsg = this.errors.PwdEmpty;
23814             return false;
23815         }
23816         
23817         if(this.insecure){
23818             return true;
23819         }
23820         
23821         if (!value.match(/[\x21-\x7e]+/)) {
23822             this.markInvalid(this.errors.PwdBadChar);
23823             this.errorMsg = this.errors.PwdBadChar;
23824             return false;
23825         }
23826         if (value.length < 6) {
23827             this.markInvalid(this.errors.PwdShort);
23828             this.errorMsg = this.errors.PwdShort;
23829             return false;
23830         }
23831         if (value.length > 16) {
23832             this.markInvalid(this.errors.PwdLong);
23833             this.errorMsg = this.errors.PwdLong;
23834             return false;
23835         }
23836         var strength;
23837         if (this.ClientSideStrongPassword(value)) {
23838             strength = 3;
23839         } else if (this.ClientSideMediumPassword(value)) {
23840             strength = 2;
23841         } else if (this.ClientSideWeakPassword(value)) {
23842             strength = 1;
23843         } else {
23844             strength = 0;
23845         }
23846
23847         
23848         if (strength < 2) {
23849             //this.markInvalid(this.errors.TooWeak);
23850             this.errorMsg = this.errors.TooWeak;
23851             //return false;
23852         }
23853         
23854         
23855         console.log('strength2: ' + strength);
23856         
23857         //var pm = this.trigger.child('div/div/div').dom;
23858         
23859         var pm = this.trigger.child('div/div');
23860         pm.removeClass(this.meterClass);
23861         pm.addClass(this.meterClass[strength]);
23862                 
23863         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23864                 
23865         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23866         
23867         this.errorMsg = ''; 
23868         return true;
23869     },
23870     // private
23871     CharacterSetChecks: function (type)
23872     {
23873         this.type = type;
23874         this.fResult = false;
23875     },
23876     // private
23877     isctype: function (character, type)
23878     {
23879         switch (type) {  
23880             case this.kCapitalLetter:
23881                 if (character >= 'A' && character <= 'Z') {
23882                     return true;
23883                 }
23884                 break;
23885             
23886             case this.kSmallLetter:
23887                 if (character >= 'a' && character <= 'z') {
23888                     return true;
23889                 }
23890                 break;
23891             
23892             case this.kDigit:
23893                 if (character >= '0' && character <= '9') {
23894                     return true;
23895                 }
23896                 break;
23897             
23898             case this.kPunctuation:
23899                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23900                     return true;
23901                 }
23902                 break;
23903             
23904             default:
23905                 return false;
23906         }
23907
23908     },
23909     // private
23910     IsLongEnough: function (pwd, size)
23911     {
23912         return !(pwd == null || isNaN(size) || pwd.length < size);
23913     },
23914     // private
23915     SpansEnoughCharacterSets: function (word, nb)
23916     {
23917         if (!this.IsLongEnough(word, nb))
23918         {
23919             return false;
23920         }
23921
23922         var characterSetChecks = new Array(
23923             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23924             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23925         );
23926         
23927         for (var index = 0; index < word.length; ++index) {
23928             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23929                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23930                     characterSetChecks[nCharSet].fResult = true;
23931                     break;
23932                 }
23933             }
23934         }
23935
23936         var nCharSets = 0;
23937         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23938             if (characterSetChecks[nCharSet].fResult) {
23939                 ++nCharSets;
23940             }
23941         }
23942
23943         if (nCharSets < nb) {
23944             return false;
23945         }
23946         return true;
23947     },
23948     // private
23949     ClientSideStrongPassword: function (pwd)
23950     {
23951         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23952     },
23953     // private
23954     ClientSideMediumPassword: function (pwd)
23955     {
23956         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23957     },
23958     // private
23959     ClientSideWeakPassword: function (pwd)
23960     {
23961         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23962     }
23963           
23964 })//<script type="text/javascript">
23965
23966 /*
23967  * Based  Ext JS Library 1.1.1
23968  * Copyright(c) 2006-2007, Ext JS, LLC.
23969  * LGPL
23970  *
23971  */
23972  
23973 /**
23974  * @class Roo.HtmlEditorCore
23975  * @extends Roo.Component
23976  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23977  *
23978  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23979  */
23980
23981 Roo.HtmlEditorCore = function(config){
23982     
23983     
23984     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23985     
23986     
23987     this.addEvents({
23988         /**
23989          * @event initialize
23990          * Fires when the editor is fully initialized (including the iframe)
23991          * @param {Roo.HtmlEditorCore} this
23992          */
23993         initialize: true,
23994         /**
23995          * @event activate
23996          * Fires when the editor is first receives the focus. Any insertion must wait
23997          * until after this event.
23998          * @param {Roo.HtmlEditorCore} this
23999          */
24000         activate: true,
24001          /**
24002          * @event beforesync
24003          * Fires before the textarea is updated with content from the editor iframe. Return false
24004          * to cancel the sync.
24005          * @param {Roo.HtmlEditorCore} this
24006          * @param {String} html
24007          */
24008         beforesync: true,
24009          /**
24010          * @event beforepush
24011          * Fires before the iframe editor is updated with content from the textarea. Return false
24012          * to cancel the push.
24013          * @param {Roo.HtmlEditorCore} this
24014          * @param {String} html
24015          */
24016         beforepush: true,
24017          /**
24018          * @event sync
24019          * Fires when the textarea is updated with content from the editor iframe.
24020          * @param {Roo.HtmlEditorCore} this
24021          * @param {String} html
24022          */
24023         sync: true,
24024          /**
24025          * @event push
24026          * Fires when the iframe editor is updated with content from the textarea.
24027          * @param {Roo.HtmlEditorCore} this
24028          * @param {String} html
24029          */
24030         push: true,
24031         
24032         /**
24033          * @event editorevent
24034          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24035          * @param {Roo.HtmlEditorCore} this
24036          */
24037         editorevent: true
24038         
24039     });
24040     
24041     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24042     
24043     // defaults : white / black...
24044     this.applyBlacklists();
24045     
24046     
24047     
24048 };
24049
24050
24051 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24052
24053
24054      /**
24055      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24056      */
24057     
24058     owner : false,
24059     
24060      /**
24061      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24062      *                        Roo.resizable.
24063      */
24064     resizable : false,
24065      /**
24066      * @cfg {Number} height (in pixels)
24067      */   
24068     height: 300,
24069    /**
24070      * @cfg {Number} width (in pixels)
24071      */   
24072     width: 500,
24073     
24074     /**
24075      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24076      * 
24077      */
24078     stylesheets: false,
24079     
24080     // id of frame..
24081     frameId: false,
24082     
24083     // private properties
24084     validationEvent : false,
24085     deferHeight: true,
24086     initialized : false,
24087     activated : false,
24088     sourceEditMode : false,
24089     onFocus : Roo.emptyFn,
24090     iframePad:3,
24091     hideMode:'offsets',
24092     
24093     clearUp: true,
24094     
24095     // blacklist + whitelisted elements..
24096     black: false,
24097     white: false,
24098      
24099     bodyCls : '',
24100
24101     /**
24102      * Protected method that will not generally be called directly. It
24103      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24104      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24105      */
24106     getDocMarkup : function(){
24107         // body styles..
24108         var st = '';
24109         
24110         // inherit styels from page...?? 
24111         if (this.stylesheets === false) {
24112             
24113             Roo.get(document.head).select('style').each(function(node) {
24114                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24115             });
24116             
24117             Roo.get(document.head).select('link').each(function(node) { 
24118                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24119             });
24120             
24121         } else if (!this.stylesheets.length) {
24122                 // simple..
24123                 st = '<style type="text/css">' +
24124                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24125                    '</style>';
24126         } else {
24127             for (var i in this.stylesheets) { 
24128                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24129             }
24130             
24131         }
24132         
24133         st +=  '<style type="text/css">' +
24134             'IMG { cursor: pointer } ' +
24135         '</style>';
24136
24137         var cls = 'roo-htmleditor-body';
24138         
24139         if(this.bodyCls.length){
24140             cls += ' ' + this.bodyCls;
24141         }
24142         
24143         return '<html><head>' + st  +
24144             //<style type="text/css">' +
24145             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24146             //'</style>' +
24147             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24148     },
24149
24150     // private
24151     onRender : function(ct, position)
24152     {
24153         var _t = this;
24154         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24155         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24156         
24157         
24158         this.el.dom.style.border = '0 none';
24159         this.el.dom.setAttribute('tabIndex', -1);
24160         this.el.addClass('x-hidden hide');
24161         
24162         
24163         
24164         if(Roo.isIE){ // fix IE 1px bogus margin
24165             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24166         }
24167        
24168         
24169         this.frameId = Roo.id();
24170         
24171          
24172         
24173         var iframe = this.owner.wrap.createChild({
24174             tag: 'iframe',
24175             cls: 'form-control', // bootstrap..
24176             id: this.frameId,
24177             name: this.frameId,
24178             frameBorder : 'no',
24179             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24180         }, this.el
24181         );
24182         
24183         
24184         this.iframe = iframe.dom;
24185
24186          this.assignDocWin();
24187         
24188         this.doc.designMode = 'on';
24189        
24190         this.doc.open();
24191         this.doc.write(this.getDocMarkup());
24192         this.doc.close();
24193
24194         
24195         var task = { // must defer to wait for browser to be ready
24196             run : function(){
24197                 //console.log("run task?" + this.doc.readyState);
24198                 this.assignDocWin();
24199                 if(this.doc.body || this.doc.readyState == 'complete'){
24200                     try {
24201                         this.doc.designMode="on";
24202                     } catch (e) {
24203                         return;
24204                     }
24205                     Roo.TaskMgr.stop(task);
24206                     this.initEditor.defer(10, this);
24207                 }
24208             },
24209             interval : 10,
24210             duration: 10000,
24211             scope: this
24212         };
24213         Roo.TaskMgr.start(task);
24214
24215     },
24216
24217     // private
24218     onResize : function(w, h)
24219     {
24220          Roo.log('resize: ' +w + ',' + h );
24221         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24222         if(!this.iframe){
24223             return;
24224         }
24225         if(typeof w == 'number'){
24226             
24227             this.iframe.style.width = w + 'px';
24228         }
24229         if(typeof h == 'number'){
24230             
24231             this.iframe.style.height = h + 'px';
24232             if(this.doc){
24233                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24234             }
24235         }
24236         
24237     },
24238
24239     /**
24240      * Toggles the editor between standard and source edit mode.
24241      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24242      */
24243     toggleSourceEdit : function(sourceEditMode){
24244         
24245         this.sourceEditMode = sourceEditMode === true;
24246         
24247         if(this.sourceEditMode){
24248  
24249             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24250             
24251         }else{
24252             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24253             //this.iframe.className = '';
24254             this.deferFocus();
24255         }
24256         //this.setSize(this.owner.wrap.getSize());
24257         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24258     },
24259
24260     
24261   
24262
24263     /**
24264      * Protected method that will not generally be called directly. If you need/want
24265      * custom HTML cleanup, this is the method you should override.
24266      * @param {String} html The HTML to be cleaned
24267      * return {String} The cleaned HTML
24268      */
24269     cleanHtml : function(html){
24270         html = String(html);
24271         if(html.length > 5){
24272             if(Roo.isSafari){ // strip safari nonsense
24273                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24274             }
24275         }
24276         if(html == '&nbsp;'){
24277             html = '';
24278         }
24279         return html;
24280     },
24281
24282     /**
24283      * HTML Editor -> Textarea
24284      * Protected method that will not generally be called directly. Syncs the contents
24285      * of the editor iframe with the textarea.
24286      */
24287     syncValue : function(){
24288         if(this.initialized){
24289             var bd = (this.doc.body || this.doc.documentElement);
24290             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24291             var html = bd.innerHTML;
24292             if(Roo.isSafari){
24293                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24294                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24295                 if(m && m[1]){
24296                     html = '<div style="'+m[0]+'">' + html + '</div>';
24297                 }
24298             }
24299             html = this.cleanHtml(html);
24300             // fix up the special chars.. normaly like back quotes in word...
24301             // however we do not want to do this with chinese..
24302             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24303                 
24304                 var cc = match.charCodeAt();
24305
24306                 // Get the character value, handling surrogate pairs
24307                 if (match.length == 2) {
24308                     // It's a surrogate pair, calculate the Unicode code point
24309                     var high = match.charCodeAt(0) - 0xD800;
24310                     var low  = match.charCodeAt(1) - 0xDC00;
24311                     cc = (high * 0x400) + low + 0x10000;
24312                 }  else if (
24313                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24314                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24315                     (cc >= 0xf900 && cc < 0xfb00 )
24316                 ) {
24317                         return match;
24318                 }  
24319          
24320                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24321                 return "&#" + cc + ";";
24322                 
24323                 
24324             });
24325             
24326             
24327              
24328             if(this.owner.fireEvent('beforesync', this, html) !== false){
24329                 this.el.dom.value = html;
24330                 this.owner.fireEvent('sync', this, html);
24331             }
24332         }
24333     },
24334
24335     /**
24336      * Protected method that will not generally be called directly. Pushes the value of the textarea
24337      * into the iframe editor.
24338      */
24339     pushValue : function(){
24340         if(this.initialized){
24341             var v = this.el.dom.value.trim();
24342             
24343 //            if(v.length < 1){
24344 //                v = '&#160;';
24345 //            }
24346             
24347             if(this.owner.fireEvent('beforepush', this, v) !== false){
24348                 var d = (this.doc.body || this.doc.documentElement);
24349                 d.innerHTML = v;
24350                 this.cleanUpPaste();
24351                 this.el.dom.value = d.innerHTML;
24352                 this.owner.fireEvent('push', this, v);
24353             }
24354         }
24355     },
24356
24357     // private
24358     deferFocus : function(){
24359         this.focus.defer(10, this);
24360     },
24361
24362     // doc'ed in Field
24363     focus : function(){
24364         if(this.win && !this.sourceEditMode){
24365             this.win.focus();
24366         }else{
24367             this.el.focus();
24368         }
24369     },
24370     
24371     assignDocWin: function()
24372     {
24373         var iframe = this.iframe;
24374         
24375          if(Roo.isIE){
24376             this.doc = iframe.contentWindow.document;
24377             this.win = iframe.contentWindow;
24378         } else {
24379 //            if (!Roo.get(this.frameId)) {
24380 //                return;
24381 //            }
24382 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24383 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24384             
24385             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24386                 return;
24387             }
24388             
24389             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24390             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24391         }
24392     },
24393     
24394     // private
24395     initEditor : function(){
24396         //console.log("INIT EDITOR");
24397         this.assignDocWin();
24398         
24399         
24400         
24401         this.doc.designMode="on";
24402         this.doc.open();
24403         this.doc.write(this.getDocMarkup());
24404         this.doc.close();
24405         
24406         var dbody = (this.doc.body || this.doc.documentElement);
24407         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24408         // this copies styles from the containing element into thsi one..
24409         // not sure why we need all of this..
24410         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24411         
24412         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24413         //ss['background-attachment'] = 'fixed'; // w3c
24414         dbody.bgProperties = 'fixed'; // ie
24415         //Roo.DomHelper.applyStyles(dbody, ss);
24416         Roo.EventManager.on(this.doc, {
24417             //'mousedown': this.onEditorEvent,
24418             'mouseup': this.onEditorEvent,
24419             'dblclick': this.onEditorEvent,
24420             'click': this.onEditorEvent,
24421             'keyup': this.onEditorEvent,
24422             buffer:100,
24423             scope: this
24424         });
24425         if(Roo.isGecko){
24426             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24427         }
24428         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24429             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24430         }
24431         this.initialized = true;
24432
24433         this.owner.fireEvent('initialize', this);
24434         this.pushValue();
24435     },
24436
24437     // private
24438     onDestroy : function(){
24439         
24440         
24441         
24442         if(this.rendered){
24443             
24444             //for (var i =0; i < this.toolbars.length;i++) {
24445             //    // fixme - ask toolbars for heights?
24446             //    this.toolbars[i].onDestroy();
24447            // }
24448             
24449             //this.wrap.dom.innerHTML = '';
24450             //this.wrap.remove();
24451         }
24452     },
24453
24454     // private
24455     onFirstFocus : function(){
24456         
24457         this.assignDocWin();
24458         
24459         
24460         this.activated = true;
24461          
24462     
24463         if(Roo.isGecko){ // prevent silly gecko errors
24464             this.win.focus();
24465             var s = this.win.getSelection();
24466             if(!s.focusNode || s.focusNode.nodeType != 3){
24467                 var r = s.getRangeAt(0);
24468                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24469                 r.collapse(true);
24470                 this.deferFocus();
24471             }
24472             try{
24473                 this.execCmd('useCSS', true);
24474                 this.execCmd('styleWithCSS', false);
24475             }catch(e){}
24476         }
24477         this.owner.fireEvent('activate', this);
24478     },
24479
24480     // private
24481     adjustFont: function(btn){
24482         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24483         //if(Roo.isSafari){ // safari
24484         //    adjust *= 2;
24485        // }
24486         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24487         if(Roo.isSafari){ // safari
24488             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24489             v =  (v < 10) ? 10 : v;
24490             v =  (v > 48) ? 48 : v;
24491             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24492             
24493         }
24494         
24495         
24496         v = Math.max(1, v+adjust);
24497         
24498         this.execCmd('FontSize', v  );
24499     },
24500
24501     onEditorEvent : function(e)
24502     {
24503         this.owner.fireEvent('editorevent', this, e);
24504       //  this.updateToolbar();
24505         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24506     },
24507
24508     insertTag : function(tg)
24509     {
24510         // could be a bit smarter... -> wrap the current selected tRoo..
24511         if (tg.toLowerCase() == 'span' ||
24512             tg.toLowerCase() == 'code' ||
24513             tg.toLowerCase() == 'sup' ||
24514             tg.toLowerCase() == 'sub' 
24515             ) {
24516             
24517             range = this.createRange(this.getSelection());
24518             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24519             wrappingNode.appendChild(range.extractContents());
24520             range.insertNode(wrappingNode);
24521
24522             return;
24523             
24524             
24525             
24526         }
24527         this.execCmd("formatblock",   tg);
24528         
24529     },
24530     
24531     insertText : function(txt)
24532     {
24533         
24534         
24535         var range = this.createRange();
24536         range.deleteContents();
24537                //alert(Sender.getAttribute('label'));
24538                
24539         range.insertNode(this.doc.createTextNode(txt));
24540     } ,
24541     
24542      
24543
24544     /**
24545      * Executes a Midas editor command on the editor document and performs necessary focus and
24546      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24547      * @param {String} cmd The Midas command
24548      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24549      */
24550     relayCmd : function(cmd, value){
24551         this.win.focus();
24552         this.execCmd(cmd, value);
24553         this.owner.fireEvent('editorevent', this);
24554         //this.updateToolbar();
24555         this.owner.deferFocus();
24556     },
24557
24558     /**
24559      * Executes a Midas editor command directly on the editor document.
24560      * For visual commands, you should use {@link #relayCmd} instead.
24561      * <b>This should only be called after the editor is initialized.</b>
24562      * @param {String} cmd The Midas command
24563      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24564      */
24565     execCmd : function(cmd, value){
24566         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24567         this.syncValue();
24568     },
24569  
24570  
24571    
24572     /**
24573      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24574      * to insert tRoo.
24575      * @param {String} text | dom node.. 
24576      */
24577     insertAtCursor : function(text)
24578     {
24579         
24580         if(!this.activated){
24581             return;
24582         }
24583         /*
24584         if(Roo.isIE){
24585             this.win.focus();
24586             var r = this.doc.selection.createRange();
24587             if(r){
24588                 r.collapse(true);
24589                 r.pasteHTML(text);
24590                 this.syncValue();
24591                 this.deferFocus();
24592             
24593             }
24594             return;
24595         }
24596         */
24597         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24598             this.win.focus();
24599             
24600             
24601             // from jquery ui (MIT licenced)
24602             var range, node;
24603             var win = this.win;
24604             
24605             if (win.getSelection && win.getSelection().getRangeAt) {
24606                 range = win.getSelection().getRangeAt(0);
24607                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24608                 range.insertNode(node);
24609             } else if (win.document.selection && win.document.selection.createRange) {
24610                 // no firefox support
24611                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24612                 win.document.selection.createRange().pasteHTML(txt);
24613             } else {
24614                 // no firefox support
24615                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24616                 this.execCmd('InsertHTML', txt);
24617             } 
24618             
24619             this.syncValue();
24620             
24621             this.deferFocus();
24622         }
24623     },
24624  // private
24625     mozKeyPress : function(e){
24626         if(e.ctrlKey){
24627             var c = e.getCharCode(), cmd;
24628           
24629             if(c > 0){
24630                 c = String.fromCharCode(c).toLowerCase();
24631                 switch(c){
24632                     case 'b':
24633                         cmd = 'bold';
24634                         break;
24635                     case 'i':
24636                         cmd = 'italic';
24637                         break;
24638                     
24639                     case 'u':
24640                         cmd = 'underline';
24641                         break;
24642                     
24643                     case 'v':
24644                         this.cleanUpPaste.defer(100, this);
24645                         return;
24646                         
24647                 }
24648                 if(cmd){
24649                     this.win.focus();
24650                     this.execCmd(cmd);
24651                     this.deferFocus();
24652                     e.preventDefault();
24653                 }
24654                 
24655             }
24656         }
24657     },
24658
24659     // private
24660     fixKeys : function(){ // load time branching for fastest keydown performance
24661         if(Roo.isIE){
24662             return function(e){
24663                 var k = e.getKey(), r;
24664                 if(k == e.TAB){
24665                     e.stopEvent();
24666                     r = this.doc.selection.createRange();
24667                     if(r){
24668                         r.collapse(true);
24669                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24670                         this.deferFocus();
24671                     }
24672                     return;
24673                 }
24674                 
24675                 if(k == e.ENTER){
24676                     r = this.doc.selection.createRange();
24677                     if(r){
24678                         var target = r.parentElement();
24679                         if(!target || target.tagName.toLowerCase() != 'li'){
24680                             e.stopEvent();
24681                             r.pasteHTML('<br />');
24682                             r.collapse(false);
24683                             r.select();
24684                         }
24685                     }
24686                 }
24687                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24688                     this.cleanUpPaste.defer(100, this);
24689                     return;
24690                 }
24691                 
24692                 
24693             };
24694         }else if(Roo.isOpera){
24695             return function(e){
24696                 var k = e.getKey();
24697                 if(k == e.TAB){
24698                     e.stopEvent();
24699                     this.win.focus();
24700                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24701                     this.deferFocus();
24702                 }
24703                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24704                     this.cleanUpPaste.defer(100, this);
24705                     return;
24706                 }
24707                 
24708             };
24709         }else if(Roo.isSafari){
24710             return function(e){
24711                 var k = e.getKey();
24712                 
24713                 if(k == e.TAB){
24714                     e.stopEvent();
24715                     this.execCmd('InsertText','\t');
24716                     this.deferFocus();
24717                     return;
24718                 }
24719                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24720                     this.cleanUpPaste.defer(100, this);
24721                     return;
24722                 }
24723                 
24724              };
24725         }
24726     }(),
24727     
24728     getAllAncestors: function()
24729     {
24730         var p = this.getSelectedNode();
24731         var a = [];
24732         if (!p) {
24733             a.push(p); // push blank onto stack..
24734             p = this.getParentElement();
24735         }
24736         
24737         
24738         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24739             a.push(p);
24740             p = p.parentNode;
24741         }
24742         a.push(this.doc.body);
24743         return a;
24744     },
24745     lastSel : false,
24746     lastSelNode : false,
24747     
24748     
24749     getSelection : function() 
24750     {
24751         this.assignDocWin();
24752         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24753     },
24754     
24755     getSelectedNode: function() 
24756     {
24757         // this may only work on Gecko!!!
24758         
24759         // should we cache this!!!!
24760         
24761         
24762         
24763          
24764         var range = this.createRange(this.getSelection()).cloneRange();
24765         
24766         if (Roo.isIE) {
24767             var parent = range.parentElement();
24768             while (true) {
24769                 var testRange = range.duplicate();
24770                 testRange.moveToElementText(parent);
24771                 if (testRange.inRange(range)) {
24772                     break;
24773                 }
24774                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24775                     break;
24776                 }
24777                 parent = parent.parentElement;
24778             }
24779             return parent;
24780         }
24781         
24782         // is ancestor a text element.
24783         var ac =  range.commonAncestorContainer;
24784         if (ac.nodeType == 3) {
24785             ac = ac.parentNode;
24786         }
24787         
24788         var ar = ac.childNodes;
24789          
24790         var nodes = [];
24791         var other_nodes = [];
24792         var has_other_nodes = false;
24793         for (var i=0;i<ar.length;i++) {
24794             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24795                 continue;
24796             }
24797             // fullly contained node.
24798             
24799             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24800                 nodes.push(ar[i]);
24801                 continue;
24802             }
24803             
24804             // probably selected..
24805             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24806                 other_nodes.push(ar[i]);
24807                 continue;
24808             }
24809             // outer..
24810             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24811                 continue;
24812             }
24813             
24814             
24815             has_other_nodes = true;
24816         }
24817         if (!nodes.length && other_nodes.length) {
24818             nodes= other_nodes;
24819         }
24820         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24821             return false;
24822         }
24823         
24824         return nodes[0];
24825     },
24826     createRange: function(sel)
24827     {
24828         // this has strange effects when using with 
24829         // top toolbar - not sure if it's a great idea.
24830         //this.editor.contentWindow.focus();
24831         if (typeof sel != "undefined") {
24832             try {
24833                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24834             } catch(e) {
24835                 return this.doc.createRange();
24836             }
24837         } else {
24838             return this.doc.createRange();
24839         }
24840     },
24841     getParentElement: function()
24842     {
24843         
24844         this.assignDocWin();
24845         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24846         
24847         var range = this.createRange(sel);
24848          
24849         try {
24850             var p = range.commonAncestorContainer;
24851             while (p.nodeType == 3) { // text node
24852                 p = p.parentNode;
24853             }
24854             return p;
24855         } catch (e) {
24856             return null;
24857         }
24858     
24859     },
24860     /***
24861      *
24862      * Range intersection.. the hard stuff...
24863      *  '-1' = before
24864      *  '0' = hits..
24865      *  '1' = after.
24866      *         [ -- selected range --- ]
24867      *   [fail]                        [fail]
24868      *
24869      *    basically..
24870      *      if end is before start or  hits it. fail.
24871      *      if start is after end or hits it fail.
24872      *
24873      *   if either hits (but other is outside. - then it's not 
24874      *   
24875      *    
24876      **/
24877     
24878     
24879     // @see http://www.thismuchiknow.co.uk/?p=64.
24880     rangeIntersectsNode : function(range, node)
24881     {
24882         var nodeRange = node.ownerDocument.createRange();
24883         try {
24884             nodeRange.selectNode(node);
24885         } catch (e) {
24886             nodeRange.selectNodeContents(node);
24887         }
24888     
24889         var rangeStartRange = range.cloneRange();
24890         rangeStartRange.collapse(true);
24891     
24892         var rangeEndRange = range.cloneRange();
24893         rangeEndRange.collapse(false);
24894     
24895         var nodeStartRange = nodeRange.cloneRange();
24896         nodeStartRange.collapse(true);
24897     
24898         var nodeEndRange = nodeRange.cloneRange();
24899         nodeEndRange.collapse(false);
24900     
24901         return rangeStartRange.compareBoundaryPoints(
24902                  Range.START_TO_START, nodeEndRange) == -1 &&
24903                rangeEndRange.compareBoundaryPoints(
24904                  Range.START_TO_START, nodeStartRange) == 1;
24905         
24906          
24907     },
24908     rangeCompareNode : function(range, node)
24909     {
24910         var nodeRange = node.ownerDocument.createRange();
24911         try {
24912             nodeRange.selectNode(node);
24913         } catch (e) {
24914             nodeRange.selectNodeContents(node);
24915         }
24916         
24917         
24918         range.collapse(true);
24919     
24920         nodeRange.collapse(true);
24921      
24922         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24923         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24924          
24925         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24926         
24927         var nodeIsBefore   =  ss == 1;
24928         var nodeIsAfter    = ee == -1;
24929         
24930         if (nodeIsBefore && nodeIsAfter) {
24931             return 0; // outer
24932         }
24933         if (!nodeIsBefore && nodeIsAfter) {
24934             return 1; //right trailed.
24935         }
24936         
24937         if (nodeIsBefore && !nodeIsAfter) {
24938             return 2;  // left trailed.
24939         }
24940         // fully contined.
24941         return 3;
24942     },
24943
24944     // private? - in a new class?
24945     cleanUpPaste :  function()
24946     {
24947         // cleans up the whole document..
24948         Roo.log('cleanuppaste');
24949         
24950         this.cleanUpChildren(this.doc.body);
24951         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24952         if (clean != this.doc.body.innerHTML) {
24953             this.doc.body.innerHTML = clean;
24954         }
24955         
24956     },
24957     
24958     cleanWordChars : function(input) {// change the chars to hex code
24959         var he = Roo.HtmlEditorCore;
24960         
24961         var output = input;
24962         Roo.each(he.swapCodes, function(sw) { 
24963             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24964             
24965             output = output.replace(swapper, sw[1]);
24966         });
24967         
24968         return output;
24969     },
24970     
24971     
24972     cleanUpChildren : function (n)
24973     {
24974         if (!n.childNodes.length) {
24975             return;
24976         }
24977         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24978            this.cleanUpChild(n.childNodes[i]);
24979         }
24980     },
24981     
24982     
24983         
24984     
24985     cleanUpChild : function (node)
24986     {
24987         var ed = this;
24988         //console.log(node);
24989         if (node.nodeName == "#text") {
24990             // clean up silly Windows -- stuff?
24991             return; 
24992         }
24993         if (node.nodeName == "#comment") {
24994             node.parentNode.removeChild(node);
24995             // clean up silly Windows -- stuff?
24996             return; 
24997         }
24998         var lcname = node.tagName.toLowerCase();
24999         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25000         // whitelist of tags..
25001         
25002         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25003             // remove node.
25004             node.parentNode.removeChild(node);
25005             return;
25006             
25007         }
25008         
25009         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25010         
25011         // spans with no attributes - just remove them..
25012         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25013             remove_keep_children = true;
25014         }
25015         
25016         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25017         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25018         
25019         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25020         //    remove_keep_children = true;
25021         //}
25022         
25023         if (remove_keep_children) {
25024             this.cleanUpChildren(node);
25025             // inserts everything just before this node...
25026             while (node.childNodes.length) {
25027                 var cn = node.childNodes[0];
25028                 node.removeChild(cn);
25029                 node.parentNode.insertBefore(cn, node);
25030             }
25031             node.parentNode.removeChild(node);
25032             return;
25033         }
25034         
25035         if (!node.attributes || !node.attributes.length) {
25036             
25037           
25038             
25039             
25040             this.cleanUpChildren(node);
25041             return;
25042         }
25043         
25044         function cleanAttr(n,v)
25045         {
25046             
25047             if (v.match(/^\./) || v.match(/^\//)) {
25048                 return;
25049             }
25050             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25051                 return;
25052             }
25053             if (v.match(/^#/)) {
25054                 return;
25055             }
25056             if (v.match(/^\{/)) { // allow template editing.
25057                 return;
25058             }
25059 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25060             node.removeAttribute(n);
25061             
25062         }
25063         
25064         var cwhite = this.cwhite;
25065         var cblack = this.cblack;
25066             
25067         function cleanStyle(n,v)
25068         {
25069             if (v.match(/expression/)) { //XSS?? should we even bother..
25070                 node.removeAttribute(n);
25071                 return;
25072             }
25073             
25074             var parts = v.split(/;/);
25075             var clean = [];
25076             
25077             Roo.each(parts, function(p) {
25078                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25079                 if (!p.length) {
25080                     return true;
25081                 }
25082                 var l = p.split(':').shift().replace(/\s+/g,'');
25083                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25084                 
25085                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25086 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25087                     //node.removeAttribute(n);
25088                     return true;
25089                 }
25090                 //Roo.log()
25091                 // only allow 'c whitelisted system attributes'
25092                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25093 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25094                     //node.removeAttribute(n);
25095                     return true;
25096                 }
25097                 
25098                 
25099                  
25100                 
25101                 clean.push(p);
25102                 return true;
25103             });
25104             if (clean.length) { 
25105                 node.setAttribute(n, clean.join(';'));
25106             } else {
25107                 node.removeAttribute(n);
25108             }
25109             
25110         }
25111         
25112         
25113         for (var i = node.attributes.length-1; i > -1 ; i--) {
25114             var a = node.attributes[i];
25115             //console.log(a);
25116             
25117             if (a.name.toLowerCase().substr(0,2)=='on')  {
25118                 node.removeAttribute(a.name);
25119                 continue;
25120             }
25121             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25122                 node.removeAttribute(a.name);
25123                 continue;
25124             }
25125             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25126                 cleanAttr(a.name,a.value); // fixme..
25127                 continue;
25128             }
25129             if (a.name == 'style') {
25130                 cleanStyle(a.name,a.value);
25131                 continue;
25132             }
25133             /// clean up MS crap..
25134             // tecnically this should be a list of valid class'es..
25135             
25136             
25137             if (a.name == 'class') {
25138                 if (a.value.match(/^Mso/)) {
25139                     node.removeAttribute('class');
25140                 }
25141                 
25142                 if (a.value.match(/^body$/)) {
25143                     node.removeAttribute('class');
25144                 }
25145                 continue;
25146             }
25147             
25148             // style cleanup!?
25149             // class cleanup?
25150             
25151         }
25152         
25153         
25154         this.cleanUpChildren(node);
25155         
25156         
25157     },
25158     
25159     /**
25160      * Clean up MS wordisms...
25161      */
25162     cleanWord : function(node)
25163     {
25164         if (!node) {
25165             this.cleanWord(this.doc.body);
25166             return;
25167         }
25168         
25169         if(
25170                 node.nodeName == 'SPAN' &&
25171                 !node.hasAttributes() &&
25172                 node.childNodes.length == 1 &&
25173                 node.firstChild.nodeName == "#text"  
25174         ) {
25175             var textNode = node.firstChild;
25176             node.removeChild(textNode);
25177             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25178                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25179             }
25180             node.parentNode.insertBefore(textNode, node);
25181             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25182                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25183             }
25184             node.parentNode.removeChild(node);
25185         }
25186         
25187         if (node.nodeName == "#text") {
25188             // clean up silly Windows -- stuff?
25189             return; 
25190         }
25191         if (node.nodeName == "#comment") {
25192             node.parentNode.removeChild(node);
25193             // clean up silly Windows -- stuff?
25194             return; 
25195         }
25196         
25197         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25198             node.parentNode.removeChild(node);
25199             return;
25200         }
25201         //Roo.log(node.tagName);
25202         // remove - but keep children..
25203         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25204             //Roo.log('-- removed');
25205             while (node.childNodes.length) {
25206                 var cn = node.childNodes[0];
25207                 node.removeChild(cn);
25208                 node.parentNode.insertBefore(cn, node);
25209                 // move node to parent - and clean it..
25210                 this.cleanWord(cn);
25211             }
25212             node.parentNode.removeChild(node);
25213             /// no need to iterate chidlren = it's got none..
25214             //this.iterateChildren(node, this.cleanWord);
25215             return;
25216         }
25217         // clean styles
25218         if (node.className.length) {
25219             
25220             var cn = node.className.split(/\W+/);
25221             var cna = [];
25222             Roo.each(cn, function(cls) {
25223                 if (cls.match(/Mso[a-zA-Z]+/)) {
25224                     return;
25225                 }
25226                 cna.push(cls);
25227             });
25228             node.className = cna.length ? cna.join(' ') : '';
25229             if (!cna.length) {
25230                 node.removeAttribute("class");
25231             }
25232         }
25233         
25234         if (node.hasAttribute("lang")) {
25235             node.removeAttribute("lang");
25236         }
25237         
25238         if (node.hasAttribute("style")) {
25239             
25240             var styles = node.getAttribute("style").split(";");
25241             var nstyle = [];
25242             Roo.each(styles, function(s) {
25243                 if (!s.match(/:/)) {
25244                     return;
25245                 }
25246                 var kv = s.split(":");
25247                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25248                     return;
25249                 }
25250                 // what ever is left... we allow.
25251                 nstyle.push(s);
25252             });
25253             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25254             if (!nstyle.length) {
25255                 node.removeAttribute('style');
25256             }
25257         }
25258         this.iterateChildren(node, this.cleanWord);
25259         
25260         
25261         
25262     },
25263     /**
25264      * iterateChildren of a Node, calling fn each time, using this as the scole..
25265      * @param {DomNode} node node to iterate children of.
25266      * @param {Function} fn method of this class to call on each item.
25267      */
25268     iterateChildren : function(node, fn)
25269     {
25270         if (!node.childNodes.length) {
25271                 return;
25272         }
25273         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25274            fn.call(this, node.childNodes[i])
25275         }
25276     },
25277     
25278     
25279     /**
25280      * cleanTableWidths.
25281      *
25282      * Quite often pasting from word etc.. results in tables with column and widths.
25283      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25284      *
25285      */
25286     cleanTableWidths : function(node)
25287     {
25288          
25289          
25290         if (!node) {
25291             this.cleanTableWidths(this.doc.body);
25292             return;
25293         }
25294         
25295         // ignore list...
25296         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25297             return; 
25298         }
25299         Roo.log(node.tagName);
25300         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25301             this.iterateChildren(node, this.cleanTableWidths);
25302             return;
25303         }
25304         if (node.hasAttribute('width')) {
25305             node.removeAttribute('width');
25306         }
25307         
25308          
25309         if (node.hasAttribute("style")) {
25310             // pretty basic...
25311             
25312             var styles = node.getAttribute("style").split(";");
25313             var nstyle = [];
25314             Roo.each(styles, function(s) {
25315                 if (!s.match(/:/)) {
25316                     return;
25317                 }
25318                 var kv = s.split(":");
25319                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25320                     return;
25321                 }
25322                 // what ever is left... we allow.
25323                 nstyle.push(s);
25324             });
25325             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25326             if (!nstyle.length) {
25327                 node.removeAttribute('style');
25328             }
25329         }
25330         
25331         this.iterateChildren(node, this.cleanTableWidths);
25332         
25333         
25334     },
25335     
25336     
25337     
25338     
25339     domToHTML : function(currentElement, depth, nopadtext) {
25340         
25341         depth = depth || 0;
25342         nopadtext = nopadtext || false;
25343     
25344         if (!currentElement) {
25345             return this.domToHTML(this.doc.body);
25346         }
25347         
25348         //Roo.log(currentElement);
25349         var j;
25350         var allText = false;
25351         var nodeName = currentElement.nodeName;
25352         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25353         
25354         if  (nodeName == '#text') {
25355             
25356             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25357         }
25358         
25359         
25360         var ret = '';
25361         if (nodeName != 'BODY') {
25362              
25363             var i = 0;
25364             // Prints the node tagName, such as <A>, <IMG>, etc
25365             if (tagName) {
25366                 var attr = [];
25367                 for(i = 0; i < currentElement.attributes.length;i++) {
25368                     // quoting?
25369                     var aname = currentElement.attributes.item(i).name;
25370                     if (!currentElement.attributes.item(i).value.length) {
25371                         continue;
25372                     }
25373                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25374                 }
25375                 
25376                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25377             } 
25378             else {
25379                 
25380                 // eack
25381             }
25382         } else {
25383             tagName = false;
25384         }
25385         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25386             return ret;
25387         }
25388         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25389             nopadtext = true;
25390         }
25391         
25392         
25393         // Traverse the tree
25394         i = 0;
25395         var currentElementChild = currentElement.childNodes.item(i);
25396         var allText = true;
25397         var innerHTML  = '';
25398         lastnode = '';
25399         while (currentElementChild) {
25400             // Formatting code (indent the tree so it looks nice on the screen)
25401             var nopad = nopadtext;
25402             if (lastnode == 'SPAN') {
25403                 nopad  = true;
25404             }
25405             // text
25406             if  (currentElementChild.nodeName == '#text') {
25407                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25408                 toadd = nopadtext ? toadd : toadd.trim();
25409                 if (!nopad && toadd.length > 80) {
25410                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25411                 }
25412                 innerHTML  += toadd;
25413                 
25414                 i++;
25415                 currentElementChild = currentElement.childNodes.item(i);
25416                 lastNode = '';
25417                 continue;
25418             }
25419             allText = false;
25420             
25421             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25422                 
25423             // Recursively traverse the tree structure of the child node
25424             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25425             lastnode = currentElementChild.nodeName;
25426             i++;
25427             currentElementChild=currentElement.childNodes.item(i);
25428         }
25429         
25430         ret += innerHTML;
25431         
25432         if (!allText) {
25433                 // The remaining code is mostly for formatting the tree
25434             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25435         }
25436         
25437         
25438         if (tagName) {
25439             ret+= "</"+tagName+">";
25440         }
25441         return ret;
25442         
25443     },
25444         
25445     applyBlacklists : function()
25446     {
25447         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25448         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25449         
25450         this.white = [];
25451         this.black = [];
25452         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25453             if (b.indexOf(tag) > -1) {
25454                 return;
25455             }
25456             this.white.push(tag);
25457             
25458         }, this);
25459         
25460         Roo.each(w, function(tag) {
25461             if (b.indexOf(tag) > -1) {
25462                 return;
25463             }
25464             if (this.white.indexOf(tag) > -1) {
25465                 return;
25466             }
25467             this.white.push(tag);
25468             
25469         }, this);
25470         
25471         
25472         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25473             if (w.indexOf(tag) > -1) {
25474                 return;
25475             }
25476             this.black.push(tag);
25477             
25478         }, this);
25479         
25480         Roo.each(b, function(tag) {
25481             if (w.indexOf(tag) > -1) {
25482                 return;
25483             }
25484             if (this.black.indexOf(tag) > -1) {
25485                 return;
25486             }
25487             this.black.push(tag);
25488             
25489         }, this);
25490         
25491         
25492         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25493         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25494         
25495         this.cwhite = [];
25496         this.cblack = [];
25497         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25498             if (b.indexOf(tag) > -1) {
25499                 return;
25500             }
25501             this.cwhite.push(tag);
25502             
25503         }, this);
25504         
25505         Roo.each(w, function(tag) {
25506             if (b.indexOf(tag) > -1) {
25507                 return;
25508             }
25509             if (this.cwhite.indexOf(tag) > -1) {
25510                 return;
25511             }
25512             this.cwhite.push(tag);
25513             
25514         }, this);
25515         
25516         
25517         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25518             if (w.indexOf(tag) > -1) {
25519                 return;
25520             }
25521             this.cblack.push(tag);
25522             
25523         }, this);
25524         
25525         Roo.each(b, function(tag) {
25526             if (w.indexOf(tag) > -1) {
25527                 return;
25528             }
25529             if (this.cblack.indexOf(tag) > -1) {
25530                 return;
25531             }
25532             this.cblack.push(tag);
25533             
25534         }, this);
25535     },
25536     
25537     setStylesheets : function(stylesheets)
25538     {
25539         if(typeof(stylesheets) == 'string'){
25540             Roo.get(this.iframe.contentDocument.head).createChild({
25541                 tag : 'link',
25542                 rel : 'stylesheet',
25543                 type : 'text/css',
25544                 href : stylesheets
25545             });
25546             
25547             return;
25548         }
25549         var _this = this;
25550      
25551         Roo.each(stylesheets, function(s) {
25552             if(!s.length){
25553                 return;
25554             }
25555             
25556             Roo.get(_this.iframe.contentDocument.head).createChild({
25557                 tag : 'link',
25558                 rel : 'stylesheet',
25559                 type : 'text/css',
25560                 href : s
25561             });
25562         });
25563
25564         
25565     },
25566     
25567     removeStylesheets : function()
25568     {
25569         var _this = this;
25570         
25571         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25572             s.remove();
25573         });
25574     },
25575     
25576     setStyle : function(style)
25577     {
25578         Roo.get(this.iframe.contentDocument.head).createChild({
25579             tag : 'style',
25580             type : 'text/css',
25581             html : style
25582         });
25583
25584         return;
25585     }
25586     
25587     // hide stuff that is not compatible
25588     /**
25589      * @event blur
25590      * @hide
25591      */
25592     /**
25593      * @event change
25594      * @hide
25595      */
25596     /**
25597      * @event focus
25598      * @hide
25599      */
25600     /**
25601      * @event specialkey
25602      * @hide
25603      */
25604     /**
25605      * @cfg {String} fieldClass @hide
25606      */
25607     /**
25608      * @cfg {String} focusClass @hide
25609      */
25610     /**
25611      * @cfg {String} autoCreate @hide
25612      */
25613     /**
25614      * @cfg {String} inputType @hide
25615      */
25616     /**
25617      * @cfg {String} invalidClass @hide
25618      */
25619     /**
25620      * @cfg {String} invalidText @hide
25621      */
25622     /**
25623      * @cfg {String} msgFx @hide
25624      */
25625     /**
25626      * @cfg {String} validateOnBlur @hide
25627      */
25628 });
25629
25630 Roo.HtmlEditorCore.white = [
25631         'area', 'br', 'img', 'input', 'hr', 'wbr',
25632         
25633        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25634        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25635        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25636        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25637        'table',   'ul',         'xmp', 
25638        
25639        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25640       'thead',   'tr', 
25641      
25642       'dir', 'menu', 'ol', 'ul', 'dl',
25643        
25644       'embed',  'object'
25645 ];
25646
25647
25648 Roo.HtmlEditorCore.black = [
25649     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25650         'applet', // 
25651         'base',   'basefont', 'bgsound', 'blink',  'body', 
25652         'frame',  'frameset', 'head',    'html',   'ilayer', 
25653         'iframe', 'layer',  'link',     'meta',    'object',   
25654         'script', 'style' ,'title',  'xml' // clean later..
25655 ];
25656 Roo.HtmlEditorCore.clean = [
25657     'script', 'style', 'title', 'xml'
25658 ];
25659 Roo.HtmlEditorCore.remove = [
25660     'font'
25661 ];
25662 // attributes..
25663
25664 Roo.HtmlEditorCore.ablack = [
25665     'on'
25666 ];
25667     
25668 Roo.HtmlEditorCore.aclean = [ 
25669     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25670 ];
25671
25672 // protocols..
25673 Roo.HtmlEditorCore.pwhite= [
25674         'http',  'https',  'mailto'
25675 ];
25676
25677 // white listed style attributes.
25678 Roo.HtmlEditorCore.cwhite= [
25679       //  'text-align', /// default is to allow most things..
25680       
25681          
25682 //        'font-size'//??
25683 ];
25684
25685 // black listed style attributes.
25686 Roo.HtmlEditorCore.cblack= [
25687       //  'font-size' -- this can be set by the project 
25688 ];
25689
25690
25691 Roo.HtmlEditorCore.swapCodes   =[ 
25692     [    8211, "--" ], 
25693     [    8212, "--" ], 
25694     [    8216,  "'" ],  
25695     [    8217, "'" ],  
25696     [    8220, '"' ],  
25697     [    8221, '"' ],  
25698     [    8226, "*" ],  
25699     [    8230, "..." ]
25700 ]; 
25701
25702     /*
25703  * - LGPL
25704  *
25705  * HtmlEditor
25706  * 
25707  */
25708
25709 /**
25710  * @class Roo.bootstrap.HtmlEditor
25711  * @extends Roo.bootstrap.TextArea
25712  * Bootstrap HtmlEditor class
25713
25714  * @constructor
25715  * Create a new HtmlEditor
25716  * @param {Object} config The config object
25717  */
25718
25719 Roo.bootstrap.HtmlEditor = function(config){
25720     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25721     if (!this.toolbars) {
25722         this.toolbars = [];
25723     }
25724     
25725     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25726     this.addEvents({
25727             /**
25728              * @event initialize
25729              * Fires when the editor is fully initialized (including the iframe)
25730              * @param {HtmlEditor} this
25731              */
25732             initialize: true,
25733             /**
25734              * @event activate
25735              * Fires when the editor is first receives the focus. Any insertion must wait
25736              * until after this event.
25737              * @param {HtmlEditor} this
25738              */
25739             activate: true,
25740              /**
25741              * @event beforesync
25742              * Fires before the textarea is updated with content from the editor iframe. Return false
25743              * to cancel the sync.
25744              * @param {HtmlEditor} this
25745              * @param {String} html
25746              */
25747             beforesync: true,
25748              /**
25749              * @event beforepush
25750              * Fires before the iframe editor is updated with content from the textarea. Return false
25751              * to cancel the push.
25752              * @param {HtmlEditor} this
25753              * @param {String} html
25754              */
25755             beforepush: true,
25756              /**
25757              * @event sync
25758              * Fires when the textarea is updated with content from the editor iframe.
25759              * @param {HtmlEditor} this
25760              * @param {String} html
25761              */
25762             sync: true,
25763              /**
25764              * @event push
25765              * Fires when the iframe editor is updated with content from the textarea.
25766              * @param {HtmlEditor} this
25767              * @param {String} html
25768              */
25769             push: true,
25770              /**
25771              * @event editmodechange
25772              * Fires when the editor switches edit modes
25773              * @param {HtmlEditor} this
25774              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25775              */
25776             editmodechange: true,
25777             /**
25778              * @event editorevent
25779              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25780              * @param {HtmlEditor} this
25781              */
25782             editorevent: true,
25783             /**
25784              * @event firstfocus
25785              * Fires when on first focus - needed by toolbars..
25786              * @param {HtmlEditor} this
25787              */
25788             firstfocus: true,
25789             /**
25790              * @event autosave
25791              * Auto save the htmlEditor value as a file into Events
25792              * @param {HtmlEditor} this
25793              */
25794             autosave: true,
25795             /**
25796              * @event savedpreview
25797              * preview the saved version of htmlEditor
25798              * @param {HtmlEditor} this
25799              */
25800             savedpreview: true
25801         });
25802 };
25803
25804
25805 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25806     
25807     
25808       /**
25809      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25810      */
25811     toolbars : false,
25812     
25813      /**
25814     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25815     */
25816     btns : [],
25817    
25818      /**
25819      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25820      *                        Roo.resizable.
25821      */
25822     resizable : false,
25823      /**
25824      * @cfg {Number} height (in pixels)
25825      */   
25826     height: 300,
25827    /**
25828      * @cfg {Number} width (in pixels)
25829      */   
25830     width: false,
25831     
25832     /**
25833      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25834      * 
25835      */
25836     stylesheets: false,
25837     
25838     // id of frame..
25839     frameId: false,
25840     
25841     // private properties
25842     validationEvent : false,
25843     deferHeight: true,
25844     initialized : false,
25845     activated : false,
25846     
25847     onFocus : Roo.emptyFn,
25848     iframePad:3,
25849     hideMode:'offsets',
25850     
25851     tbContainer : false,
25852     
25853     bodyCls : '',
25854     
25855     toolbarContainer :function() {
25856         return this.wrap.select('.x-html-editor-tb',true).first();
25857     },
25858
25859     /**
25860      * Protected method that will not generally be called directly. It
25861      * is called when the editor creates its toolbar. Override this method if you need to
25862      * add custom toolbar buttons.
25863      * @param {HtmlEditor} editor
25864      */
25865     createToolbar : function(){
25866         Roo.log('renewing');
25867         Roo.log("create toolbars");
25868         
25869         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25870         this.toolbars[0].render(this.toolbarContainer());
25871         
25872         return;
25873         
25874 //        if (!editor.toolbars || !editor.toolbars.length) {
25875 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25876 //        }
25877 //        
25878 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25879 //            editor.toolbars[i] = Roo.factory(
25880 //                    typeof(editor.toolbars[i]) == 'string' ?
25881 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25882 //                Roo.bootstrap.HtmlEditor);
25883 //            editor.toolbars[i].init(editor);
25884 //        }
25885     },
25886
25887      
25888     // private
25889     onRender : function(ct, position)
25890     {
25891        // Roo.log("Call onRender: " + this.xtype);
25892         var _t = this;
25893         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25894       
25895         this.wrap = this.inputEl().wrap({
25896             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25897         });
25898         
25899         this.editorcore.onRender(ct, position);
25900          
25901         if (this.resizable) {
25902             this.resizeEl = new Roo.Resizable(this.wrap, {
25903                 pinned : true,
25904                 wrap: true,
25905                 dynamic : true,
25906                 minHeight : this.height,
25907                 height: this.height,
25908                 handles : this.resizable,
25909                 width: this.width,
25910                 listeners : {
25911                     resize : function(r, w, h) {
25912                         _t.onResize(w,h); // -something
25913                     }
25914                 }
25915             });
25916             
25917         }
25918         this.createToolbar(this);
25919        
25920         
25921         if(!this.width && this.resizable){
25922             this.setSize(this.wrap.getSize());
25923         }
25924         if (this.resizeEl) {
25925             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25926             // should trigger onReize..
25927         }
25928         
25929     },
25930
25931     // private
25932     onResize : function(w, h)
25933     {
25934         Roo.log('resize: ' +w + ',' + h );
25935         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25936         var ew = false;
25937         var eh = false;
25938         
25939         if(this.inputEl() ){
25940             if(typeof w == 'number'){
25941                 var aw = w - this.wrap.getFrameWidth('lr');
25942                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25943                 ew = aw;
25944             }
25945             if(typeof h == 'number'){
25946                  var tbh = -11;  // fixme it needs to tool bar size!
25947                 for (var i =0; i < this.toolbars.length;i++) {
25948                     // fixme - ask toolbars for heights?
25949                     tbh += this.toolbars[i].el.getHeight();
25950                     //if (this.toolbars[i].footer) {
25951                     //    tbh += this.toolbars[i].footer.el.getHeight();
25952                     //}
25953                 }
25954               
25955                 
25956                 
25957                 
25958                 
25959                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25960                 ah -= 5; // knock a few pixes off for look..
25961                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25962                 var eh = ah;
25963             }
25964         }
25965         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25966         this.editorcore.onResize(ew,eh);
25967         
25968     },
25969
25970     /**
25971      * Toggles the editor between standard and source edit mode.
25972      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25973      */
25974     toggleSourceEdit : function(sourceEditMode)
25975     {
25976         this.editorcore.toggleSourceEdit(sourceEditMode);
25977         
25978         if(this.editorcore.sourceEditMode){
25979             Roo.log('editor - showing textarea');
25980             
25981 //            Roo.log('in');
25982 //            Roo.log(this.syncValue());
25983             this.syncValue();
25984             this.inputEl().removeClass(['hide', 'x-hidden']);
25985             this.inputEl().dom.removeAttribute('tabIndex');
25986             this.inputEl().focus();
25987         }else{
25988             Roo.log('editor - hiding textarea');
25989 //            Roo.log('out')
25990 //            Roo.log(this.pushValue()); 
25991             this.pushValue();
25992             
25993             this.inputEl().addClass(['hide', 'x-hidden']);
25994             this.inputEl().dom.setAttribute('tabIndex', -1);
25995             //this.deferFocus();
25996         }
25997          
25998         if(this.resizable){
25999             this.setSize(this.wrap.getSize());
26000         }
26001         
26002         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26003     },
26004  
26005     // private (for BoxComponent)
26006     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26007
26008     // private (for BoxComponent)
26009     getResizeEl : function(){
26010         return this.wrap;
26011     },
26012
26013     // private (for BoxComponent)
26014     getPositionEl : function(){
26015         return this.wrap;
26016     },
26017
26018     // private
26019     initEvents : function(){
26020         this.originalValue = this.getValue();
26021     },
26022
26023 //    /**
26024 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26025 //     * @method
26026 //     */
26027 //    markInvalid : Roo.emptyFn,
26028 //    /**
26029 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26030 //     * @method
26031 //     */
26032 //    clearInvalid : Roo.emptyFn,
26033
26034     setValue : function(v){
26035         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26036         this.editorcore.pushValue();
26037     },
26038
26039      
26040     // private
26041     deferFocus : function(){
26042         this.focus.defer(10, this);
26043     },
26044
26045     // doc'ed in Field
26046     focus : function(){
26047         this.editorcore.focus();
26048         
26049     },
26050       
26051
26052     // private
26053     onDestroy : function(){
26054         
26055         
26056         
26057         if(this.rendered){
26058             
26059             for (var i =0; i < this.toolbars.length;i++) {
26060                 // fixme - ask toolbars for heights?
26061                 this.toolbars[i].onDestroy();
26062             }
26063             
26064             this.wrap.dom.innerHTML = '';
26065             this.wrap.remove();
26066         }
26067     },
26068
26069     // private
26070     onFirstFocus : function(){
26071         //Roo.log("onFirstFocus");
26072         this.editorcore.onFirstFocus();
26073          for (var i =0; i < this.toolbars.length;i++) {
26074             this.toolbars[i].onFirstFocus();
26075         }
26076         
26077     },
26078     
26079     // private
26080     syncValue : function()
26081     {   
26082         this.editorcore.syncValue();
26083     },
26084     
26085     pushValue : function()
26086     {   
26087         this.editorcore.pushValue();
26088     }
26089      
26090     
26091     // hide stuff that is not compatible
26092     /**
26093      * @event blur
26094      * @hide
26095      */
26096     /**
26097      * @event change
26098      * @hide
26099      */
26100     /**
26101      * @event focus
26102      * @hide
26103      */
26104     /**
26105      * @event specialkey
26106      * @hide
26107      */
26108     /**
26109      * @cfg {String} fieldClass @hide
26110      */
26111     /**
26112      * @cfg {String} focusClass @hide
26113      */
26114     /**
26115      * @cfg {String} autoCreate @hide
26116      */
26117     /**
26118      * @cfg {String} inputType @hide
26119      */
26120      
26121     /**
26122      * @cfg {String} invalidText @hide
26123      */
26124     /**
26125      * @cfg {String} msgFx @hide
26126      */
26127     /**
26128      * @cfg {String} validateOnBlur @hide
26129      */
26130 });
26131  
26132     
26133    
26134    
26135    
26136       
26137 Roo.namespace('Roo.bootstrap.htmleditor');
26138 /**
26139  * @class Roo.bootstrap.HtmlEditorToolbar1
26140  * Basic Toolbar
26141  * 
26142  * @example
26143  * Usage:
26144  *
26145  new Roo.bootstrap.HtmlEditor({
26146     ....
26147     toolbars : [
26148         new Roo.bootstrap.HtmlEditorToolbar1({
26149             disable : { fonts: 1 , format: 1, ..., ... , ...],
26150             btns : [ .... ]
26151         })
26152     }
26153      
26154  * 
26155  * @cfg {Object} disable List of elements to disable..
26156  * @cfg {Array} btns List of additional buttons.
26157  * 
26158  * 
26159  * NEEDS Extra CSS? 
26160  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26161  */
26162  
26163 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26164 {
26165     
26166     Roo.apply(this, config);
26167     
26168     // default disabled, based on 'good practice'..
26169     this.disable = this.disable || {};
26170     Roo.applyIf(this.disable, {
26171         fontSize : true,
26172         colors : true,
26173         specialElements : true
26174     });
26175     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26176     
26177     this.editor = config.editor;
26178     this.editorcore = config.editor.editorcore;
26179     
26180     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26181     
26182     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26183     // dont call parent... till later.
26184 }
26185 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26186      
26187     bar : true,
26188     
26189     editor : false,
26190     editorcore : false,
26191     
26192     
26193     formats : [
26194         "p" ,  
26195         "h1","h2","h3","h4","h5","h6", 
26196         "pre", "code", 
26197         "abbr", "acronym", "address", "cite", "samp", "var",
26198         'div','span'
26199     ],
26200     
26201     onRender : function(ct, position)
26202     {
26203        // Roo.log("Call onRender: " + this.xtype);
26204         
26205        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26206        Roo.log(this.el);
26207        this.el.dom.style.marginBottom = '0';
26208        var _this = this;
26209        var editorcore = this.editorcore;
26210        var editor= this.editor;
26211        
26212        var children = [];
26213        var btn = function(id,cmd , toggle, handler, html){
26214        
26215             var  event = toggle ? 'toggle' : 'click';
26216        
26217             var a = {
26218                 size : 'sm',
26219                 xtype: 'Button',
26220                 xns: Roo.bootstrap,
26221                 //glyphicon : id,
26222                 fa: id,
26223                 cmd : id || cmd,
26224                 enableToggle:toggle !== false,
26225                 html : html || '',
26226                 pressed : toggle ? false : null,
26227                 listeners : {}
26228             };
26229             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26230                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26231             };
26232             children.push(a);
26233             return a;
26234        }
26235        
26236     //    var cb_box = function...
26237         
26238         var style = {
26239                 xtype: 'Button',
26240                 size : 'sm',
26241                 xns: Roo.bootstrap,
26242                 fa : 'font',
26243                 //html : 'submit'
26244                 menu : {
26245                     xtype: 'Menu',
26246                     xns: Roo.bootstrap,
26247                     items:  []
26248                 }
26249         };
26250         Roo.each(this.formats, function(f) {
26251             style.menu.items.push({
26252                 xtype :'MenuItem',
26253                 xns: Roo.bootstrap,
26254                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26255                 tagname : f,
26256                 listeners : {
26257                     click : function()
26258                     {
26259                         editorcore.insertTag(this.tagname);
26260                         editor.focus();
26261                     }
26262                 }
26263                 
26264             });
26265         });
26266         children.push(style);   
26267         
26268         btn('bold',false,true);
26269         btn('italic',false,true);
26270         btn('align-left', 'justifyleft',true);
26271         btn('align-center', 'justifycenter',true);
26272         btn('align-right' , 'justifyright',true);
26273         btn('link', false, false, function(btn) {
26274             //Roo.log("create link?");
26275             var url = prompt(this.createLinkText, this.defaultLinkValue);
26276             if(url && url != 'http:/'+'/'){
26277                 this.editorcore.relayCmd('createlink', url);
26278             }
26279         }),
26280         btn('list','insertunorderedlist',true);
26281         btn('pencil', false,true, function(btn){
26282                 Roo.log(this);
26283                 this.toggleSourceEdit(btn.pressed);
26284         });
26285         
26286         if (this.editor.btns.length > 0) {
26287             for (var i = 0; i<this.editor.btns.length; i++) {
26288                 children.push(this.editor.btns[i]);
26289             }
26290         }
26291         
26292         /*
26293         var cog = {
26294                 xtype: 'Button',
26295                 size : 'sm',
26296                 xns: Roo.bootstrap,
26297                 glyphicon : 'cog',
26298                 //html : 'submit'
26299                 menu : {
26300                     xtype: 'Menu',
26301                     xns: Roo.bootstrap,
26302                     items:  []
26303                 }
26304         };
26305         
26306         cog.menu.items.push({
26307             xtype :'MenuItem',
26308             xns: Roo.bootstrap,
26309             html : Clean styles,
26310             tagname : f,
26311             listeners : {
26312                 click : function()
26313                 {
26314                     editorcore.insertTag(this.tagname);
26315                     editor.focus();
26316                 }
26317             }
26318             
26319         });
26320        */
26321         
26322          
26323        this.xtype = 'NavSimplebar';
26324         
26325         for(var i=0;i< children.length;i++) {
26326             
26327             this.buttons.add(this.addxtypeChild(children[i]));
26328             
26329         }
26330         
26331         editor.on('editorevent', this.updateToolbar, this);
26332     },
26333     onBtnClick : function(id)
26334     {
26335        this.editorcore.relayCmd(id);
26336        this.editorcore.focus();
26337     },
26338     
26339     /**
26340      * Protected method that will not generally be called directly. It triggers
26341      * a toolbar update by reading the markup state of the current selection in the editor.
26342      */
26343     updateToolbar: function(){
26344
26345         if(!this.editorcore.activated){
26346             this.editor.onFirstFocus(); // is this neeed?
26347             return;
26348         }
26349
26350         var btns = this.buttons; 
26351         var doc = this.editorcore.doc;
26352         btns.get('bold').setActive(doc.queryCommandState('bold'));
26353         btns.get('italic').setActive(doc.queryCommandState('italic'));
26354         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26355         
26356         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26357         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26358         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26359         
26360         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26361         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26362          /*
26363         
26364         var ans = this.editorcore.getAllAncestors();
26365         if (this.formatCombo) {
26366             
26367             
26368             var store = this.formatCombo.store;
26369             this.formatCombo.setValue("");
26370             for (var i =0; i < ans.length;i++) {
26371                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26372                     // select it..
26373                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26374                     break;
26375                 }
26376             }
26377         }
26378         
26379         
26380         
26381         // hides menus... - so this cant be on a menu...
26382         Roo.bootstrap.MenuMgr.hideAll();
26383         */
26384         Roo.bootstrap.MenuMgr.hideAll();
26385         //this.editorsyncValue();
26386     },
26387     onFirstFocus: function() {
26388         this.buttons.each(function(item){
26389            item.enable();
26390         });
26391     },
26392     toggleSourceEdit : function(sourceEditMode){
26393         
26394           
26395         if(sourceEditMode){
26396             Roo.log("disabling buttons");
26397            this.buttons.each( function(item){
26398                 if(item.cmd != 'pencil'){
26399                     item.disable();
26400                 }
26401             });
26402           
26403         }else{
26404             Roo.log("enabling buttons");
26405             if(this.editorcore.initialized){
26406                 this.buttons.each( function(item){
26407                     item.enable();
26408                 });
26409             }
26410             
26411         }
26412         Roo.log("calling toggole on editor");
26413         // tell the editor that it's been pressed..
26414         this.editor.toggleSourceEdit(sourceEditMode);
26415        
26416     }
26417 });
26418
26419
26420
26421
26422  
26423 /*
26424  * - LGPL
26425  */
26426
26427 /**
26428  * @class Roo.bootstrap.Markdown
26429  * @extends Roo.bootstrap.TextArea
26430  * Bootstrap Showdown editable area
26431  * @cfg {string} content
26432  * 
26433  * @constructor
26434  * Create a new Showdown
26435  */
26436
26437 Roo.bootstrap.Markdown = function(config){
26438     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26439    
26440 };
26441
26442 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26443     
26444     editing :false,
26445     
26446     initEvents : function()
26447     {
26448         
26449         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26450         this.markdownEl = this.el.createChild({
26451             cls : 'roo-markdown-area'
26452         });
26453         this.inputEl().addClass('d-none');
26454         if (this.getValue() == '') {
26455             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26456             
26457         } else {
26458             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26459         }
26460         this.markdownEl.on('click', this.toggleTextEdit, this);
26461         this.on('blur', this.toggleTextEdit, this);
26462         this.on('specialkey', this.resizeTextArea, this);
26463     },
26464     
26465     toggleTextEdit : function()
26466     {
26467         var sh = this.markdownEl.getHeight();
26468         this.inputEl().addClass('d-none');
26469         this.markdownEl.addClass('d-none');
26470         if (!this.editing) {
26471             // show editor?
26472             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26473             this.inputEl().removeClass('d-none');
26474             this.inputEl().focus();
26475             this.editing = true;
26476             return;
26477         }
26478         // show showdown...
26479         this.updateMarkdown();
26480         this.markdownEl.removeClass('d-none');
26481         this.editing = false;
26482         return;
26483     },
26484     updateMarkdown : function()
26485     {
26486         if (this.getValue() == '') {
26487             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26488             return;
26489         }
26490  
26491         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26492     },
26493     
26494     resizeTextArea: function () {
26495         
26496         var sh = 100;
26497         Roo.log([sh, this.getValue().split("\n").length * 30]);
26498         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26499     },
26500     setValue : function(val)
26501     {
26502         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26503         if (!this.editing) {
26504             this.updateMarkdown();
26505         }
26506         
26507     },
26508     focus : function()
26509     {
26510         if (!this.editing) {
26511             this.toggleTextEdit();
26512         }
26513         
26514     }
26515
26516
26517 });
26518 /**
26519  * @class Roo.bootstrap.Table.AbstractSelectionModel
26520  * @extends Roo.util.Observable
26521  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26522  * implemented by descendant classes.  This class should not be directly instantiated.
26523  * @constructor
26524  */
26525 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26526     this.locked = false;
26527     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26528 };
26529
26530
26531 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26532     /** @ignore Called by the grid automatically. Do not call directly. */
26533     init : function(grid){
26534         this.grid = grid;
26535         this.initEvents();
26536     },
26537
26538     /**
26539      * Locks the selections.
26540      */
26541     lock : function(){
26542         this.locked = true;
26543     },
26544
26545     /**
26546      * Unlocks the selections.
26547      */
26548     unlock : function(){
26549         this.locked = false;
26550     },
26551
26552     /**
26553      * Returns true if the selections are locked.
26554      * @return {Boolean}
26555      */
26556     isLocked : function(){
26557         return this.locked;
26558     },
26559     
26560     
26561     initEvents : function ()
26562     {
26563         
26564     }
26565 });
26566 /**
26567  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26568  * @class Roo.bootstrap.Table.RowSelectionModel
26569  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26570  * It supports multiple selections and keyboard selection/navigation. 
26571  * @constructor
26572  * @param {Object} config
26573  */
26574
26575 Roo.bootstrap.Table.RowSelectionModel = function(config){
26576     Roo.apply(this, config);
26577     this.selections = new Roo.util.MixedCollection(false, function(o){
26578         return o.id;
26579     });
26580
26581     this.last = false;
26582     this.lastActive = false;
26583
26584     this.addEvents({
26585         /**
26586              * @event selectionchange
26587              * Fires when the selection changes
26588              * @param {SelectionModel} this
26589              */
26590             "selectionchange" : true,
26591         /**
26592              * @event afterselectionchange
26593              * Fires after the selection changes (eg. by key press or clicking)
26594              * @param {SelectionModel} this
26595              */
26596             "afterselectionchange" : true,
26597         /**
26598              * @event beforerowselect
26599              * Fires when a row is selected being selected, return false to cancel.
26600              * @param {SelectionModel} this
26601              * @param {Number} rowIndex The selected index
26602              * @param {Boolean} keepExisting False if other selections will be cleared
26603              */
26604             "beforerowselect" : true,
26605         /**
26606              * @event rowselect
26607              * Fires when a row is selected.
26608              * @param {SelectionModel} this
26609              * @param {Number} rowIndex The selected index
26610              * @param {Roo.data.Record} r The record
26611              */
26612             "rowselect" : true,
26613         /**
26614              * @event rowdeselect
26615              * Fires when a row is deselected.
26616              * @param {SelectionModel} this
26617              * @param {Number} rowIndex The selected index
26618              */
26619         "rowdeselect" : true
26620     });
26621     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26622     this.locked = false;
26623  };
26624
26625 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26626     /**
26627      * @cfg {Boolean} singleSelect
26628      * True to allow selection of only one row at a time (defaults to false)
26629      */
26630     singleSelect : false,
26631
26632     // private
26633     initEvents : function()
26634     {
26635
26636         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26637         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26638         //}else{ // allow click to work like normal
26639          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26640         //}
26641         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26642         this.grid.on("rowclick", this.handleMouseDown, this);
26643         
26644         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26645             "up" : function(e){
26646                 if(!e.shiftKey){
26647                     this.selectPrevious(e.shiftKey);
26648                 }else if(this.last !== false && this.lastActive !== false){
26649                     var last = this.last;
26650                     this.selectRange(this.last,  this.lastActive-1);
26651                     this.grid.getView().focusRow(this.lastActive);
26652                     if(last !== false){
26653                         this.last = last;
26654                     }
26655                 }else{
26656                     this.selectFirstRow();
26657                 }
26658                 this.fireEvent("afterselectionchange", this);
26659             },
26660             "down" : function(e){
26661                 if(!e.shiftKey){
26662                     this.selectNext(e.shiftKey);
26663                 }else if(this.last !== false && this.lastActive !== false){
26664                     var last = this.last;
26665                     this.selectRange(this.last,  this.lastActive+1);
26666                     this.grid.getView().focusRow(this.lastActive);
26667                     if(last !== false){
26668                         this.last = last;
26669                     }
26670                 }else{
26671                     this.selectFirstRow();
26672                 }
26673                 this.fireEvent("afterselectionchange", this);
26674             },
26675             scope: this
26676         });
26677         this.grid.store.on('load', function(){
26678             this.selections.clear();
26679         },this);
26680         /*
26681         var view = this.grid.view;
26682         view.on("refresh", this.onRefresh, this);
26683         view.on("rowupdated", this.onRowUpdated, this);
26684         view.on("rowremoved", this.onRemove, this);
26685         */
26686     },
26687
26688     // private
26689     onRefresh : function()
26690     {
26691         var ds = this.grid.store, i, v = this.grid.view;
26692         var s = this.selections;
26693         s.each(function(r){
26694             if((i = ds.indexOfId(r.id)) != -1){
26695                 v.onRowSelect(i);
26696             }else{
26697                 s.remove(r);
26698             }
26699         });
26700     },
26701
26702     // private
26703     onRemove : function(v, index, r){
26704         this.selections.remove(r);
26705     },
26706
26707     // private
26708     onRowUpdated : function(v, index, r){
26709         if(this.isSelected(r)){
26710             v.onRowSelect(index);
26711         }
26712     },
26713
26714     /**
26715      * Select records.
26716      * @param {Array} records The records to select
26717      * @param {Boolean} keepExisting (optional) True to keep existing selections
26718      */
26719     selectRecords : function(records, keepExisting)
26720     {
26721         if(!keepExisting){
26722             this.clearSelections();
26723         }
26724             var ds = this.grid.store;
26725         for(var i = 0, len = records.length; i < len; i++){
26726             this.selectRow(ds.indexOf(records[i]), true);
26727         }
26728     },
26729
26730     /**
26731      * Gets the number of selected rows.
26732      * @return {Number}
26733      */
26734     getCount : function(){
26735         return this.selections.length;
26736     },
26737
26738     /**
26739      * Selects the first row in the grid.
26740      */
26741     selectFirstRow : function(){
26742         this.selectRow(0);
26743     },
26744
26745     /**
26746      * Select the last row.
26747      * @param {Boolean} keepExisting (optional) True to keep existing selections
26748      */
26749     selectLastRow : function(keepExisting){
26750         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26751         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26752     },
26753
26754     /**
26755      * Selects the row immediately following the last selected row.
26756      * @param {Boolean} keepExisting (optional) True to keep existing selections
26757      */
26758     selectNext : function(keepExisting)
26759     {
26760             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26761             this.selectRow(this.last+1, keepExisting);
26762             this.grid.getView().focusRow(this.last);
26763         }
26764     },
26765
26766     /**
26767      * Selects the row that precedes the last selected row.
26768      * @param {Boolean} keepExisting (optional) True to keep existing selections
26769      */
26770     selectPrevious : function(keepExisting){
26771         if(this.last){
26772             this.selectRow(this.last-1, keepExisting);
26773             this.grid.getView().focusRow(this.last);
26774         }
26775     },
26776
26777     /**
26778      * Returns the selected records
26779      * @return {Array} Array of selected records
26780      */
26781     getSelections : function(){
26782         return [].concat(this.selections.items);
26783     },
26784
26785     /**
26786      * Returns the first selected record.
26787      * @return {Record}
26788      */
26789     getSelected : function(){
26790         return this.selections.itemAt(0);
26791     },
26792
26793
26794     /**
26795      * Clears all selections.
26796      */
26797     clearSelections : function(fast)
26798     {
26799         if(this.locked) {
26800             return;
26801         }
26802         if(fast !== true){
26803                 var ds = this.grid.store;
26804             var s = this.selections;
26805             s.each(function(r){
26806                 this.deselectRow(ds.indexOfId(r.id));
26807             }, this);
26808             s.clear();
26809         }else{
26810             this.selections.clear();
26811         }
26812         this.last = false;
26813     },
26814
26815
26816     /**
26817      * Selects all rows.
26818      */
26819     selectAll : function(){
26820         if(this.locked) {
26821             return;
26822         }
26823         this.selections.clear();
26824         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26825             this.selectRow(i, true);
26826         }
26827     },
26828
26829     /**
26830      * Returns True if there is a selection.
26831      * @return {Boolean}
26832      */
26833     hasSelection : function(){
26834         return this.selections.length > 0;
26835     },
26836
26837     /**
26838      * Returns True if the specified row is selected.
26839      * @param {Number/Record} record The record or index of the record to check
26840      * @return {Boolean}
26841      */
26842     isSelected : function(index){
26843             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26844         return (r && this.selections.key(r.id) ? true : false);
26845     },
26846
26847     /**
26848      * Returns True if the specified record id is selected.
26849      * @param {String} id The id of record to check
26850      * @return {Boolean}
26851      */
26852     isIdSelected : function(id){
26853         return (this.selections.key(id) ? true : false);
26854     },
26855
26856
26857     // private
26858     handleMouseDBClick : function(e, t){
26859         
26860     },
26861     // private
26862     handleMouseDown : function(e, t)
26863     {
26864             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26865         if(this.isLocked() || rowIndex < 0 ){
26866             return;
26867         };
26868         if(e.shiftKey && this.last !== false){
26869             var last = this.last;
26870             this.selectRange(last, rowIndex, e.ctrlKey);
26871             this.last = last; // reset the last
26872             t.focus();
26873     
26874         }else{
26875             var isSelected = this.isSelected(rowIndex);
26876             //Roo.log("select row:" + rowIndex);
26877             if(isSelected){
26878                 this.deselectRow(rowIndex);
26879             } else {
26880                         this.selectRow(rowIndex, true);
26881             }
26882     
26883             /*
26884                 if(e.button !== 0 && isSelected){
26885                 alert('rowIndex 2: ' + rowIndex);
26886                     view.focusRow(rowIndex);
26887                 }else if(e.ctrlKey && isSelected){
26888                     this.deselectRow(rowIndex);
26889                 }else if(!isSelected){
26890                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26891                     view.focusRow(rowIndex);
26892                 }
26893             */
26894         }
26895         this.fireEvent("afterselectionchange", this);
26896     },
26897     // private
26898     handleDragableRowClick :  function(grid, rowIndex, e) 
26899     {
26900         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26901             this.selectRow(rowIndex, false);
26902             grid.view.focusRow(rowIndex);
26903              this.fireEvent("afterselectionchange", this);
26904         }
26905     },
26906     
26907     /**
26908      * Selects multiple rows.
26909      * @param {Array} rows Array of the indexes of the row to select
26910      * @param {Boolean} keepExisting (optional) True to keep existing selections
26911      */
26912     selectRows : function(rows, keepExisting){
26913         if(!keepExisting){
26914             this.clearSelections();
26915         }
26916         for(var i = 0, len = rows.length; i < len; i++){
26917             this.selectRow(rows[i], true);
26918         }
26919     },
26920
26921     /**
26922      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26923      * @param {Number} startRow The index of the first row in the range
26924      * @param {Number} endRow The index of the last row in the range
26925      * @param {Boolean} keepExisting (optional) True to retain existing selections
26926      */
26927     selectRange : function(startRow, endRow, keepExisting){
26928         if(this.locked) {
26929             return;
26930         }
26931         if(!keepExisting){
26932             this.clearSelections();
26933         }
26934         if(startRow <= endRow){
26935             for(var i = startRow; i <= endRow; i++){
26936                 this.selectRow(i, true);
26937             }
26938         }else{
26939             for(var i = startRow; i >= endRow; i--){
26940                 this.selectRow(i, true);
26941             }
26942         }
26943     },
26944
26945     /**
26946      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26947      * @param {Number} startRow The index of the first row in the range
26948      * @param {Number} endRow The index of the last row in the range
26949      */
26950     deselectRange : function(startRow, endRow, preventViewNotify){
26951         if(this.locked) {
26952             return;
26953         }
26954         for(var i = startRow; i <= endRow; i++){
26955             this.deselectRow(i, preventViewNotify);
26956         }
26957     },
26958
26959     /**
26960      * Selects a row.
26961      * @param {Number} row The index of the row to select
26962      * @param {Boolean} keepExisting (optional) True to keep existing selections
26963      */
26964     selectRow : function(index, keepExisting, preventViewNotify)
26965     {
26966             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26967             return;
26968         }
26969         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26970             if(!keepExisting || this.singleSelect){
26971                 this.clearSelections();
26972             }
26973             
26974             var r = this.grid.store.getAt(index);
26975             //console.log('selectRow - record id :' + r.id);
26976             
26977             this.selections.add(r);
26978             this.last = this.lastActive = index;
26979             if(!preventViewNotify){
26980                 var proxy = new Roo.Element(
26981                                 this.grid.getRowDom(index)
26982                 );
26983                 proxy.addClass('bg-info info');
26984             }
26985             this.fireEvent("rowselect", this, index, r);
26986             this.fireEvent("selectionchange", this);
26987         }
26988     },
26989
26990     /**
26991      * Deselects a row.
26992      * @param {Number} row The index of the row to deselect
26993      */
26994     deselectRow : function(index, preventViewNotify)
26995     {
26996         if(this.locked) {
26997             return;
26998         }
26999         if(this.last == index){
27000             this.last = false;
27001         }
27002         if(this.lastActive == index){
27003             this.lastActive = false;
27004         }
27005         
27006         var r = this.grid.store.getAt(index);
27007         if (!r) {
27008             return;
27009         }
27010         
27011         this.selections.remove(r);
27012         //.console.log('deselectRow - record id :' + r.id);
27013         if(!preventViewNotify){
27014         
27015             var proxy = new Roo.Element(
27016                 this.grid.getRowDom(index)
27017             );
27018             proxy.removeClass('bg-info info');
27019         }
27020         this.fireEvent("rowdeselect", this, index);
27021         this.fireEvent("selectionchange", this);
27022     },
27023
27024     // private
27025     restoreLast : function(){
27026         if(this._last){
27027             this.last = this._last;
27028         }
27029     },
27030
27031     // private
27032     acceptsNav : function(row, col, cm){
27033         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27034     },
27035
27036     // private
27037     onEditorKey : function(field, e){
27038         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27039         if(k == e.TAB){
27040             e.stopEvent();
27041             ed.completeEdit();
27042             if(e.shiftKey){
27043                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27044             }else{
27045                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27046             }
27047         }else if(k == e.ENTER && !e.ctrlKey){
27048             e.stopEvent();
27049             ed.completeEdit();
27050             if(e.shiftKey){
27051                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27052             }else{
27053                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27054             }
27055         }else if(k == e.ESC){
27056             ed.cancelEdit();
27057         }
27058         if(newCell){
27059             g.startEditing(newCell[0], newCell[1]);
27060         }
27061     }
27062 });
27063 /*
27064  * Based on:
27065  * Ext JS Library 1.1.1
27066  * Copyright(c) 2006-2007, Ext JS, LLC.
27067  *
27068  * Originally Released Under LGPL - original licence link has changed is not relivant.
27069  *
27070  * Fork - LGPL
27071  * <script type="text/javascript">
27072  */
27073  
27074 /**
27075  * @class Roo.bootstrap.PagingToolbar
27076  * @extends Roo.bootstrap.NavSimplebar
27077  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27078  * @constructor
27079  * Create a new PagingToolbar
27080  * @param {Object} config The config object
27081  * @param {Roo.data.Store} store
27082  */
27083 Roo.bootstrap.PagingToolbar = function(config)
27084 {
27085     // old args format still supported... - xtype is prefered..
27086         // created from xtype...
27087     
27088     this.ds = config.dataSource;
27089     
27090     if (config.store && !this.ds) {
27091         this.store= Roo.factory(config.store, Roo.data);
27092         this.ds = this.store;
27093         this.ds.xmodule = this.xmodule || false;
27094     }
27095     
27096     this.toolbarItems = [];
27097     if (config.items) {
27098         this.toolbarItems = config.items;
27099     }
27100     
27101     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27102     
27103     this.cursor = 0;
27104     
27105     if (this.ds) { 
27106         this.bind(this.ds);
27107     }
27108     
27109     if (Roo.bootstrap.version == 4) {
27110         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27111     } else {
27112         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27113     }
27114     
27115 };
27116
27117 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27118     /**
27119      * @cfg {Roo.data.Store} dataSource
27120      * The underlying data store providing the paged data
27121      */
27122     /**
27123      * @cfg {String/HTMLElement/Element} container
27124      * container The id or element that will contain the toolbar
27125      */
27126     /**
27127      * @cfg {Boolean} displayInfo
27128      * True to display the displayMsg (defaults to false)
27129      */
27130     /**
27131      * @cfg {Number} pageSize
27132      * The number of records to display per page (defaults to 20)
27133      */
27134     pageSize: 20,
27135     /**
27136      * @cfg {String} displayMsg
27137      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27138      */
27139     displayMsg : 'Displaying {0} - {1} of {2}',
27140     /**
27141      * @cfg {String} emptyMsg
27142      * The message to display when no records are found (defaults to "No data to display")
27143      */
27144     emptyMsg : 'No data to display',
27145     /**
27146      * Customizable piece of the default paging text (defaults to "Page")
27147      * @type String
27148      */
27149     beforePageText : "Page",
27150     /**
27151      * Customizable piece of the default paging text (defaults to "of %0")
27152      * @type String
27153      */
27154     afterPageText : "of {0}",
27155     /**
27156      * Customizable piece of the default paging text (defaults to "First Page")
27157      * @type String
27158      */
27159     firstText : "First Page",
27160     /**
27161      * Customizable piece of the default paging text (defaults to "Previous Page")
27162      * @type String
27163      */
27164     prevText : "Previous Page",
27165     /**
27166      * Customizable piece of the default paging text (defaults to "Next Page")
27167      * @type String
27168      */
27169     nextText : "Next Page",
27170     /**
27171      * Customizable piece of the default paging text (defaults to "Last Page")
27172      * @type String
27173      */
27174     lastText : "Last Page",
27175     /**
27176      * Customizable piece of the default paging text (defaults to "Refresh")
27177      * @type String
27178      */
27179     refreshText : "Refresh",
27180
27181     buttons : false,
27182     // private
27183     onRender : function(ct, position) 
27184     {
27185         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27186         this.navgroup.parentId = this.id;
27187         this.navgroup.onRender(this.el, null);
27188         // add the buttons to the navgroup
27189         
27190         if(this.displayInfo){
27191             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27192             this.displayEl = this.el.select('.x-paging-info', true).first();
27193 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27194 //            this.displayEl = navel.el.select('span',true).first();
27195         }
27196         
27197         var _this = this;
27198         
27199         if(this.buttons){
27200             Roo.each(_this.buttons, function(e){ // this might need to use render????
27201                Roo.factory(e).render(_this.el);
27202             });
27203         }
27204             
27205         Roo.each(_this.toolbarItems, function(e) {
27206             _this.navgroup.addItem(e);
27207         });
27208         
27209         
27210         this.first = this.navgroup.addItem({
27211             tooltip: this.firstText,
27212             cls: "prev btn-outline-secondary",
27213             html : ' <i class="fa fa-step-backward"></i>',
27214             disabled: true,
27215             preventDefault: true,
27216             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27217         });
27218         
27219         this.prev =  this.navgroup.addItem({
27220             tooltip: this.prevText,
27221             cls: "prev btn-outline-secondary",
27222             html : ' <i class="fa fa-backward"></i>',
27223             disabled: true,
27224             preventDefault: true,
27225             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27226         });
27227     //this.addSeparator();
27228         
27229         
27230         var field = this.navgroup.addItem( {
27231             tagtype : 'span',
27232             cls : 'x-paging-position  btn-outline-secondary',
27233              disabled: true,
27234             html : this.beforePageText  +
27235                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27236                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27237          } ); //?? escaped?
27238         
27239         this.field = field.el.select('input', true).first();
27240         this.field.on("keydown", this.onPagingKeydown, this);
27241         this.field.on("focus", function(){this.dom.select();});
27242     
27243     
27244         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27245         //this.field.setHeight(18);
27246         //this.addSeparator();
27247         this.next = this.navgroup.addItem({
27248             tooltip: this.nextText,
27249             cls: "next btn-outline-secondary",
27250             html : ' <i class="fa fa-forward"></i>',
27251             disabled: true,
27252             preventDefault: true,
27253             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27254         });
27255         this.last = this.navgroup.addItem({
27256             tooltip: this.lastText,
27257             html : ' <i class="fa fa-step-forward"></i>',
27258             cls: "next btn-outline-secondary",
27259             disabled: true,
27260             preventDefault: true,
27261             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27262         });
27263     //this.addSeparator();
27264         this.loading = this.navgroup.addItem({
27265             tooltip: this.refreshText,
27266             cls: "btn-outline-secondary",
27267             html : ' <i class="fa fa-refresh"></i>',
27268             preventDefault: true,
27269             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27270         });
27271         
27272     },
27273
27274     // private
27275     updateInfo : function(){
27276         if(this.displayEl){
27277             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27278             var msg = count == 0 ?
27279                 this.emptyMsg :
27280                 String.format(
27281                     this.displayMsg,
27282                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27283                 );
27284             this.displayEl.update(msg);
27285         }
27286     },
27287
27288     // private
27289     onLoad : function(ds, r, o)
27290     {
27291         this.cursor = o.params.start ? o.params.start : 0;
27292         
27293         var d = this.getPageData(),
27294             ap = d.activePage,
27295             ps = d.pages;
27296         
27297         
27298         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27299         this.field.dom.value = ap;
27300         this.first.setDisabled(ap == 1);
27301         this.prev.setDisabled(ap == 1);
27302         this.next.setDisabled(ap == ps);
27303         this.last.setDisabled(ap == ps);
27304         this.loading.enable();
27305         this.updateInfo();
27306     },
27307
27308     // private
27309     getPageData : function(){
27310         var total = this.ds.getTotalCount();
27311         return {
27312             total : total,
27313             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27314             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27315         };
27316     },
27317
27318     // private
27319     onLoadError : function(){
27320         this.loading.enable();
27321     },
27322
27323     // private
27324     onPagingKeydown : function(e){
27325         var k = e.getKey();
27326         var d = this.getPageData();
27327         if(k == e.RETURN){
27328             var v = this.field.dom.value, pageNum;
27329             if(!v || isNaN(pageNum = parseInt(v, 10))){
27330                 this.field.dom.value = d.activePage;
27331                 return;
27332             }
27333             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27334             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27335             e.stopEvent();
27336         }
27337         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))
27338         {
27339           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27340           this.field.dom.value = pageNum;
27341           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27342           e.stopEvent();
27343         }
27344         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27345         {
27346           var v = this.field.dom.value, pageNum; 
27347           var increment = (e.shiftKey) ? 10 : 1;
27348           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27349                 increment *= -1;
27350           }
27351           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27352             this.field.dom.value = d.activePage;
27353             return;
27354           }
27355           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27356           {
27357             this.field.dom.value = parseInt(v, 10) + increment;
27358             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27359             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27360           }
27361           e.stopEvent();
27362         }
27363     },
27364
27365     // private
27366     beforeLoad : function(){
27367         if(this.loading){
27368             this.loading.disable();
27369         }
27370     },
27371
27372     // private
27373     onClick : function(which){
27374         
27375         var ds = this.ds;
27376         if (!ds) {
27377             return;
27378         }
27379         
27380         switch(which){
27381             case "first":
27382                 ds.load({params:{start: 0, limit: this.pageSize}});
27383             break;
27384             case "prev":
27385                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27386             break;
27387             case "next":
27388                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27389             break;
27390             case "last":
27391                 var total = ds.getTotalCount();
27392                 var extra = total % this.pageSize;
27393                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27394                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27395             break;
27396             case "refresh":
27397                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27398             break;
27399         }
27400     },
27401
27402     /**
27403      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27404      * @param {Roo.data.Store} store The data store to unbind
27405      */
27406     unbind : function(ds){
27407         ds.un("beforeload", this.beforeLoad, this);
27408         ds.un("load", this.onLoad, this);
27409         ds.un("loadexception", this.onLoadError, this);
27410         ds.un("remove", this.updateInfo, this);
27411         ds.un("add", this.updateInfo, this);
27412         this.ds = undefined;
27413     },
27414
27415     /**
27416      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27417      * @param {Roo.data.Store} store The data store to bind
27418      */
27419     bind : function(ds){
27420         ds.on("beforeload", this.beforeLoad, this);
27421         ds.on("load", this.onLoad, this);
27422         ds.on("loadexception", this.onLoadError, this);
27423         ds.on("remove", this.updateInfo, this);
27424         ds.on("add", this.updateInfo, this);
27425         this.ds = ds;
27426     }
27427 });/*
27428  * - LGPL
27429  *
27430  * element
27431  * 
27432  */
27433
27434 /**
27435  * @class Roo.bootstrap.MessageBar
27436  * @extends Roo.bootstrap.Component
27437  * Bootstrap MessageBar class
27438  * @cfg {String} html contents of the MessageBar
27439  * @cfg {String} weight (info | success | warning | danger) default info
27440  * @cfg {String} beforeClass insert the bar before the given class
27441  * @cfg {Boolean} closable (true | false) default false
27442  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27443  * 
27444  * @constructor
27445  * Create a new Element
27446  * @param {Object} config The config object
27447  */
27448
27449 Roo.bootstrap.MessageBar = function(config){
27450     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27451 };
27452
27453 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27454     
27455     html: '',
27456     weight: 'info',
27457     closable: false,
27458     fixed: false,
27459     beforeClass: 'bootstrap-sticky-wrap',
27460     
27461     getAutoCreate : function(){
27462         
27463         var cfg = {
27464             tag: 'div',
27465             cls: 'alert alert-dismissable alert-' + this.weight,
27466             cn: [
27467                 {
27468                     tag: 'span',
27469                     cls: 'message',
27470                     html: this.html || ''
27471                 }
27472             ]
27473         };
27474         
27475         if(this.fixed){
27476             cfg.cls += ' alert-messages-fixed';
27477         }
27478         
27479         if(this.closable){
27480             cfg.cn.push({
27481                 tag: 'button',
27482                 cls: 'close',
27483                 html: 'x'
27484             });
27485         }
27486         
27487         return cfg;
27488     },
27489     
27490     onRender : function(ct, position)
27491     {
27492         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27493         
27494         if(!this.el){
27495             var cfg = Roo.apply({},  this.getAutoCreate());
27496             cfg.id = Roo.id();
27497             
27498             if (this.cls) {
27499                 cfg.cls += ' ' + this.cls;
27500             }
27501             if (this.style) {
27502                 cfg.style = this.style;
27503             }
27504             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27505             
27506             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27507         }
27508         
27509         this.el.select('>button.close').on('click', this.hide, this);
27510         
27511     },
27512     
27513     show : function()
27514     {
27515         if (!this.rendered) {
27516             this.render();
27517         }
27518         
27519         this.el.show();
27520         
27521         this.fireEvent('show', this);
27522         
27523     },
27524     
27525     hide : function()
27526     {
27527         if (!this.rendered) {
27528             this.render();
27529         }
27530         
27531         this.el.hide();
27532         
27533         this.fireEvent('hide', this);
27534     },
27535     
27536     update : function()
27537     {
27538 //        var e = this.el.dom.firstChild;
27539 //        
27540 //        if(this.closable){
27541 //            e = e.nextSibling;
27542 //        }
27543 //        
27544 //        e.data = this.html || '';
27545
27546         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27547     }
27548    
27549 });
27550
27551  
27552
27553      /*
27554  * - LGPL
27555  *
27556  * Graph
27557  * 
27558  */
27559
27560
27561 /**
27562  * @class Roo.bootstrap.Graph
27563  * @extends Roo.bootstrap.Component
27564  * Bootstrap Graph class
27565 > Prameters
27566  -sm {number} sm 4
27567  -md {number} md 5
27568  @cfg {String} graphtype  bar | vbar | pie
27569  @cfg {number} g_x coodinator | centre x (pie)
27570  @cfg {number} g_y coodinator | centre y (pie)
27571  @cfg {number} g_r radius (pie)
27572  @cfg {number} g_height height of the chart (respected by all elements in the set)
27573  @cfg {number} g_width width of the chart (respected by all elements in the set)
27574  @cfg {Object} title The title of the chart
27575     
27576  -{Array}  values
27577  -opts (object) options for the chart 
27578      o {
27579      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27580      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27581      o vgutter (number)
27582      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.
27583      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27584      o to
27585      o stretch (boolean)
27586      o }
27587  -opts (object) options for the pie
27588      o{
27589      o cut
27590      o startAngle (number)
27591      o endAngle (number)
27592      } 
27593  *
27594  * @constructor
27595  * Create a new Input
27596  * @param {Object} config The config object
27597  */
27598
27599 Roo.bootstrap.Graph = function(config){
27600     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27601     
27602     this.addEvents({
27603         // img events
27604         /**
27605          * @event click
27606          * The img click event for the img.
27607          * @param {Roo.EventObject} e
27608          */
27609         "click" : true
27610     });
27611 };
27612
27613 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27614     
27615     sm: 4,
27616     md: 5,
27617     graphtype: 'bar',
27618     g_height: 250,
27619     g_width: 400,
27620     g_x: 50,
27621     g_y: 50,
27622     g_r: 30,
27623     opts:{
27624         //g_colors: this.colors,
27625         g_type: 'soft',
27626         g_gutter: '20%'
27627
27628     },
27629     title : false,
27630
27631     getAutoCreate : function(){
27632         
27633         var cfg = {
27634             tag: 'div',
27635             html : null
27636         };
27637         
27638         
27639         return  cfg;
27640     },
27641
27642     onRender : function(ct,position){
27643         
27644         
27645         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27646         
27647         if (typeof(Raphael) == 'undefined') {
27648             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27649             return;
27650         }
27651         
27652         this.raphael = Raphael(this.el.dom);
27653         
27654                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27655                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27656                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27657                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27658                 /*
27659                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27660                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27661                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27662                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27663                 
27664                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27665                 r.barchart(330, 10, 300, 220, data1);
27666                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27667                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27668                 */
27669                 
27670                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27671                 // r.barchart(30, 30, 560, 250,  xdata, {
27672                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27673                 //     axis : "0 0 1 1",
27674                 //     axisxlabels :  xdata
27675                 //     //yvalues : cols,
27676                    
27677                 // });
27678 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27679 //        
27680 //        this.load(null,xdata,{
27681 //                axis : "0 0 1 1",
27682 //                axisxlabels :  xdata
27683 //                });
27684
27685     },
27686
27687     load : function(graphtype,xdata,opts)
27688     {
27689         this.raphael.clear();
27690         if(!graphtype) {
27691             graphtype = this.graphtype;
27692         }
27693         if(!opts){
27694             opts = this.opts;
27695         }
27696         var r = this.raphael,
27697             fin = function () {
27698                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27699             },
27700             fout = function () {
27701                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27702             },
27703             pfin = function() {
27704                 this.sector.stop();
27705                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27706
27707                 if (this.label) {
27708                     this.label[0].stop();
27709                     this.label[0].attr({ r: 7.5 });
27710                     this.label[1].attr({ "font-weight": 800 });
27711                 }
27712             },
27713             pfout = function() {
27714                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27715
27716                 if (this.label) {
27717                     this.label[0].animate({ r: 5 }, 500, "bounce");
27718                     this.label[1].attr({ "font-weight": 400 });
27719                 }
27720             };
27721
27722         switch(graphtype){
27723             case 'bar':
27724                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27725                 break;
27726             case 'hbar':
27727                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27728                 break;
27729             case 'pie':
27730 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27731 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27732 //            
27733                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27734                 
27735                 break;
27736
27737         }
27738         
27739         if(this.title){
27740             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27741         }
27742         
27743     },
27744     
27745     setTitle: function(o)
27746     {
27747         this.title = o;
27748     },
27749     
27750     initEvents: function() {
27751         
27752         if(!this.href){
27753             this.el.on('click', this.onClick, this);
27754         }
27755     },
27756     
27757     onClick : function(e)
27758     {
27759         Roo.log('img onclick');
27760         this.fireEvent('click', this, e);
27761     }
27762    
27763 });
27764
27765  
27766 /*
27767  * - LGPL
27768  *
27769  * numberBox
27770  * 
27771  */
27772 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27773
27774 /**
27775  * @class Roo.bootstrap.dash.NumberBox
27776  * @extends Roo.bootstrap.Component
27777  * Bootstrap NumberBox class
27778  * @cfg {String} headline Box headline
27779  * @cfg {String} content Box content
27780  * @cfg {String} icon Box icon
27781  * @cfg {String} footer Footer text
27782  * @cfg {String} fhref Footer href
27783  * 
27784  * @constructor
27785  * Create a new NumberBox
27786  * @param {Object} config The config object
27787  */
27788
27789
27790 Roo.bootstrap.dash.NumberBox = function(config){
27791     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27792     
27793 };
27794
27795 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27796     
27797     headline : '',
27798     content : '',
27799     icon : '',
27800     footer : '',
27801     fhref : '',
27802     ficon : '',
27803     
27804     getAutoCreate : function(){
27805         
27806         var cfg = {
27807             tag : 'div',
27808             cls : 'small-box ',
27809             cn : [
27810                 {
27811                     tag : 'div',
27812                     cls : 'inner',
27813                     cn :[
27814                         {
27815                             tag : 'h3',
27816                             cls : 'roo-headline',
27817                             html : this.headline
27818                         },
27819                         {
27820                             tag : 'p',
27821                             cls : 'roo-content',
27822                             html : this.content
27823                         }
27824                     ]
27825                 }
27826             ]
27827         };
27828         
27829         if(this.icon){
27830             cfg.cn.push({
27831                 tag : 'div',
27832                 cls : 'icon',
27833                 cn :[
27834                     {
27835                         tag : 'i',
27836                         cls : 'ion ' + this.icon
27837                     }
27838                 ]
27839             });
27840         }
27841         
27842         if(this.footer){
27843             var footer = {
27844                 tag : 'a',
27845                 cls : 'small-box-footer',
27846                 href : this.fhref || '#',
27847                 html : this.footer
27848             };
27849             
27850             cfg.cn.push(footer);
27851             
27852         }
27853         
27854         return  cfg;
27855     },
27856
27857     onRender : function(ct,position){
27858         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27859
27860
27861        
27862                 
27863     },
27864
27865     setHeadline: function (value)
27866     {
27867         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27868     },
27869     
27870     setFooter: function (value, href)
27871     {
27872         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27873         
27874         if(href){
27875             this.el.select('a.small-box-footer',true).first().attr('href', href);
27876         }
27877         
27878     },
27879
27880     setContent: function (value)
27881     {
27882         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27883     },
27884
27885     initEvents: function() 
27886     {   
27887         
27888     }
27889     
27890 });
27891
27892  
27893 /*
27894  * - LGPL
27895  *
27896  * TabBox
27897  * 
27898  */
27899 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27900
27901 /**
27902  * @class Roo.bootstrap.dash.TabBox
27903  * @extends Roo.bootstrap.Component
27904  * Bootstrap TabBox class
27905  * @cfg {String} title Title of the TabBox
27906  * @cfg {String} icon Icon of the TabBox
27907  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27908  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27909  * 
27910  * @constructor
27911  * Create a new TabBox
27912  * @param {Object} config The config object
27913  */
27914
27915
27916 Roo.bootstrap.dash.TabBox = function(config){
27917     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27918     this.addEvents({
27919         // raw events
27920         /**
27921          * @event addpane
27922          * When a pane is added
27923          * @param {Roo.bootstrap.dash.TabPane} pane
27924          */
27925         "addpane" : true,
27926         /**
27927          * @event activatepane
27928          * When a pane is activated
27929          * @param {Roo.bootstrap.dash.TabPane} pane
27930          */
27931         "activatepane" : true
27932         
27933          
27934     });
27935     
27936     this.panes = [];
27937 };
27938
27939 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27940
27941     title : '',
27942     icon : false,
27943     showtabs : true,
27944     tabScrollable : false,
27945     
27946     getChildContainer : function()
27947     {
27948         return this.el.select('.tab-content', true).first();
27949     },
27950     
27951     getAutoCreate : function(){
27952         
27953         var header = {
27954             tag: 'li',
27955             cls: 'pull-left header',
27956             html: this.title,
27957             cn : []
27958         };
27959         
27960         if(this.icon){
27961             header.cn.push({
27962                 tag: 'i',
27963                 cls: 'fa ' + this.icon
27964             });
27965         }
27966         
27967         var h = {
27968             tag: 'ul',
27969             cls: 'nav nav-tabs pull-right',
27970             cn: [
27971                 header
27972             ]
27973         };
27974         
27975         if(this.tabScrollable){
27976             h = {
27977                 tag: 'div',
27978                 cls: 'tab-header',
27979                 cn: [
27980                     {
27981                         tag: 'ul',
27982                         cls: 'nav nav-tabs pull-right',
27983                         cn: [
27984                             header
27985                         ]
27986                     }
27987                 ]
27988             };
27989         }
27990         
27991         var cfg = {
27992             tag: 'div',
27993             cls: 'nav-tabs-custom',
27994             cn: [
27995                 h,
27996                 {
27997                     tag: 'div',
27998                     cls: 'tab-content no-padding',
27999                     cn: []
28000                 }
28001             ]
28002         };
28003
28004         return  cfg;
28005     },
28006     initEvents : function()
28007     {
28008         //Roo.log('add add pane handler');
28009         this.on('addpane', this.onAddPane, this);
28010     },
28011      /**
28012      * Updates the box title
28013      * @param {String} html to set the title to.
28014      */
28015     setTitle : function(value)
28016     {
28017         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28018     },
28019     onAddPane : function(pane)
28020     {
28021         this.panes.push(pane);
28022         //Roo.log('addpane');
28023         //Roo.log(pane);
28024         // tabs are rendere left to right..
28025         if(!this.showtabs){
28026             return;
28027         }
28028         
28029         var ctr = this.el.select('.nav-tabs', true).first();
28030          
28031          
28032         var existing = ctr.select('.nav-tab',true);
28033         var qty = existing.getCount();;
28034         
28035         
28036         var tab = ctr.createChild({
28037             tag : 'li',
28038             cls : 'nav-tab' + (qty ? '' : ' active'),
28039             cn : [
28040                 {
28041                     tag : 'a',
28042                     href:'#',
28043                     html : pane.title
28044                 }
28045             ]
28046         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28047         pane.tab = tab;
28048         
28049         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28050         if (!qty) {
28051             pane.el.addClass('active');
28052         }
28053         
28054                 
28055     },
28056     onTabClick : function(ev,un,ob,pane)
28057     {
28058         //Roo.log('tab - prev default');
28059         ev.preventDefault();
28060         
28061         
28062         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28063         pane.tab.addClass('active');
28064         //Roo.log(pane.title);
28065         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28066         // technically we should have a deactivate event.. but maybe add later.
28067         // and it should not de-activate the selected tab...
28068         this.fireEvent('activatepane', pane);
28069         pane.el.addClass('active');
28070         pane.fireEvent('activate');
28071         
28072         
28073     },
28074     
28075     getActivePane : function()
28076     {
28077         var r = false;
28078         Roo.each(this.panes, function(p) {
28079             if(p.el.hasClass('active')){
28080                 r = p;
28081                 return false;
28082             }
28083             
28084             return;
28085         });
28086         
28087         return r;
28088     }
28089     
28090     
28091 });
28092
28093  
28094 /*
28095  * - LGPL
28096  *
28097  * Tab pane
28098  * 
28099  */
28100 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28101 /**
28102  * @class Roo.bootstrap.TabPane
28103  * @extends Roo.bootstrap.Component
28104  * Bootstrap TabPane class
28105  * @cfg {Boolean} active (false | true) Default false
28106  * @cfg {String} title title of panel
28107
28108  * 
28109  * @constructor
28110  * Create a new TabPane
28111  * @param {Object} config The config object
28112  */
28113
28114 Roo.bootstrap.dash.TabPane = function(config){
28115     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28116     
28117     this.addEvents({
28118         // raw events
28119         /**
28120          * @event activate
28121          * When a pane is activated
28122          * @param {Roo.bootstrap.dash.TabPane} pane
28123          */
28124         "activate" : true
28125          
28126     });
28127 };
28128
28129 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28130     
28131     active : false,
28132     title : '',
28133     
28134     // the tabBox that this is attached to.
28135     tab : false,
28136      
28137     getAutoCreate : function() 
28138     {
28139         var cfg = {
28140             tag: 'div',
28141             cls: 'tab-pane'
28142         };
28143         
28144         if(this.active){
28145             cfg.cls += ' active';
28146         }
28147         
28148         return cfg;
28149     },
28150     initEvents  : function()
28151     {
28152         //Roo.log('trigger add pane handler');
28153         this.parent().fireEvent('addpane', this)
28154     },
28155     
28156      /**
28157      * Updates the tab title 
28158      * @param {String} html to set the title to.
28159      */
28160     setTitle: function(str)
28161     {
28162         if (!this.tab) {
28163             return;
28164         }
28165         this.title = str;
28166         this.tab.select('a', true).first().dom.innerHTML = str;
28167         
28168     }
28169     
28170     
28171     
28172 });
28173
28174  
28175
28176
28177  /*
28178  * - LGPL
28179  *
28180  * menu
28181  * 
28182  */
28183 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28184
28185 /**
28186  * @class Roo.bootstrap.menu.Menu
28187  * @extends Roo.bootstrap.Component
28188  * Bootstrap Menu class - container for Menu
28189  * @cfg {String} html Text of the menu
28190  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28191  * @cfg {String} icon Font awesome icon
28192  * @cfg {String} pos Menu align to (top | bottom) default bottom
28193  * 
28194  * 
28195  * @constructor
28196  * Create a new Menu
28197  * @param {Object} config The config object
28198  */
28199
28200
28201 Roo.bootstrap.menu.Menu = function(config){
28202     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28203     
28204     this.addEvents({
28205         /**
28206          * @event beforeshow
28207          * Fires before this menu is displayed
28208          * @param {Roo.bootstrap.menu.Menu} this
28209          */
28210         beforeshow : true,
28211         /**
28212          * @event beforehide
28213          * Fires before this menu is hidden
28214          * @param {Roo.bootstrap.menu.Menu} this
28215          */
28216         beforehide : true,
28217         /**
28218          * @event show
28219          * Fires after this menu is displayed
28220          * @param {Roo.bootstrap.menu.Menu} this
28221          */
28222         show : true,
28223         /**
28224          * @event hide
28225          * Fires after this menu is hidden
28226          * @param {Roo.bootstrap.menu.Menu} this
28227          */
28228         hide : true,
28229         /**
28230          * @event click
28231          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28232          * @param {Roo.bootstrap.menu.Menu} this
28233          * @param {Roo.EventObject} e
28234          */
28235         click : true
28236     });
28237     
28238 };
28239
28240 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28241     
28242     submenu : false,
28243     html : '',
28244     weight : 'default',
28245     icon : false,
28246     pos : 'bottom',
28247     
28248     
28249     getChildContainer : function() {
28250         if(this.isSubMenu){
28251             return this.el;
28252         }
28253         
28254         return this.el.select('ul.dropdown-menu', true).first();  
28255     },
28256     
28257     getAutoCreate : function()
28258     {
28259         var text = [
28260             {
28261                 tag : 'span',
28262                 cls : 'roo-menu-text',
28263                 html : this.html
28264             }
28265         ];
28266         
28267         if(this.icon){
28268             text.unshift({
28269                 tag : 'i',
28270                 cls : 'fa ' + this.icon
28271             })
28272         }
28273         
28274         
28275         var cfg = {
28276             tag : 'div',
28277             cls : 'btn-group',
28278             cn : [
28279                 {
28280                     tag : 'button',
28281                     cls : 'dropdown-button btn btn-' + this.weight,
28282                     cn : text
28283                 },
28284                 {
28285                     tag : 'button',
28286                     cls : 'dropdown-toggle btn btn-' + this.weight,
28287                     cn : [
28288                         {
28289                             tag : 'span',
28290                             cls : 'caret'
28291                         }
28292                     ]
28293                 },
28294                 {
28295                     tag : 'ul',
28296                     cls : 'dropdown-menu'
28297                 }
28298             ]
28299             
28300         };
28301         
28302         if(this.pos == 'top'){
28303             cfg.cls += ' dropup';
28304         }
28305         
28306         if(this.isSubMenu){
28307             cfg = {
28308                 tag : 'ul',
28309                 cls : 'dropdown-menu'
28310             }
28311         }
28312         
28313         return cfg;
28314     },
28315     
28316     onRender : function(ct, position)
28317     {
28318         this.isSubMenu = ct.hasClass('dropdown-submenu');
28319         
28320         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28321     },
28322     
28323     initEvents : function() 
28324     {
28325         if(this.isSubMenu){
28326             return;
28327         }
28328         
28329         this.hidden = true;
28330         
28331         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28332         this.triggerEl.on('click', this.onTriggerPress, this);
28333         
28334         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28335         this.buttonEl.on('click', this.onClick, this);
28336         
28337     },
28338     
28339     list : function()
28340     {
28341         if(this.isSubMenu){
28342             return this.el;
28343         }
28344         
28345         return this.el.select('ul.dropdown-menu', true).first();
28346     },
28347     
28348     onClick : function(e)
28349     {
28350         this.fireEvent("click", this, e);
28351     },
28352     
28353     onTriggerPress  : function(e)
28354     {   
28355         if (this.isVisible()) {
28356             this.hide();
28357         } else {
28358             this.show();
28359         }
28360     },
28361     
28362     isVisible : function(){
28363         return !this.hidden;
28364     },
28365     
28366     show : function()
28367     {
28368         this.fireEvent("beforeshow", this);
28369         
28370         this.hidden = false;
28371         this.el.addClass('open');
28372         
28373         Roo.get(document).on("mouseup", this.onMouseUp, this);
28374         
28375         this.fireEvent("show", this);
28376         
28377         
28378     },
28379     
28380     hide : function()
28381     {
28382         this.fireEvent("beforehide", this);
28383         
28384         this.hidden = true;
28385         this.el.removeClass('open');
28386         
28387         Roo.get(document).un("mouseup", this.onMouseUp);
28388         
28389         this.fireEvent("hide", this);
28390     },
28391     
28392     onMouseUp : function()
28393     {
28394         this.hide();
28395     }
28396     
28397 });
28398
28399  
28400  /*
28401  * - LGPL
28402  *
28403  * menu item
28404  * 
28405  */
28406 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28407
28408 /**
28409  * @class Roo.bootstrap.menu.Item
28410  * @extends Roo.bootstrap.Component
28411  * Bootstrap MenuItem class
28412  * @cfg {Boolean} submenu (true | false) default false
28413  * @cfg {String} html text of the item
28414  * @cfg {String} href the link
28415  * @cfg {Boolean} disable (true | false) default false
28416  * @cfg {Boolean} preventDefault (true | false) default true
28417  * @cfg {String} icon Font awesome icon
28418  * @cfg {String} pos Submenu align to (left | right) default right 
28419  * 
28420  * 
28421  * @constructor
28422  * Create a new Item
28423  * @param {Object} config The config object
28424  */
28425
28426
28427 Roo.bootstrap.menu.Item = function(config){
28428     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28429     this.addEvents({
28430         /**
28431          * @event mouseover
28432          * Fires when the mouse is hovering over this menu
28433          * @param {Roo.bootstrap.menu.Item} this
28434          * @param {Roo.EventObject} e
28435          */
28436         mouseover : true,
28437         /**
28438          * @event mouseout
28439          * Fires when the mouse exits this menu
28440          * @param {Roo.bootstrap.menu.Item} this
28441          * @param {Roo.EventObject} e
28442          */
28443         mouseout : true,
28444         // raw events
28445         /**
28446          * @event click
28447          * The raw click event for the entire grid.
28448          * @param {Roo.EventObject} e
28449          */
28450         click : true
28451     });
28452 };
28453
28454 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28455     
28456     submenu : false,
28457     href : '',
28458     html : '',
28459     preventDefault: true,
28460     disable : false,
28461     icon : false,
28462     pos : 'right',
28463     
28464     getAutoCreate : function()
28465     {
28466         var text = [
28467             {
28468                 tag : 'span',
28469                 cls : 'roo-menu-item-text',
28470                 html : this.html
28471             }
28472         ];
28473         
28474         if(this.icon){
28475             text.unshift({
28476                 tag : 'i',
28477                 cls : 'fa ' + this.icon
28478             })
28479         }
28480         
28481         var cfg = {
28482             tag : 'li',
28483             cn : [
28484                 {
28485                     tag : 'a',
28486                     href : this.href || '#',
28487                     cn : text
28488                 }
28489             ]
28490         };
28491         
28492         if(this.disable){
28493             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28494         }
28495         
28496         if(this.submenu){
28497             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28498             
28499             if(this.pos == 'left'){
28500                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28501             }
28502         }
28503         
28504         return cfg;
28505     },
28506     
28507     initEvents : function() 
28508     {
28509         this.el.on('mouseover', this.onMouseOver, this);
28510         this.el.on('mouseout', this.onMouseOut, this);
28511         
28512         this.el.select('a', true).first().on('click', this.onClick, this);
28513         
28514     },
28515     
28516     onClick : function(e)
28517     {
28518         if(this.preventDefault){
28519             e.preventDefault();
28520         }
28521         
28522         this.fireEvent("click", this, e);
28523     },
28524     
28525     onMouseOver : function(e)
28526     {
28527         if(this.submenu && this.pos == 'left'){
28528             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28529         }
28530         
28531         this.fireEvent("mouseover", this, e);
28532     },
28533     
28534     onMouseOut : function(e)
28535     {
28536         this.fireEvent("mouseout", this, e);
28537     }
28538 });
28539
28540  
28541
28542  /*
28543  * - LGPL
28544  *
28545  * menu separator
28546  * 
28547  */
28548 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28549
28550 /**
28551  * @class Roo.bootstrap.menu.Separator
28552  * @extends Roo.bootstrap.Component
28553  * Bootstrap Separator class
28554  * 
28555  * @constructor
28556  * Create a new Separator
28557  * @param {Object} config The config object
28558  */
28559
28560
28561 Roo.bootstrap.menu.Separator = function(config){
28562     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28563 };
28564
28565 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28566     
28567     getAutoCreate : function(){
28568         var cfg = {
28569             tag : 'li',
28570             cls: 'divider'
28571         };
28572         
28573         return cfg;
28574     }
28575    
28576 });
28577
28578  
28579
28580  /*
28581  * - LGPL
28582  *
28583  * Tooltip
28584  * 
28585  */
28586
28587 /**
28588  * @class Roo.bootstrap.Tooltip
28589  * Bootstrap Tooltip class
28590  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28591  * to determine which dom element triggers the tooltip.
28592  * 
28593  * It needs to add support for additional attributes like tooltip-position
28594  * 
28595  * @constructor
28596  * Create a new Toolti
28597  * @param {Object} config The config object
28598  */
28599
28600 Roo.bootstrap.Tooltip = function(config){
28601     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28602     
28603     this.alignment = Roo.bootstrap.Tooltip.alignment;
28604     
28605     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28606         this.alignment = config.alignment;
28607     }
28608     
28609 };
28610
28611 Roo.apply(Roo.bootstrap.Tooltip, {
28612     /**
28613      * @function init initialize tooltip monitoring.
28614      * @static
28615      */
28616     currentEl : false,
28617     currentTip : false,
28618     currentRegion : false,
28619     
28620     //  init : delay?
28621     
28622     init : function()
28623     {
28624         Roo.get(document).on('mouseover', this.enter ,this);
28625         Roo.get(document).on('mouseout', this.leave, this);
28626          
28627         
28628         this.currentTip = new Roo.bootstrap.Tooltip();
28629     },
28630     
28631     enter : function(ev)
28632     {
28633         var dom = ev.getTarget();
28634         
28635         //Roo.log(['enter',dom]);
28636         var el = Roo.fly(dom);
28637         if (this.currentEl) {
28638             //Roo.log(dom);
28639             //Roo.log(this.currentEl);
28640             //Roo.log(this.currentEl.contains(dom));
28641             if (this.currentEl == el) {
28642                 return;
28643             }
28644             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28645                 return;
28646             }
28647
28648         }
28649         
28650         if (this.currentTip.el) {
28651             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28652         }    
28653         //Roo.log(ev);
28654         
28655         if(!el || el.dom == document){
28656             return;
28657         }
28658         
28659         var bindEl = el;
28660         
28661         // you can not look for children, as if el is the body.. then everythign is the child..
28662         if (!el.attr('tooltip')) { //
28663             if (!el.select("[tooltip]").elements.length) {
28664                 return;
28665             }
28666             // is the mouse over this child...?
28667             bindEl = el.select("[tooltip]").first();
28668             var xy = ev.getXY();
28669             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28670                 //Roo.log("not in region.");
28671                 return;
28672             }
28673             //Roo.log("child element over..");
28674             
28675         }
28676         this.currentEl = bindEl;
28677         this.currentTip.bind(bindEl);
28678         this.currentRegion = Roo.lib.Region.getRegion(dom);
28679         this.currentTip.enter();
28680         
28681     },
28682     leave : function(ev)
28683     {
28684         var dom = ev.getTarget();
28685         //Roo.log(['leave',dom]);
28686         if (!this.currentEl) {
28687             return;
28688         }
28689         
28690         
28691         if (dom != this.currentEl.dom) {
28692             return;
28693         }
28694         var xy = ev.getXY();
28695         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28696             return;
28697         }
28698         // only activate leave if mouse cursor is outside... bounding box..
28699         
28700         
28701         
28702         
28703         if (this.currentTip) {
28704             this.currentTip.leave();
28705         }
28706         //Roo.log('clear currentEl');
28707         this.currentEl = false;
28708         
28709         
28710     },
28711     alignment : {
28712         'left' : ['r-l', [-2,0], 'right'],
28713         'right' : ['l-r', [2,0], 'left'],
28714         'bottom' : ['t-b', [0,2], 'top'],
28715         'top' : [ 'b-t', [0,-2], 'bottom']
28716     }
28717     
28718 });
28719
28720
28721 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28722     
28723     
28724     bindEl : false,
28725     
28726     delay : null, // can be { show : 300 , hide: 500}
28727     
28728     timeout : null,
28729     
28730     hoverState : null, //???
28731     
28732     placement : 'bottom', 
28733     
28734     alignment : false,
28735     
28736     getAutoCreate : function(){
28737     
28738         var cfg = {
28739            cls : 'tooltip',   
28740            role : 'tooltip',
28741            cn : [
28742                 {
28743                     cls : 'tooltip-arrow arrow'
28744                 },
28745                 {
28746                     cls : 'tooltip-inner'
28747                 }
28748            ]
28749         };
28750         
28751         return cfg;
28752     },
28753     bind : function(el)
28754     {
28755         this.bindEl = el;
28756     },
28757     
28758     initEvents : function()
28759     {
28760         this.arrowEl = this.el.select('.arrow', true).first();
28761         this.innerEl = this.el.select('.tooltip-inner', true).first();
28762     },
28763     
28764     enter : function () {
28765        
28766         if (this.timeout != null) {
28767             clearTimeout(this.timeout);
28768         }
28769         
28770         this.hoverState = 'in';
28771          //Roo.log("enter - show");
28772         if (!this.delay || !this.delay.show) {
28773             this.show();
28774             return;
28775         }
28776         var _t = this;
28777         this.timeout = setTimeout(function () {
28778             if (_t.hoverState == 'in') {
28779                 _t.show();
28780             }
28781         }, this.delay.show);
28782     },
28783     leave : function()
28784     {
28785         clearTimeout(this.timeout);
28786     
28787         this.hoverState = 'out';
28788          if (!this.delay || !this.delay.hide) {
28789             this.hide();
28790             return;
28791         }
28792        
28793         var _t = this;
28794         this.timeout = setTimeout(function () {
28795             //Roo.log("leave - timeout");
28796             
28797             if (_t.hoverState == 'out') {
28798                 _t.hide();
28799                 Roo.bootstrap.Tooltip.currentEl = false;
28800             }
28801         }, delay);
28802     },
28803     
28804     show : function (msg)
28805     {
28806         if (!this.el) {
28807             this.render(document.body);
28808         }
28809         // set content.
28810         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28811         
28812         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28813         
28814         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28815         
28816         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28817                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28818         
28819         var placement = typeof this.placement == 'function' ?
28820             this.placement.call(this, this.el, on_el) :
28821             this.placement;
28822             
28823         var autoToken = /\s?auto?\s?/i;
28824         var autoPlace = autoToken.test(placement);
28825         if (autoPlace) {
28826             placement = placement.replace(autoToken, '') || 'top';
28827         }
28828         
28829         //this.el.detach()
28830         //this.el.setXY([0,0]);
28831         this.el.show();
28832         //this.el.dom.style.display='block';
28833         
28834         //this.el.appendTo(on_el);
28835         
28836         var p = this.getPosition();
28837         var box = this.el.getBox();
28838         
28839         if (autoPlace) {
28840             // fixme..
28841         }
28842         
28843         var align = this.alignment[placement];
28844         
28845         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28846         
28847         if(placement == 'top' || placement == 'bottom'){
28848             if(xy[0] < 0){
28849                 placement = 'right';
28850             }
28851             
28852             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28853                 placement = 'left';
28854             }
28855             
28856             var scroll = Roo.select('body', true).first().getScroll();
28857             
28858             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28859                 placement = 'top';
28860             }
28861             
28862             align = this.alignment[placement];
28863             
28864             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28865             
28866         }
28867         
28868         this.el.alignTo(this.bindEl, align[0],align[1]);
28869         //var arrow = this.el.select('.arrow',true).first();
28870         //arrow.set(align[2], 
28871         
28872         this.el.addClass(placement);
28873         this.el.addClass("bs-tooltip-"+ placement);
28874         
28875         this.el.addClass('in fade show');
28876         
28877         this.hoverState = null;
28878         
28879         if (this.el.hasClass('fade')) {
28880             // fade it?
28881         }
28882         
28883         
28884         
28885         
28886         
28887     },
28888     hide : function()
28889     {
28890          
28891         if (!this.el) {
28892             return;
28893         }
28894         //this.el.setXY([0,0]);
28895         this.el.removeClass(['show', 'in']);
28896         //this.el.hide();
28897         
28898     }
28899     
28900 });
28901  
28902
28903  /*
28904  * - LGPL
28905  *
28906  * Location Picker
28907  * 
28908  */
28909
28910 /**
28911  * @class Roo.bootstrap.LocationPicker
28912  * @extends Roo.bootstrap.Component
28913  * Bootstrap LocationPicker class
28914  * @cfg {Number} latitude Position when init default 0
28915  * @cfg {Number} longitude Position when init default 0
28916  * @cfg {Number} zoom default 15
28917  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28918  * @cfg {Boolean} mapTypeControl default false
28919  * @cfg {Boolean} disableDoubleClickZoom default false
28920  * @cfg {Boolean} scrollwheel default true
28921  * @cfg {Boolean} streetViewControl default false
28922  * @cfg {Number} radius default 0
28923  * @cfg {String} locationName
28924  * @cfg {Boolean} draggable default true
28925  * @cfg {Boolean} enableAutocomplete default false
28926  * @cfg {Boolean} enableReverseGeocode default true
28927  * @cfg {String} markerTitle
28928  * 
28929  * @constructor
28930  * Create a new LocationPicker
28931  * @param {Object} config The config object
28932  */
28933
28934
28935 Roo.bootstrap.LocationPicker = function(config){
28936     
28937     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28938     
28939     this.addEvents({
28940         /**
28941          * @event initial
28942          * Fires when the picker initialized.
28943          * @param {Roo.bootstrap.LocationPicker} this
28944          * @param {Google Location} location
28945          */
28946         initial : true,
28947         /**
28948          * @event positionchanged
28949          * Fires when the picker position changed.
28950          * @param {Roo.bootstrap.LocationPicker} this
28951          * @param {Google Location} location
28952          */
28953         positionchanged : true,
28954         /**
28955          * @event resize
28956          * Fires when the map resize.
28957          * @param {Roo.bootstrap.LocationPicker} this
28958          */
28959         resize : true,
28960         /**
28961          * @event show
28962          * Fires when the map show.
28963          * @param {Roo.bootstrap.LocationPicker} this
28964          */
28965         show : true,
28966         /**
28967          * @event hide
28968          * Fires when the map hide.
28969          * @param {Roo.bootstrap.LocationPicker} this
28970          */
28971         hide : true,
28972         /**
28973          * @event mapClick
28974          * Fires when click the map.
28975          * @param {Roo.bootstrap.LocationPicker} this
28976          * @param {Map event} e
28977          */
28978         mapClick : true,
28979         /**
28980          * @event mapRightClick
28981          * Fires when right click the map.
28982          * @param {Roo.bootstrap.LocationPicker} this
28983          * @param {Map event} e
28984          */
28985         mapRightClick : true,
28986         /**
28987          * @event markerClick
28988          * Fires when click the marker.
28989          * @param {Roo.bootstrap.LocationPicker} this
28990          * @param {Map event} e
28991          */
28992         markerClick : true,
28993         /**
28994          * @event markerRightClick
28995          * Fires when right click the marker.
28996          * @param {Roo.bootstrap.LocationPicker} this
28997          * @param {Map event} e
28998          */
28999         markerRightClick : true,
29000         /**
29001          * @event OverlayViewDraw
29002          * Fires when OverlayView Draw
29003          * @param {Roo.bootstrap.LocationPicker} this
29004          */
29005         OverlayViewDraw : true,
29006         /**
29007          * @event OverlayViewOnAdd
29008          * Fires when OverlayView Draw
29009          * @param {Roo.bootstrap.LocationPicker} this
29010          */
29011         OverlayViewOnAdd : true,
29012         /**
29013          * @event OverlayViewOnRemove
29014          * Fires when OverlayView Draw
29015          * @param {Roo.bootstrap.LocationPicker} this
29016          */
29017         OverlayViewOnRemove : true,
29018         /**
29019          * @event OverlayViewShow
29020          * Fires when OverlayView Draw
29021          * @param {Roo.bootstrap.LocationPicker} this
29022          * @param {Pixel} cpx
29023          */
29024         OverlayViewShow : true,
29025         /**
29026          * @event OverlayViewHide
29027          * Fires when OverlayView Draw
29028          * @param {Roo.bootstrap.LocationPicker} this
29029          */
29030         OverlayViewHide : true,
29031         /**
29032          * @event loadexception
29033          * Fires when load google lib failed.
29034          * @param {Roo.bootstrap.LocationPicker} this
29035          */
29036         loadexception : true
29037     });
29038         
29039 };
29040
29041 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29042     
29043     gMapContext: false,
29044     
29045     latitude: 0,
29046     longitude: 0,
29047     zoom: 15,
29048     mapTypeId: false,
29049     mapTypeControl: false,
29050     disableDoubleClickZoom: false,
29051     scrollwheel: true,
29052     streetViewControl: false,
29053     radius: 0,
29054     locationName: '',
29055     draggable: true,
29056     enableAutocomplete: false,
29057     enableReverseGeocode: true,
29058     markerTitle: '',
29059     
29060     getAutoCreate: function()
29061     {
29062
29063         var cfg = {
29064             tag: 'div',
29065             cls: 'roo-location-picker'
29066         };
29067         
29068         return cfg
29069     },
29070     
29071     initEvents: function(ct, position)
29072     {       
29073         if(!this.el.getWidth() || this.isApplied()){
29074             return;
29075         }
29076         
29077         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29078         
29079         this.initial();
29080     },
29081     
29082     initial: function()
29083     {
29084         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29085             this.fireEvent('loadexception', this);
29086             return;
29087         }
29088         
29089         if(!this.mapTypeId){
29090             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29091         }
29092         
29093         this.gMapContext = this.GMapContext();
29094         
29095         this.initOverlayView();
29096         
29097         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29098         
29099         var _this = this;
29100                 
29101         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29102             _this.setPosition(_this.gMapContext.marker.position);
29103         });
29104         
29105         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29106             _this.fireEvent('mapClick', this, event);
29107             
29108         });
29109
29110         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29111             _this.fireEvent('mapRightClick', this, event);
29112             
29113         });
29114         
29115         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29116             _this.fireEvent('markerClick', this, event);
29117             
29118         });
29119
29120         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29121             _this.fireEvent('markerRightClick', this, event);
29122             
29123         });
29124         
29125         this.setPosition(this.gMapContext.location);
29126         
29127         this.fireEvent('initial', this, this.gMapContext.location);
29128     },
29129     
29130     initOverlayView: function()
29131     {
29132         var _this = this;
29133         
29134         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29135             
29136             draw: function()
29137             {
29138                 _this.fireEvent('OverlayViewDraw', _this);
29139             },
29140             
29141             onAdd: function()
29142             {
29143                 _this.fireEvent('OverlayViewOnAdd', _this);
29144             },
29145             
29146             onRemove: function()
29147             {
29148                 _this.fireEvent('OverlayViewOnRemove', _this);
29149             },
29150             
29151             show: function(cpx)
29152             {
29153                 _this.fireEvent('OverlayViewShow', _this, cpx);
29154             },
29155             
29156             hide: function()
29157             {
29158                 _this.fireEvent('OverlayViewHide', _this);
29159             }
29160             
29161         });
29162     },
29163     
29164     fromLatLngToContainerPixel: function(event)
29165     {
29166         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29167     },
29168     
29169     isApplied: function() 
29170     {
29171         return this.getGmapContext() == false ? false : true;
29172     },
29173     
29174     getGmapContext: function() 
29175     {
29176         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29177     },
29178     
29179     GMapContext: function() 
29180     {
29181         var position = new google.maps.LatLng(this.latitude, this.longitude);
29182         
29183         var _map = new google.maps.Map(this.el.dom, {
29184             center: position,
29185             zoom: this.zoom,
29186             mapTypeId: this.mapTypeId,
29187             mapTypeControl: this.mapTypeControl,
29188             disableDoubleClickZoom: this.disableDoubleClickZoom,
29189             scrollwheel: this.scrollwheel,
29190             streetViewControl: this.streetViewControl,
29191             locationName: this.locationName,
29192             draggable: this.draggable,
29193             enableAutocomplete: this.enableAutocomplete,
29194             enableReverseGeocode: this.enableReverseGeocode
29195         });
29196         
29197         var _marker = new google.maps.Marker({
29198             position: position,
29199             map: _map,
29200             title: this.markerTitle,
29201             draggable: this.draggable
29202         });
29203         
29204         return {
29205             map: _map,
29206             marker: _marker,
29207             circle: null,
29208             location: position,
29209             radius: this.radius,
29210             locationName: this.locationName,
29211             addressComponents: {
29212                 formatted_address: null,
29213                 addressLine1: null,
29214                 addressLine2: null,
29215                 streetName: null,
29216                 streetNumber: null,
29217                 city: null,
29218                 district: null,
29219                 state: null,
29220                 stateOrProvince: null
29221             },
29222             settings: this,
29223             domContainer: this.el.dom,
29224             geodecoder: new google.maps.Geocoder()
29225         };
29226     },
29227     
29228     drawCircle: function(center, radius, options) 
29229     {
29230         if (this.gMapContext.circle != null) {
29231             this.gMapContext.circle.setMap(null);
29232         }
29233         if (radius > 0) {
29234             radius *= 1;
29235             options = Roo.apply({}, options, {
29236                 strokeColor: "#0000FF",
29237                 strokeOpacity: .35,
29238                 strokeWeight: 2,
29239                 fillColor: "#0000FF",
29240                 fillOpacity: .2
29241             });
29242             
29243             options.map = this.gMapContext.map;
29244             options.radius = radius;
29245             options.center = center;
29246             this.gMapContext.circle = new google.maps.Circle(options);
29247             return this.gMapContext.circle;
29248         }
29249         
29250         return null;
29251     },
29252     
29253     setPosition: function(location) 
29254     {
29255         this.gMapContext.location = location;
29256         this.gMapContext.marker.setPosition(location);
29257         this.gMapContext.map.panTo(location);
29258         this.drawCircle(location, this.gMapContext.radius, {});
29259         
29260         var _this = this;
29261         
29262         if (this.gMapContext.settings.enableReverseGeocode) {
29263             this.gMapContext.geodecoder.geocode({
29264                 latLng: this.gMapContext.location
29265             }, function(results, status) {
29266                 
29267                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29268                     _this.gMapContext.locationName = results[0].formatted_address;
29269                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29270                     
29271                     _this.fireEvent('positionchanged', this, location);
29272                 }
29273             });
29274             
29275             return;
29276         }
29277         
29278         this.fireEvent('positionchanged', this, location);
29279     },
29280     
29281     resize: function()
29282     {
29283         google.maps.event.trigger(this.gMapContext.map, "resize");
29284         
29285         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29286         
29287         this.fireEvent('resize', this);
29288     },
29289     
29290     setPositionByLatLng: function(latitude, longitude)
29291     {
29292         this.setPosition(new google.maps.LatLng(latitude, longitude));
29293     },
29294     
29295     getCurrentPosition: function() 
29296     {
29297         return {
29298             latitude: this.gMapContext.location.lat(),
29299             longitude: this.gMapContext.location.lng()
29300         };
29301     },
29302     
29303     getAddressName: function() 
29304     {
29305         return this.gMapContext.locationName;
29306     },
29307     
29308     getAddressComponents: function() 
29309     {
29310         return this.gMapContext.addressComponents;
29311     },
29312     
29313     address_component_from_google_geocode: function(address_components) 
29314     {
29315         var result = {};
29316         
29317         for (var i = 0; i < address_components.length; i++) {
29318             var component = address_components[i];
29319             if (component.types.indexOf("postal_code") >= 0) {
29320                 result.postalCode = component.short_name;
29321             } else if (component.types.indexOf("street_number") >= 0) {
29322                 result.streetNumber = component.short_name;
29323             } else if (component.types.indexOf("route") >= 0) {
29324                 result.streetName = component.short_name;
29325             } else if (component.types.indexOf("neighborhood") >= 0) {
29326                 result.city = component.short_name;
29327             } else if (component.types.indexOf("locality") >= 0) {
29328                 result.city = component.short_name;
29329             } else if (component.types.indexOf("sublocality") >= 0) {
29330                 result.district = component.short_name;
29331             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29332                 result.stateOrProvince = component.short_name;
29333             } else if (component.types.indexOf("country") >= 0) {
29334                 result.country = component.short_name;
29335             }
29336         }
29337         
29338         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29339         result.addressLine2 = "";
29340         return result;
29341     },
29342     
29343     setZoomLevel: function(zoom)
29344     {
29345         this.gMapContext.map.setZoom(zoom);
29346     },
29347     
29348     show: function()
29349     {
29350         if(!this.el){
29351             return;
29352         }
29353         
29354         this.el.show();
29355         
29356         this.resize();
29357         
29358         this.fireEvent('show', this);
29359     },
29360     
29361     hide: function()
29362     {
29363         if(!this.el){
29364             return;
29365         }
29366         
29367         this.el.hide();
29368         
29369         this.fireEvent('hide', this);
29370     }
29371     
29372 });
29373
29374 Roo.apply(Roo.bootstrap.LocationPicker, {
29375     
29376     OverlayView : function(map, options)
29377     {
29378         options = options || {};
29379         
29380         this.setMap(map);
29381     }
29382     
29383     
29384 });/**
29385  * @class Roo.bootstrap.Alert
29386  * @extends Roo.bootstrap.Component
29387  * Bootstrap Alert class - shows an alert area box
29388  * eg
29389  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29390   Enter a valid email address
29391 </div>
29392  * @licence LGPL
29393  * @cfg {String} title The title of alert
29394  * @cfg {String} html The content of alert
29395  * @cfg {String} weight (  success | info | warning | danger )
29396  * @cfg {String} faicon font-awesomeicon
29397  * 
29398  * @constructor
29399  * Create a new alert
29400  * @param {Object} config The config object
29401  */
29402
29403
29404 Roo.bootstrap.Alert = function(config){
29405     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29406     
29407 };
29408
29409 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29410     
29411     title: '',
29412     html: '',
29413     weight: false,
29414     faicon: false,
29415     
29416     getAutoCreate : function()
29417     {
29418         
29419         var cfg = {
29420             tag : 'div',
29421             cls : 'alert',
29422             cn : [
29423                 {
29424                     tag : 'i',
29425                     cls : 'roo-alert-icon'
29426                     
29427                 },
29428                 {
29429                     tag : 'b',
29430                     cls : 'roo-alert-title',
29431                     html : this.title
29432                 },
29433                 {
29434                     tag : 'span',
29435                     cls : 'roo-alert-text',
29436                     html : this.html
29437                 }
29438             ]
29439         };
29440         
29441         if(this.faicon){
29442             cfg.cn[0].cls += ' fa ' + this.faicon;
29443         }
29444         
29445         if(this.weight){
29446             cfg.cls += ' alert-' + this.weight;
29447         }
29448         
29449         return cfg;
29450     },
29451     
29452     initEvents: function() 
29453     {
29454         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29455     },
29456     
29457     setTitle : function(str)
29458     {
29459         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29460     },
29461     
29462     setText : function(str)
29463     {
29464         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29465     },
29466     
29467     setWeight : function(weight)
29468     {
29469         if(this.weight){
29470             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29471         }
29472         
29473         this.weight = weight;
29474         
29475         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29476     },
29477     
29478     setIcon : function(icon)
29479     {
29480         if(this.faicon){
29481             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29482         }
29483         
29484         this.faicon = icon;
29485         
29486         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29487     },
29488     
29489     hide: function() 
29490     {
29491         this.el.hide();   
29492     },
29493     
29494     show: function() 
29495     {  
29496         this.el.show();   
29497     }
29498     
29499 });
29500
29501  
29502 /*
29503 * Licence: LGPL
29504 */
29505
29506 /**
29507  * @class Roo.bootstrap.UploadCropbox
29508  * @extends Roo.bootstrap.Component
29509  * Bootstrap UploadCropbox class
29510  * @cfg {String} emptyText show when image has been loaded
29511  * @cfg {String} rotateNotify show when image too small to rotate
29512  * @cfg {Number} errorTimeout default 3000
29513  * @cfg {Number} minWidth default 300
29514  * @cfg {Number} minHeight default 300
29515  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29516  * @cfg {Boolean} isDocument (true|false) default false
29517  * @cfg {String} url action url
29518  * @cfg {String} paramName default 'imageUpload'
29519  * @cfg {String} method default POST
29520  * @cfg {Boolean} loadMask (true|false) default true
29521  * @cfg {Boolean} loadingText default 'Loading...'
29522  * 
29523  * @constructor
29524  * Create a new UploadCropbox
29525  * @param {Object} config The config object
29526  */
29527
29528 Roo.bootstrap.UploadCropbox = function(config){
29529     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29530     
29531     this.addEvents({
29532         /**
29533          * @event beforeselectfile
29534          * Fire before select file
29535          * @param {Roo.bootstrap.UploadCropbox} this
29536          */
29537         "beforeselectfile" : true,
29538         /**
29539          * @event initial
29540          * Fire after initEvent
29541          * @param {Roo.bootstrap.UploadCropbox} this
29542          */
29543         "initial" : true,
29544         /**
29545          * @event crop
29546          * Fire after initEvent
29547          * @param {Roo.bootstrap.UploadCropbox} this
29548          * @param {String} data
29549          */
29550         "crop" : true,
29551         /**
29552          * @event prepare
29553          * Fire when preparing the file data
29554          * @param {Roo.bootstrap.UploadCropbox} this
29555          * @param {Object} file
29556          */
29557         "prepare" : true,
29558         /**
29559          * @event exception
29560          * Fire when get exception
29561          * @param {Roo.bootstrap.UploadCropbox} this
29562          * @param {XMLHttpRequest} xhr
29563          */
29564         "exception" : true,
29565         /**
29566          * @event beforeloadcanvas
29567          * Fire before load the canvas
29568          * @param {Roo.bootstrap.UploadCropbox} this
29569          * @param {String} src
29570          */
29571         "beforeloadcanvas" : true,
29572         /**
29573          * @event trash
29574          * Fire when trash image
29575          * @param {Roo.bootstrap.UploadCropbox} this
29576          */
29577         "trash" : true,
29578         /**
29579          * @event download
29580          * Fire when download the image
29581          * @param {Roo.bootstrap.UploadCropbox} this
29582          */
29583         "download" : true,
29584         /**
29585          * @event footerbuttonclick
29586          * Fire when footerbuttonclick
29587          * @param {Roo.bootstrap.UploadCropbox} this
29588          * @param {String} type
29589          */
29590         "footerbuttonclick" : true,
29591         /**
29592          * @event resize
29593          * Fire when resize
29594          * @param {Roo.bootstrap.UploadCropbox} this
29595          */
29596         "resize" : true,
29597         /**
29598          * @event rotate
29599          * Fire when rotate the image
29600          * @param {Roo.bootstrap.UploadCropbox} this
29601          * @param {String} pos
29602          */
29603         "rotate" : true,
29604         /**
29605          * @event inspect
29606          * Fire when inspect the file
29607          * @param {Roo.bootstrap.UploadCropbox} this
29608          * @param {Object} file
29609          */
29610         "inspect" : true,
29611         /**
29612          * @event upload
29613          * Fire when xhr upload the file
29614          * @param {Roo.bootstrap.UploadCropbox} this
29615          * @param {Object} data
29616          */
29617         "upload" : true,
29618         /**
29619          * @event arrange
29620          * Fire when arrange the file data
29621          * @param {Roo.bootstrap.UploadCropbox} this
29622          * @param {Object} formData
29623          */
29624         "arrange" : true
29625     });
29626     
29627     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29628 };
29629
29630 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29631     
29632     emptyText : 'Click to upload image',
29633     rotateNotify : 'Image is too small to rotate',
29634     errorTimeout : 3000,
29635     scale : 0,
29636     baseScale : 1,
29637     rotate : 0,
29638     dragable : false,
29639     pinching : false,
29640     mouseX : 0,
29641     mouseY : 0,
29642     cropData : false,
29643     minWidth : 300,
29644     minHeight : 300,
29645     file : false,
29646     exif : {},
29647     baseRotate : 1,
29648     cropType : 'image/jpeg',
29649     buttons : false,
29650     canvasLoaded : false,
29651     isDocument : false,
29652     method : 'POST',
29653     paramName : 'imageUpload',
29654     loadMask : true,
29655     loadingText : 'Loading...',
29656     maskEl : false,
29657     
29658     getAutoCreate : function()
29659     {
29660         var cfg = {
29661             tag : 'div',
29662             cls : 'roo-upload-cropbox',
29663             cn : [
29664                 {
29665                     tag : 'input',
29666                     cls : 'roo-upload-cropbox-selector',
29667                     type : 'file'
29668                 },
29669                 {
29670                     tag : 'div',
29671                     cls : 'roo-upload-cropbox-body',
29672                     style : 'cursor:pointer',
29673                     cn : [
29674                         {
29675                             tag : 'div',
29676                             cls : 'roo-upload-cropbox-preview'
29677                         },
29678                         {
29679                             tag : 'div',
29680                             cls : 'roo-upload-cropbox-thumb'
29681                         },
29682                         {
29683                             tag : 'div',
29684                             cls : 'roo-upload-cropbox-empty-notify',
29685                             html : this.emptyText
29686                         },
29687                         {
29688                             tag : 'div',
29689                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29690                             html : this.rotateNotify
29691                         }
29692                     ]
29693                 },
29694                 {
29695                     tag : 'div',
29696                     cls : 'roo-upload-cropbox-footer',
29697                     cn : {
29698                         tag : 'div',
29699                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29700                         cn : []
29701                     }
29702                 }
29703             ]
29704         };
29705         
29706         return cfg;
29707     },
29708     
29709     onRender : function(ct, position)
29710     {
29711         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29712         
29713         if (this.buttons.length) {
29714             
29715             Roo.each(this.buttons, function(bb) {
29716                 
29717                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29718                 
29719                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29720                 
29721             }, this);
29722         }
29723         
29724         if(this.loadMask){
29725             this.maskEl = this.el;
29726         }
29727     },
29728     
29729     initEvents : function()
29730     {
29731         this.urlAPI = (window.createObjectURL && window) || 
29732                                 (window.URL && URL.revokeObjectURL && URL) || 
29733                                 (window.webkitURL && webkitURL);
29734                         
29735         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29736         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29737         
29738         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29739         this.selectorEl.hide();
29740         
29741         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29742         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29743         
29744         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29745         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29746         this.thumbEl.hide();
29747         
29748         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29749         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29750         
29751         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29752         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29753         this.errorEl.hide();
29754         
29755         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29756         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29757         this.footerEl.hide();
29758         
29759         this.setThumbBoxSize();
29760         
29761         this.bind();
29762         
29763         this.resize();
29764         
29765         this.fireEvent('initial', this);
29766     },
29767
29768     bind : function()
29769     {
29770         var _this = this;
29771         
29772         window.addEventListener("resize", function() { _this.resize(); } );
29773         
29774         this.bodyEl.on('click', this.beforeSelectFile, this);
29775         
29776         if(Roo.isTouch){
29777             this.bodyEl.on('touchstart', this.onTouchStart, this);
29778             this.bodyEl.on('touchmove', this.onTouchMove, this);
29779             this.bodyEl.on('touchend', this.onTouchEnd, this);
29780         }
29781         
29782         if(!Roo.isTouch){
29783             this.bodyEl.on('mousedown', this.onMouseDown, this);
29784             this.bodyEl.on('mousemove', this.onMouseMove, this);
29785             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29786             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29787             Roo.get(document).on('mouseup', this.onMouseUp, this);
29788         }
29789         
29790         this.selectorEl.on('change', this.onFileSelected, this);
29791     },
29792     
29793     reset : function()
29794     {    
29795         this.scale = 0;
29796         this.baseScale = 1;
29797         this.rotate = 0;
29798         this.baseRotate = 1;
29799         this.dragable = false;
29800         this.pinching = false;
29801         this.mouseX = 0;
29802         this.mouseY = 0;
29803         this.cropData = false;
29804         this.notifyEl.dom.innerHTML = this.emptyText;
29805         
29806         this.selectorEl.dom.value = '';
29807         
29808     },
29809     
29810     resize : function()
29811     {
29812         if(this.fireEvent('resize', this) != false){
29813             this.setThumbBoxPosition();
29814             this.setCanvasPosition();
29815         }
29816     },
29817     
29818     onFooterButtonClick : function(e, el, o, type)
29819     {
29820         switch (type) {
29821             case 'rotate-left' :
29822                 this.onRotateLeft(e);
29823                 break;
29824             case 'rotate-right' :
29825                 this.onRotateRight(e);
29826                 break;
29827             case 'picture' :
29828                 this.beforeSelectFile(e);
29829                 break;
29830             case 'trash' :
29831                 this.trash(e);
29832                 break;
29833             case 'crop' :
29834                 this.crop(e);
29835                 break;
29836             case 'download' :
29837                 this.download(e);
29838                 break;
29839             default :
29840                 break;
29841         }
29842         
29843         this.fireEvent('footerbuttonclick', this, type);
29844     },
29845     
29846     beforeSelectFile : function(e)
29847     {
29848         e.preventDefault();
29849         
29850         if(this.fireEvent('beforeselectfile', this) != false){
29851             this.selectorEl.dom.click();
29852         }
29853     },
29854     
29855     onFileSelected : function(e)
29856     {
29857         e.preventDefault();
29858         
29859         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29860             return;
29861         }
29862         
29863         var file = this.selectorEl.dom.files[0];
29864         
29865         if(this.fireEvent('inspect', this, file) != false){
29866             this.prepare(file);
29867         }
29868         
29869     },
29870     
29871     trash : function(e)
29872     {
29873         this.fireEvent('trash', this);
29874     },
29875     
29876     download : function(e)
29877     {
29878         this.fireEvent('download', this);
29879     },
29880     
29881     loadCanvas : function(src)
29882     {   
29883         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29884             
29885             this.reset();
29886             
29887             this.imageEl = document.createElement('img');
29888             
29889             var _this = this;
29890             
29891             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29892             
29893             this.imageEl.src = src;
29894         }
29895     },
29896     
29897     onLoadCanvas : function()
29898     {   
29899         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29900         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29901         
29902         this.bodyEl.un('click', this.beforeSelectFile, this);
29903         
29904         this.notifyEl.hide();
29905         this.thumbEl.show();
29906         this.footerEl.show();
29907         
29908         this.baseRotateLevel();
29909         
29910         if(this.isDocument){
29911             this.setThumbBoxSize();
29912         }
29913         
29914         this.setThumbBoxPosition();
29915         
29916         this.baseScaleLevel();
29917         
29918         this.draw();
29919         
29920         this.resize();
29921         
29922         this.canvasLoaded = true;
29923         
29924         if(this.loadMask){
29925             this.maskEl.unmask();
29926         }
29927         
29928     },
29929     
29930     setCanvasPosition : function()
29931     {   
29932         if(!this.canvasEl){
29933             return;
29934         }
29935         
29936         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29937         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29938         
29939         this.previewEl.setLeft(pw);
29940         this.previewEl.setTop(ph);
29941         
29942     },
29943     
29944     onMouseDown : function(e)
29945     {   
29946         e.stopEvent();
29947         
29948         this.dragable = true;
29949         this.pinching = false;
29950         
29951         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29952             this.dragable = false;
29953             return;
29954         }
29955         
29956         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29957         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29958         
29959     },
29960     
29961     onMouseMove : function(e)
29962     {   
29963         e.stopEvent();
29964         
29965         if(!this.canvasLoaded){
29966             return;
29967         }
29968         
29969         if (!this.dragable){
29970             return;
29971         }
29972         
29973         var minX = Math.ceil(this.thumbEl.getLeft(true));
29974         var minY = Math.ceil(this.thumbEl.getTop(true));
29975         
29976         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29977         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29978         
29979         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29980         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29981         
29982         x = x - this.mouseX;
29983         y = y - this.mouseY;
29984         
29985         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29986         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29987         
29988         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29989         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29990         
29991         this.previewEl.setLeft(bgX);
29992         this.previewEl.setTop(bgY);
29993         
29994         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29995         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29996     },
29997     
29998     onMouseUp : function(e)
29999     {   
30000         e.stopEvent();
30001         
30002         this.dragable = false;
30003     },
30004     
30005     onMouseWheel : function(e)
30006     {   
30007         e.stopEvent();
30008         
30009         this.startScale = this.scale;
30010         
30011         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30012         
30013         if(!this.zoomable()){
30014             this.scale = this.startScale;
30015             return;
30016         }
30017         
30018         this.draw();
30019         
30020         return;
30021     },
30022     
30023     zoomable : function()
30024     {
30025         var minScale = this.thumbEl.getWidth() / this.minWidth;
30026         
30027         if(this.minWidth < this.minHeight){
30028             minScale = this.thumbEl.getHeight() / this.minHeight;
30029         }
30030         
30031         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30032         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30033         
30034         if(
30035                 this.isDocument &&
30036                 (this.rotate == 0 || this.rotate == 180) && 
30037                 (
30038                     width > this.imageEl.OriginWidth || 
30039                     height > this.imageEl.OriginHeight ||
30040                     (width < this.minWidth && height < this.minHeight)
30041                 )
30042         ){
30043             return false;
30044         }
30045         
30046         if(
30047                 this.isDocument &&
30048                 (this.rotate == 90 || this.rotate == 270) && 
30049                 (
30050                     width > this.imageEl.OriginWidth || 
30051                     height > this.imageEl.OriginHeight ||
30052                     (width < this.minHeight && height < this.minWidth)
30053                 )
30054         ){
30055             return false;
30056         }
30057         
30058         if(
30059                 !this.isDocument &&
30060                 (this.rotate == 0 || this.rotate == 180) && 
30061                 (
30062                     width < this.minWidth || 
30063                     width > this.imageEl.OriginWidth || 
30064                     height < this.minHeight || 
30065                     height > this.imageEl.OriginHeight
30066                 )
30067         ){
30068             return false;
30069         }
30070         
30071         if(
30072                 !this.isDocument &&
30073                 (this.rotate == 90 || this.rotate == 270) && 
30074                 (
30075                     width < this.minHeight || 
30076                     width > this.imageEl.OriginWidth || 
30077                     height < this.minWidth || 
30078                     height > this.imageEl.OriginHeight
30079                 )
30080         ){
30081             return false;
30082         }
30083         
30084         return true;
30085         
30086     },
30087     
30088     onRotateLeft : function(e)
30089     {   
30090         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30091             
30092             var minScale = this.thumbEl.getWidth() / this.minWidth;
30093             
30094             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30095             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30096             
30097             this.startScale = this.scale;
30098             
30099             while (this.getScaleLevel() < minScale){
30100             
30101                 this.scale = this.scale + 1;
30102                 
30103                 if(!this.zoomable()){
30104                     break;
30105                 }
30106                 
30107                 if(
30108                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30109                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30110                 ){
30111                     continue;
30112                 }
30113                 
30114                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30115
30116                 this.draw();
30117                 
30118                 return;
30119             }
30120             
30121             this.scale = this.startScale;
30122             
30123             this.onRotateFail();
30124             
30125             return false;
30126         }
30127         
30128         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30129
30130         if(this.isDocument){
30131             this.setThumbBoxSize();
30132             this.setThumbBoxPosition();
30133             this.setCanvasPosition();
30134         }
30135         
30136         this.draw();
30137         
30138         this.fireEvent('rotate', this, 'left');
30139         
30140     },
30141     
30142     onRotateRight : function(e)
30143     {
30144         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30145             
30146             var minScale = this.thumbEl.getWidth() / this.minWidth;
30147         
30148             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30149             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30150             
30151             this.startScale = this.scale;
30152             
30153             while (this.getScaleLevel() < minScale){
30154             
30155                 this.scale = this.scale + 1;
30156                 
30157                 if(!this.zoomable()){
30158                     break;
30159                 }
30160                 
30161                 if(
30162                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30163                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30164                 ){
30165                     continue;
30166                 }
30167                 
30168                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30169
30170                 this.draw();
30171                 
30172                 return;
30173             }
30174             
30175             this.scale = this.startScale;
30176             
30177             this.onRotateFail();
30178             
30179             return false;
30180         }
30181         
30182         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30183
30184         if(this.isDocument){
30185             this.setThumbBoxSize();
30186             this.setThumbBoxPosition();
30187             this.setCanvasPosition();
30188         }
30189         
30190         this.draw();
30191         
30192         this.fireEvent('rotate', this, 'right');
30193     },
30194     
30195     onRotateFail : function()
30196     {
30197         this.errorEl.show(true);
30198         
30199         var _this = this;
30200         
30201         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30202     },
30203     
30204     draw : function()
30205     {
30206         this.previewEl.dom.innerHTML = '';
30207         
30208         var canvasEl = document.createElement("canvas");
30209         
30210         var contextEl = canvasEl.getContext("2d");
30211         
30212         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30213         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30214         var center = this.imageEl.OriginWidth / 2;
30215         
30216         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30217             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30218             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30219             center = this.imageEl.OriginHeight / 2;
30220         }
30221         
30222         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30223         
30224         contextEl.translate(center, center);
30225         contextEl.rotate(this.rotate * Math.PI / 180);
30226
30227         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30228         
30229         this.canvasEl = document.createElement("canvas");
30230         
30231         this.contextEl = this.canvasEl.getContext("2d");
30232         
30233         switch (this.rotate) {
30234             case 0 :
30235                 
30236                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30237                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30238                 
30239                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30240                 
30241                 break;
30242             case 90 : 
30243                 
30244                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30245                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30246                 
30247                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30248                     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);
30249                     break;
30250                 }
30251                 
30252                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30253                 
30254                 break;
30255             case 180 :
30256                 
30257                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30258                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30259                 
30260                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30261                     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);
30262                     break;
30263                 }
30264                 
30265                 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);
30266                 
30267                 break;
30268             case 270 :
30269                 
30270                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30271                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30272         
30273                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30274                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30275                     break;
30276                 }
30277                 
30278                 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);
30279                 
30280                 break;
30281             default : 
30282                 break;
30283         }
30284         
30285         this.previewEl.appendChild(this.canvasEl);
30286         
30287         this.setCanvasPosition();
30288     },
30289     
30290     crop : function()
30291     {
30292         if(!this.canvasLoaded){
30293             return;
30294         }
30295         
30296         var imageCanvas = document.createElement("canvas");
30297         
30298         var imageContext = imageCanvas.getContext("2d");
30299         
30300         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30301         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30302         
30303         var center = imageCanvas.width / 2;
30304         
30305         imageContext.translate(center, center);
30306         
30307         imageContext.rotate(this.rotate * Math.PI / 180);
30308         
30309         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30310         
30311         var canvas = document.createElement("canvas");
30312         
30313         var context = canvas.getContext("2d");
30314                 
30315         canvas.width = this.minWidth;
30316         canvas.height = this.minHeight;
30317
30318         switch (this.rotate) {
30319             case 0 :
30320                 
30321                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30322                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30323                 
30324                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30325                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30326                 
30327                 var targetWidth = this.minWidth - 2 * x;
30328                 var targetHeight = this.minHeight - 2 * y;
30329                 
30330                 var scale = 1;
30331                 
30332                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30333                     scale = targetWidth / width;
30334                 }
30335                 
30336                 if(x > 0 && y == 0){
30337                     scale = targetHeight / height;
30338                 }
30339                 
30340                 if(x > 0 && y > 0){
30341                     scale = targetWidth / width;
30342                     
30343                     if(width < height){
30344                         scale = targetHeight / height;
30345                     }
30346                 }
30347                 
30348                 context.scale(scale, scale);
30349                 
30350                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30351                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30352
30353                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30354                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30355
30356                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30357                 
30358                 break;
30359             case 90 : 
30360                 
30361                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30362                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30363                 
30364                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30365                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30366                 
30367                 var targetWidth = this.minWidth - 2 * x;
30368                 var targetHeight = this.minHeight - 2 * y;
30369                 
30370                 var scale = 1;
30371                 
30372                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30373                     scale = targetWidth / width;
30374                 }
30375                 
30376                 if(x > 0 && y == 0){
30377                     scale = targetHeight / height;
30378                 }
30379                 
30380                 if(x > 0 && y > 0){
30381                     scale = targetWidth / width;
30382                     
30383                     if(width < height){
30384                         scale = targetHeight / height;
30385                     }
30386                 }
30387                 
30388                 context.scale(scale, scale);
30389                 
30390                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30391                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30392
30393                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30394                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30395                 
30396                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30397                 
30398                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30399                 
30400                 break;
30401             case 180 :
30402                 
30403                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30404                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30405                 
30406                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30407                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30408                 
30409                 var targetWidth = this.minWidth - 2 * x;
30410                 var targetHeight = this.minHeight - 2 * y;
30411                 
30412                 var scale = 1;
30413                 
30414                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30415                     scale = targetWidth / width;
30416                 }
30417                 
30418                 if(x > 0 && y == 0){
30419                     scale = targetHeight / height;
30420                 }
30421                 
30422                 if(x > 0 && y > 0){
30423                     scale = targetWidth / width;
30424                     
30425                     if(width < height){
30426                         scale = targetHeight / height;
30427                     }
30428                 }
30429                 
30430                 context.scale(scale, scale);
30431                 
30432                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30433                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30434
30435                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30436                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30437
30438                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30439                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30440                 
30441                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30442                 
30443                 break;
30444             case 270 :
30445                 
30446                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30447                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30448                 
30449                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30450                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30451                 
30452                 var targetWidth = this.minWidth - 2 * x;
30453                 var targetHeight = this.minHeight - 2 * y;
30454                 
30455                 var scale = 1;
30456                 
30457                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30458                     scale = targetWidth / width;
30459                 }
30460                 
30461                 if(x > 0 && y == 0){
30462                     scale = targetHeight / height;
30463                 }
30464                 
30465                 if(x > 0 && y > 0){
30466                     scale = targetWidth / width;
30467                     
30468                     if(width < height){
30469                         scale = targetHeight / height;
30470                     }
30471                 }
30472                 
30473                 context.scale(scale, scale);
30474                 
30475                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30476                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30477
30478                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30479                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30480                 
30481                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30482                 
30483                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30484                 
30485                 break;
30486             default : 
30487                 break;
30488         }
30489         
30490         this.cropData = canvas.toDataURL(this.cropType);
30491         
30492         if(this.fireEvent('crop', this, this.cropData) !== false){
30493             this.process(this.file, this.cropData);
30494         }
30495         
30496         return;
30497         
30498     },
30499     
30500     setThumbBoxSize : function()
30501     {
30502         var width, height;
30503         
30504         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30505             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30506             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30507             
30508             this.minWidth = width;
30509             this.minHeight = height;
30510             
30511             if(this.rotate == 90 || this.rotate == 270){
30512                 this.minWidth = height;
30513                 this.minHeight = width;
30514             }
30515         }
30516         
30517         height = 300;
30518         width = Math.ceil(this.minWidth * height / this.minHeight);
30519         
30520         if(this.minWidth > this.minHeight){
30521             width = 300;
30522             height = Math.ceil(this.minHeight * width / this.minWidth);
30523         }
30524         
30525         this.thumbEl.setStyle({
30526             width : width + 'px',
30527             height : height + 'px'
30528         });
30529
30530         return;
30531             
30532     },
30533     
30534     setThumbBoxPosition : function()
30535     {
30536         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30537         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30538         
30539         this.thumbEl.setLeft(x);
30540         this.thumbEl.setTop(y);
30541         
30542     },
30543     
30544     baseRotateLevel : function()
30545     {
30546         this.baseRotate = 1;
30547         
30548         if(
30549                 typeof(this.exif) != 'undefined' &&
30550                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30551                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30552         ){
30553             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30554         }
30555         
30556         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30557         
30558     },
30559     
30560     baseScaleLevel : function()
30561     {
30562         var width, height;
30563         
30564         if(this.isDocument){
30565             
30566             if(this.baseRotate == 6 || this.baseRotate == 8){
30567             
30568                 height = this.thumbEl.getHeight();
30569                 this.baseScale = height / this.imageEl.OriginWidth;
30570
30571                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30572                     width = this.thumbEl.getWidth();
30573                     this.baseScale = width / this.imageEl.OriginHeight;
30574                 }
30575
30576                 return;
30577             }
30578
30579             height = this.thumbEl.getHeight();
30580             this.baseScale = height / this.imageEl.OriginHeight;
30581
30582             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30583                 width = this.thumbEl.getWidth();
30584                 this.baseScale = width / this.imageEl.OriginWidth;
30585             }
30586
30587             return;
30588         }
30589         
30590         if(this.baseRotate == 6 || this.baseRotate == 8){
30591             
30592             width = this.thumbEl.getHeight();
30593             this.baseScale = width / this.imageEl.OriginHeight;
30594             
30595             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30596                 height = this.thumbEl.getWidth();
30597                 this.baseScale = height / this.imageEl.OriginHeight;
30598             }
30599             
30600             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30601                 height = this.thumbEl.getWidth();
30602                 this.baseScale = height / this.imageEl.OriginHeight;
30603                 
30604                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30605                     width = this.thumbEl.getHeight();
30606                     this.baseScale = width / this.imageEl.OriginWidth;
30607                 }
30608             }
30609             
30610             return;
30611         }
30612         
30613         width = this.thumbEl.getWidth();
30614         this.baseScale = width / this.imageEl.OriginWidth;
30615         
30616         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30617             height = this.thumbEl.getHeight();
30618             this.baseScale = height / this.imageEl.OriginHeight;
30619         }
30620         
30621         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30622             
30623             height = this.thumbEl.getHeight();
30624             this.baseScale = height / this.imageEl.OriginHeight;
30625             
30626             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30627                 width = this.thumbEl.getWidth();
30628                 this.baseScale = width / this.imageEl.OriginWidth;
30629             }
30630             
30631         }
30632         
30633         return;
30634     },
30635     
30636     getScaleLevel : function()
30637     {
30638         return this.baseScale * Math.pow(1.1, this.scale);
30639     },
30640     
30641     onTouchStart : function(e)
30642     {
30643         if(!this.canvasLoaded){
30644             this.beforeSelectFile(e);
30645             return;
30646         }
30647         
30648         var touches = e.browserEvent.touches;
30649         
30650         if(!touches){
30651             return;
30652         }
30653         
30654         if(touches.length == 1){
30655             this.onMouseDown(e);
30656             return;
30657         }
30658         
30659         if(touches.length != 2){
30660             return;
30661         }
30662         
30663         var coords = [];
30664         
30665         for(var i = 0, finger; finger = touches[i]; i++){
30666             coords.push(finger.pageX, finger.pageY);
30667         }
30668         
30669         var x = Math.pow(coords[0] - coords[2], 2);
30670         var y = Math.pow(coords[1] - coords[3], 2);
30671         
30672         this.startDistance = Math.sqrt(x + y);
30673         
30674         this.startScale = this.scale;
30675         
30676         this.pinching = true;
30677         this.dragable = false;
30678         
30679     },
30680     
30681     onTouchMove : function(e)
30682     {
30683         if(!this.pinching && !this.dragable){
30684             return;
30685         }
30686         
30687         var touches = e.browserEvent.touches;
30688         
30689         if(!touches){
30690             return;
30691         }
30692         
30693         if(this.dragable){
30694             this.onMouseMove(e);
30695             return;
30696         }
30697         
30698         var coords = [];
30699         
30700         for(var i = 0, finger; finger = touches[i]; i++){
30701             coords.push(finger.pageX, finger.pageY);
30702         }
30703         
30704         var x = Math.pow(coords[0] - coords[2], 2);
30705         var y = Math.pow(coords[1] - coords[3], 2);
30706         
30707         this.endDistance = Math.sqrt(x + y);
30708         
30709         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30710         
30711         if(!this.zoomable()){
30712             this.scale = this.startScale;
30713             return;
30714         }
30715         
30716         this.draw();
30717         
30718     },
30719     
30720     onTouchEnd : function(e)
30721     {
30722         this.pinching = false;
30723         this.dragable = false;
30724         
30725     },
30726     
30727     process : function(file, crop)
30728     {
30729         if(this.loadMask){
30730             this.maskEl.mask(this.loadingText);
30731         }
30732         
30733         this.xhr = new XMLHttpRequest();
30734         
30735         file.xhr = this.xhr;
30736
30737         this.xhr.open(this.method, this.url, true);
30738         
30739         var headers = {
30740             "Accept": "application/json",
30741             "Cache-Control": "no-cache",
30742             "X-Requested-With": "XMLHttpRequest"
30743         };
30744         
30745         for (var headerName in headers) {
30746             var headerValue = headers[headerName];
30747             if (headerValue) {
30748                 this.xhr.setRequestHeader(headerName, headerValue);
30749             }
30750         }
30751         
30752         var _this = this;
30753         
30754         this.xhr.onload = function()
30755         {
30756             _this.xhrOnLoad(_this.xhr);
30757         }
30758         
30759         this.xhr.onerror = function()
30760         {
30761             _this.xhrOnError(_this.xhr);
30762         }
30763         
30764         var formData = new FormData();
30765
30766         formData.append('returnHTML', 'NO');
30767         
30768         if(crop){
30769             formData.append('crop', crop);
30770         }
30771         
30772         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30773             formData.append(this.paramName, file, file.name);
30774         }
30775         
30776         if(typeof(file.filename) != 'undefined'){
30777             formData.append('filename', file.filename);
30778         }
30779         
30780         if(typeof(file.mimetype) != 'undefined'){
30781             formData.append('mimetype', file.mimetype);
30782         }
30783         
30784         if(this.fireEvent('arrange', this, formData) != false){
30785             this.xhr.send(formData);
30786         };
30787     },
30788     
30789     xhrOnLoad : function(xhr)
30790     {
30791         if(this.loadMask){
30792             this.maskEl.unmask();
30793         }
30794         
30795         if (xhr.readyState !== 4) {
30796             this.fireEvent('exception', this, xhr);
30797             return;
30798         }
30799
30800         var response = Roo.decode(xhr.responseText);
30801         
30802         if(!response.success){
30803             this.fireEvent('exception', this, xhr);
30804             return;
30805         }
30806         
30807         var response = Roo.decode(xhr.responseText);
30808         
30809         this.fireEvent('upload', this, response);
30810         
30811     },
30812     
30813     xhrOnError : function()
30814     {
30815         if(this.loadMask){
30816             this.maskEl.unmask();
30817         }
30818         
30819         Roo.log('xhr on error');
30820         
30821         var response = Roo.decode(xhr.responseText);
30822           
30823         Roo.log(response);
30824         
30825     },
30826     
30827     prepare : function(file)
30828     {   
30829         if(this.loadMask){
30830             this.maskEl.mask(this.loadingText);
30831         }
30832         
30833         this.file = false;
30834         this.exif = {};
30835         
30836         if(typeof(file) === 'string'){
30837             this.loadCanvas(file);
30838             return;
30839         }
30840         
30841         if(!file || !this.urlAPI){
30842             return;
30843         }
30844         
30845         this.file = file;
30846         this.cropType = file.type;
30847         
30848         var _this = this;
30849         
30850         if(this.fireEvent('prepare', this, this.file) != false){
30851             
30852             var reader = new FileReader();
30853             
30854             reader.onload = function (e) {
30855                 if (e.target.error) {
30856                     Roo.log(e.target.error);
30857                     return;
30858                 }
30859                 
30860                 var buffer = e.target.result,
30861                     dataView = new DataView(buffer),
30862                     offset = 2,
30863                     maxOffset = dataView.byteLength - 4,
30864                     markerBytes,
30865                     markerLength;
30866                 
30867                 if (dataView.getUint16(0) === 0xffd8) {
30868                     while (offset < maxOffset) {
30869                         markerBytes = dataView.getUint16(offset);
30870                         
30871                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30872                             markerLength = dataView.getUint16(offset + 2) + 2;
30873                             if (offset + markerLength > dataView.byteLength) {
30874                                 Roo.log('Invalid meta data: Invalid segment size.');
30875                                 break;
30876                             }
30877                             
30878                             if(markerBytes == 0xffe1){
30879                                 _this.parseExifData(
30880                                     dataView,
30881                                     offset,
30882                                     markerLength
30883                                 );
30884                             }
30885                             
30886                             offset += markerLength;
30887                             
30888                             continue;
30889                         }
30890                         
30891                         break;
30892                     }
30893                     
30894                 }
30895                 
30896                 var url = _this.urlAPI.createObjectURL(_this.file);
30897                 
30898                 _this.loadCanvas(url);
30899                 
30900                 return;
30901             }
30902             
30903             reader.readAsArrayBuffer(this.file);
30904             
30905         }
30906         
30907     },
30908     
30909     parseExifData : function(dataView, offset, length)
30910     {
30911         var tiffOffset = offset + 10,
30912             littleEndian,
30913             dirOffset;
30914     
30915         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30916             // No Exif data, might be XMP data instead
30917             return;
30918         }
30919         
30920         // Check for the ASCII code for "Exif" (0x45786966):
30921         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30922             // No Exif data, might be XMP data instead
30923             return;
30924         }
30925         if (tiffOffset + 8 > dataView.byteLength) {
30926             Roo.log('Invalid Exif data: Invalid segment size.');
30927             return;
30928         }
30929         // Check for the two null bytes:
30930         if (dataView.getUint16(offset + 8) !== 0x0000) {
30931             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30932             return;
30933         }
30934         // Check the byte alignment:
30935         switch (dataView.getUint16(tiffOffset)) {
30936         case 0x4949:
30937             littleEndian = true;
30938             break;
30939         case 0x4D4D:
30940             littleEndian = false;
30941             break;
30942         default:
30943             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30944             return;
30945         }
30946         // Check for the TIFF tag marker (0x002A):
30947         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30948             Roo.log('Invalid Exif data: Missing TIFF marker.');
30949             return;
30950         }
30951         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30952         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30953         
30954         this.parseExifTags(
30955             dataView,
30956             tiffOffset,
30957             tiffOffset + dirOffset,
30958             littleEndian
30959         );
30960     },
30961     
30962     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30963     {
30964         var tagsNumber,
30965             dirEndOffset,
30966             i;
30967         if (dirOffset + 6 > dataView.byteLength) {
30968             Roo.log('Invalid Exif data: Invalid directory offset.');
30969             return;
30970         }
30971         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30972         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30973         if (dirEndOffset + 4 > dataView.byteLength) {
30974             Roo.log('Invalid Exif data: Invalid directory size.');
30975             return;
30976         }
30977         for (i = 0; i < tagsNumber; i += 1) {
30978             this.parseExifTag(
30979                 dataView,
30980                 tiffOffset,
30981                 dirOffset + 2 + 12 * i, // tag offset
30982                 littleEndian
30983             );
30984         }
30985         // Return the offset to the next directory:
30986         return dataView.getUint32(dirEndOffset, littleEndian);
30987     },
30988     
30989     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30990     {
30991         var tag = dataView.getUint16(offset, littleEndian);
30992         
30993         this.exif[tag] = this.getExifValue(
30994             dataView,
30995             tiffOffset,
30996             offset,
30997             dataView.getUint16(offset + 2, littleEndian), // tag type
30998             dataView.getUint32(offset + 4, littleEndian), // tag length
30999             littleEndian
31000         );
31001     },
31002     
31003     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31004     {
31005         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31006             tagSize,
31007             dataOffset,
31008             values,
31009             i,
31010             str,
31011             c;
31012     
31013         if (!tagType) {
31014             Roo.log('Invalid Exif data: Invalid tag type.');
31015             return;
31016         }
31017         
31018         tagSize = tagType.size * length;
31019         // Determine if the value is contained in the dataOffset bytes,
31020         // or if the value at the dataOffset is a pointer to the actual data:
31021         dataOffset = tagSize > 4 ?
31022                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31023         if (dataOffset + tagSize > dataView.byteLength) {
31024             Roo.log('Invalid Exif data: Invalid data offset.');
31025             return;
31026         }
31027         if (length === 1) {
31028             return tagType.getValue(dataView, dataOffset, littleEndian);
31029         }
31030         values = [];
31031         for (i = 0; i < length; i += 1) {
31032             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31033         }
31034         
31035         if (tagType.ascii) {
31036             str = '';
31037             // Concatenate the chars:
31038             for (i = 0; i < values.length; i += 1) {
31039                 c = values[i];
31040                 // Ignore the terminating NULL byte(s):
31041                 if (c === '\u0000') {
31042                     break;
31043                 }
31044                 str += c;
31045             }
31046             return str;
31047         }
31048         return values;
31049     }
31050     
31051 });
31052
31053 Roo.apply(Roo.bootstrap.UploadCropbox, {
31054     tags : {
31055         'Orientation': 0x0112
31056     },
31057     
31058     Orientation: {
31059             1: 0, //'top-left',
31060 //            2: 'top-right',
31061             3: 180, //'bottom-right',
31062 //            4: 'bottom-left',
31063 //            5: 'left-top',
31064             6: 90, //'right-top',
31065 //            7: 'right-bottom',
31066             8: 270 //'left-bottom'
31067     },
31068     
31069     exifTagTypes : {
31070         // byte, 8-bit unsigned int:
31071         1: {
31072             getValue: function (dataView, dataOffset) {
31073                 return dataView.getUint8(dataOffset);
31074             },
31075             size: 1
31076         },
31077         // ascii, 8-bit byte:
31078         2: {
31079             getValue: function (dataView, dataOffset) {
31080                 return String.fromCharCode(dataView.getUint8(dataOffset));
31081             },
31082             size: 1,
31083             ascii: true
31084         },
31085         // short, 16 bit int:
31086         3: {
31087             getValue: function (dataView, dataOffset, littleEndian) {
31088                 return dataView.getUint16(dataOffset, littleEndian);
31089             },
31090             size: 2
31091         },
31092         // long, 32 bit int:
31093         4: {
31094             getValue: function (dataView, dataOffset, littleEndian) {
31095                 return dataView.getUint32(dataOffset, littleEndian);
31096             },
31097             size: 4
31098         },
31099         // rational = two long values, first is numerator, second is denominator:
31100         5: {
31101             getValue: function (dataView, dataOffset, littleEndian) {
31102                 return dataView.getUint32(dataOffset, littleEndian) /
31103                     dataView.getUint32(dataOffset + 4, littleEndian);
31104             },
31105             size: 8
31106         },
31107         // slong, 32 bit signed int:
31108         9: {
31109             getValue: function (dataView, dataOffset, littleEndian) {
31110                 return dataView.getInt32(dataOffset, littleEndian);
31111             },
31112             size: 4
31113         },
31114         // srational, two slongs, first is numerator, second is denominator:
31115         10: {
31116             getValue: function (dataView, dataOffset, littleEndian) {
31117                 return dataView.getInt32(dataOffset, littleEndian) /
31118                     dataView.getInt32(dataOffset + 4, littleEndian);
31119             },
31120             size: 8
31121         }
31122     },
31123     
31124     footer : {
31125         STANDARD : [
31126             {
31127                 tag : 'div',
31128                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31129                 action : 'rotate-left',
31130                 cn : [
31131                     {
31132                         tag : 'button',
31133                         cls : 'btn btn-default',
31134                         html : '<i class="fa fa-undo"></i>'
31135                     }
31136                 ]
31137             },
31138             {
31139                 tag : 'div',
31140                 cls : 'btn-group roo-upload-cropbox-picture',
31141                 action : 'picture',
31142                 cn : [
31143                     {
31144                         tag : 'button',
31145                         cls : 'btn btn-default',
31146                         html : '<i class="fa fa-picture-o"></i>'
31147                     }
31148                 ]
31149             },
31150             {
31151                 tag : 'div',
31152                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31153                 action : 'rotate-right',
31154                 cn : [
31155                     {
31156                         tag : 'button',
31157                         cls : 'btn btn-default',
31158                         html : '<i class="fa fa-repeat"></i>'
31159                     }
31160                 ]
31161             }
31162         ],
31163         DOCUMENT : [
31164             {
31165                 tag : 'div',
31166                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31167                 action : 'rotate-left',
31168                 cn : [
31169                     {
31170                         tag : 'button',
31171                         cls : 'btn btn-default',
31172                         html : '<i class="fa fa-undo"></i>'
31173                     }
31174                 ]
31175             },
31176             {
31177                 tag : 'div',
31178                 cls : 'btn-group roo-upload-cropbox-download',
31179                 action : 'download',
31180                 cn : [
31181                     {
31182                         tag : 'button',
31183                         cls : 'btn btn-default',
31184                         html : '<i class="fa fa-download"></i>'
31185                     }
31186                 ]
31187             },
31188             {
31189                 tag : 'div',
31190                 cls : 'btn-group roo-upload-cropbox-crop',
31191                 action : 'crop',
31192                 cn : [
31193                     {
31194                         tag : 'button',
31195                         cls : 'btn btn-default',
31196                         html : '<i class="fa fa-crop"></i>'
31197                     }
31198                 ]
31199             },
31200             {
31201                 tag : 'div',
31202                 cls : 'btn-group roo-upload-cropbox-trash',
31203                 action : 'trash',
31204                 cn : [
31205                     {
31206                         tag : 'button',
31207                         cls : 'btn btn-default',
31208                         html : '<i class="fa fa-trash"></i>'
31209                     }
31210                 ]
31211             },
31212             {
31213                 tag : 'div',
31214                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31215                 action : 'rotate-right',
31216                 cn : [
31217                     {
31218                         tag : 'button',
31219                         cls : 'btn btn-default',
31220                         html : '<i class="fa fa-repeat"></i>'
31221                     }
31222                 ]
31223             }
31224         ],
31225         ROTATOR : [
31226             {
31227                 tag : 'div',
31228                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31229                 action : 'rotate-left',
31230                 cn : [
31231                     {
31232                         tag : 'button',
31233                         cls : 'btn btn-default',
31234                         html : '<i class="fa fa-undo"></i>'
31235                     }
31236                 ]
31237             },
31238             {
31239                 tag : 'div',
31240                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31241                 action : 'rotate-right',
31242                 cn : [
31243                     {
31244                         tag : 'button',
31245                         cls : 'btn btn-default',
31246                         html : '<i class="fa fa-repeat"></i>'
31247                     }
31248                 ]
31249             }
31250         ]
31251     }
31252 });
31253
31254 /*
31255 * Licence: LGPL
31256 */
31257
31258 /**
31259  * @class Roo.bootstrap.DocumentManager
31260  * @extends Roo.bootstrap.Component
31261  * Bootstrap DocumentManager class
31262  * @cfg {String} paramName default 'imageUpload'
31263  * @cfg {String} toolTipName default 'filename'
31264  * @cfg {String} method default POST
31265  * @cfg {String} url action url
31266  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31267  * @cfg {Boolean} multiple multiple upload default true
31268  * @cfg {Number} thumbSize default 300
31269  * @cfg {String} fieldLabel
31270  * @cfg {Number} labelWidth default 4
31271  * @cfg {String} labelAlign (left|top) default left
31272  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31273 * @cfg {Number} labellg set the width of label (1-12)
31274  * @cfg {Number} labelmd set the width of label (1-12)
31275  * @cfg {Number} labelsm set the width of label (1-12)
31276  * @cfg {Number} labelxs set the width of label (1-12)
31277  * 
31278  * @constructor
31279  * Create a new DocumentManager
31280  * @param {Object} config The config object
31281  */
31282
31283 Roo.bootstrap.DocumentManager = function(config){
31284     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31285     
31286     this.files = [];
31287     this.delegates = [];
31288     
31289     this.addEvents({
31290         /**
31291          * @event initial
31292          * Fire when initial the DocumentManager
31293          * @param {Roo.bootstrap.DocumentManager} this
31294          */
31295         "initial" : true,
31296         /**
31297          * @event inspect
31298          * inspect selected file
31299          * @param {Roo.bootstrap.DocumentManager} this
31300          * @param {File} file
31301          */
31302         "inspect" : true,
31303         /**
31304          * @event exception
31305          * Fire when xhr load exception
31306          * @param {Roo.bootstrap.DocumentManager} this
31307          * @param {XMLHttpRequest} xhr
31308          */
31309         "exception" : true,
31310         /**
31311          * @event afterupload
31312          * Fire when xhr load exception
31313          * @param {Roo.bootstrap.DocumentManager} this
31314          * @param {XMLHttpRequest} xhr
31315          */
31316         "afterupload" : true,
31317         /**
31318          * @event prepare
31319          * prepare the form data
31320          * @param {Roo.bootstrap.DocumentManager} this
31321          * @param {Object} formData
31322          */
31323         "prepare" : true,
31324         /**
31325          * @event remove
31326          * Fire when remove the file
31327          * @param {Roo.bootstrap.DocumentManager} this
31328          * @param {Object} file
31329          */
31330         "remove" : true,
31331         /**
31332          * @event refresh
31333          * Fire after refresh the file
31334          * @param {Roo.bootstrap.DocumentManager} this
31335          */
31336         "refresh" : true,
31337         /**
31338          * @event click
31339          * Fire after click the image
31340          * @param {Roo.bootstrap.DocumentManager} this
31341          * @param {Object} file
31342          */
31343         "click" : true,
31344         /**
31345          * @event edit
31346          * Fire when upload a image and editable set to true
31347          * @param {Roo.bootstrap.DocumentManager} this
31348          * @param {Object} file
31349          */
31350         "edit" : true,
31351         /**
31352          * @event beforeselectfile
31353          * Fire before select file
31354          * @param {Roo.bootstrap.DocumentManager} this
31355          */
31356         "beforeselectfile" : true,
31357         /**
31358          * @event process
31359          * Fire before process file
31360          * @param {Roo.bootstrap.DocumentManager} this
31361          * @param {Object} file
31362          */
31363         "process" : true,
31364         /**
31365          * @event previewrendered
31366          * Fire when preview rendered
31367          * @param {Roo.bootstrap.DocumentManager} this
31368          * @param {Object} file
31369          */
31370         "previewrendered" : true,
31371         /**
31372          */
31373         "previewResize" : true
31374         
31375     });
31376 };
31377
31378 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31379     
31380     boxes : 0,
31381     inputName : '',
31382     thumbSize : 300,
31383     multiple : true,
31384     files : false,
31385     method : 'POST',
31386     url : '',
31387     paramName : 'imageUpload',
31388     toolTipName : 'filename',
31389     fieldLabel : '',
31390     labelWidth : 4,
31391     labelAlign : 'left',
31392     editable : true,
31393     delegates : false,
31394     xhr : false, 
31395     
31396     labellg : 0,
31397     labelmd : 0,
31398     labelsm : 0,
31399     labelxs : 0,
31400     
31401     getAutoCreate : function()
31402     {   
31403         var managerWidget = {
31404             tag : 'div',
31405             cls : 'roo-document-manager',
31406             cn : [
31407                 {
31408                     tag : 'input',
31409                     cls : 'roo-document-manager-selector',
31410                     type : 'file'
31411                 },
31412                 {
31413                     tag : 'div',
31414                     cls : 'roo-document-manager-uploader',
31415                     cn : [
31416                         {
31417                             tag : 'div',
31418                             cls : 'roo-document-manager-upload-btn',
31419                             html : '<i class="fa fa-plus"></i>'
31420                         }
31421                     ]
31422                     
31423                 }
31424             ]
31425         };
31426         
31427         var content = [
31428             {
31429                 tag : 'div',
31430                 cls : 'column col-md-12',
31431                 cn : managerWidget
31432             }
31433         ];
31434         
31435         if(this.fieldLabel.length){
31436             
31437             content = [
31438                 {
31439                     tag : 'div',
31440                     cls : 'column col-md-12',
31441                     html : this.fieldLabel
31442                 },
31443                 {
31444                     tag : 'div',
31445                     cls : 'column col-md-12',
31446                     cn : managerWidget
31447                 }
31448             ];
31449
31450             if(this.labelAlign == 'left'){
31451                 content = [
31452                     {
31453                         tag : 'div',
31454                         cls : 'column',
31455                         html : this.fieldLabel
31456                     },
31457                     {
31458                         tag : 'div',
31459                         cls : 'column',
31460                         cn : managerWidget
31461                     }
31462                 ];
31463                 
31464                 if(this.labelWidth > 12){
31465                     content[0].style = "width: " + this.labelWidth + 'px';
31466                 }
31467
31468                 if(this.labelWidth < 13 && this.labelmd == 0){
31469                     this.labelmd = this.labelWidth;
31470                 }
31471
31472                 if(this.labellg > 0){
31473                     content[0].cls += ' col-lg-' + this.labellg;
31474                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31475                 }
31476
31477                 if(this.labelmd > 0){
31478                     content[0].cls += ' col-md-' + this.labelmd;
31479                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31480                 }
31481
31482                 if(this.labelsm > 0){
31483                     content[0].cls += ' col-sm-' + this.labelsm;
31484                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31485                 }
31486
31487                 if(this.labelxs > 0){
31488                     content[0].cls += ' col-xs-' + this.labelxs;
31489                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31490                 }
31491                 
31492             }
31493         }
31494         
31495         var cfg = {
31496             tag : 'div',
31497             cls : 'row clearfix',
31498             cn : content
31499         };
31500         
31501         return cfg;
31502         
31503     },
31504     
31505     initEvents : function()
31506     {
31507         this.managerEl = this.el.select('.roo-document-manager', true).first();
31508         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31509         
31510         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31511         this.selectorEl.hide();
31512         
31513         if(this.multiple){
31514             this.selectorEl.attr('multiple', 'multiple');
31515         }
31516         
31517         this.selectorEl.on('change', this.onFileSelected, this);
31518         
31519         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31520         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31521         
31522         this.uploader.on('click', this.onUploaderClick, this);
31523         
31524         this.renderProgressDialog();
31525         
31526         var _this = this;
31527         
31528         window.addEventListener("resize", function() { _this.refresh(); } );
31529         
31530         this.fireEvent('initial', this);
31531     },
31532     
31533     renderProgressDialog : function()
31534     {
31535         var _this = this;
31536         
31537         this.progressDialog = new Roo.bootstrap.Modal({
31538             cls : 'roo-document-manager-progress-dialog',
31539             allow_close : false,
31540             animate : false,
31541             title : '',
31542             buttons : [
31543                 {
31544                     name  :'cancel',
31545                     weight : 'danger',
31546                     html : 'Cancel'
31547                 }
31548             ], 
31549             listeners : { 
31550                 btnclick : function() {
31551                     _this.uploadCancel();
31552                     this.hide();
31553                 }
31554             }
31555         });
31556          
31557         this.progressDialog.render(Roo.get(document.body));
31558          
31559         this.progress = new Roo.bootstrap.Progress({
31560             cls : 'roo-document-manager-progress',
31561             active : true,
31562             striped : true
31563         });
31564         
31565         this.progress.render(this.progressDialog.getChildContainer());
31566         
31567         this.progressBar = new Roo.bootstrap.ProgressBar({
31568             cls : 'roo-document-manager-progress-bar',
31569             aria_valuenow : 0,
31570             aria_valuemin : 0,
31571             aria_valuemax : 12,
31572             panel : 'success'
31573         });
31574         
31575         this.progressBar.render(this.progress.getChildContainer());
31576     },
31577     
31578     onUploaderClick : function(e)
31579     {
31580         e.preventDefault();
31581      
31582         if(this.fireEvent('beforeselectfile', this) != false){
31583             this.selectorEl.dom.click();
31584         }
31585         
31586     },
31587     
31588     onFileSelected : function(e)
31589     {
31590         e.preventDefault();
31591         
31592         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31593             return;
31594         }
31595         
31596         Roo.each(this.selectorEl.dom.files, function(file){
31597             if(this.fireEvent('inspect', this, file) != false){
31598                 this.files.push(file);
31599             }
31600         }, this);
31601         
31602         this.queue();
31603         
31604     },
31605     
31606     queue : function()
31607     {
31608         this.selectorEl.dom.value = '';
31609         
31610         if(!this.files || !this.files.length){
31611             return;
31612         }
31613         
31614         if(this.boxes > 0 && this.files.length > this.boxes){
31615             this.files = this.files.slice(0, this.boxes);
31616         }
31617         
31618         this.uploader.show();
31619         
31620         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31621             this.uploader.hide();
31622         }
31623         
31624         var _this = this;
31625         
31626         var files = [];
31627         
31628         var docs = [];
31629         
31630         Roo.each(this.files, function(file){
31631             
31632             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31633                 var f = this.renderPreview(file);
31634                 files.push(f);
31635                 return;
31636             }
31637             
31638             if(file.type.indexOf('image') != -1){
31639                 this.delegates.push(
31640                     (function(){
31641                         _this.process(file);
31642                     }).createDelegate(this)
31643                 );
31644         
31645                 return;
31646             }
31647             
31648             docs.push(
31649                 (function(){
31650                     _this.process(file);
31651                 }).createDelegate(this)
31652             );
31653             
31654         }, this);
31655         
31656         this.files = files;
31657         
31658         this.delegates = this.delegates.concat(docs);
31659         
31660         if(!this.delegates.length){
31661             this.refresh();
31662             return;
31663         }
31664         
31665         this.progressBar.aria_valuemax = this.delegates.length;
31666         
31667         this.arrange();
31668         
31669         return;
31670     },
31671     
31672     arrange : function()
31673     {
31674         if(!this.delegates.length){
31675             this.progressDialog.hide();
31676             this.refresh();
31677             return;
31678         }
31679         
31680         var delegate = this.delegates.shift();
31681         
31682         this.progressDialog.show();
31683         
31684         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31685         
31686         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31687         
31688         delegate();
31689     },
31690     
31691     refresh : function()
31692     {
31693         this.uploader.show();
31694         
31695         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31696             this.uploader.hide();
31697         }
31698         
31699         Roo.isTouch ? this.closable(false) : this.closable(true);
31700         
31701         this.fireEvent('refresh', this);
31702     },
31703     
31704     onRemove : function(e, el, o)
31705     {
31706         e.preventDefault();
31707         
31708         this.fireEvent('remove', this, o);
31709         
31710     },
31711     
31712     remove : function(o)
31713     {
31714         var files = [];
31715         
31716         Roo.each(this.files, function(file){
31717             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31718                 files.push(file);
31719                 return;
31720             }
31721
31722             o.target.remove();
31723
31724         }, this);
31725         
31726         this.files = files;
31727         
31728         this.refresh();
31729     },
31730     
31731     clear : function()
31732     {
31733         Roo.each(this.files, function(file){
31734             if(!file.target){
31735                 return;
31736             }
31737             
31738             file.target.remove();
31739
31740         }, this);
31741         
31742         this.files = [];
31743         
31744         this.refresh();
31745     },
31746     
31747     onClick : function(e, el, o)
31748     {
31749         e.preventDefault();
31750         
31751         this.fireEvent('click', this, o);
31752         
31753     },
31754     
31755     closable : function(closable)
31756     {
31757         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31758             
31759             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31760             
31761             if(closable){
31762                 el.show();
31763                 return;
31764             }
31765             
31766             el.hide();
31767             
31768         }, this);
31769     },
31770     
31771     xhrOnLoad : function(xhr)
31772     {
31773         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31774             el.remove();
31775         }, this);
31776         
31777         if (xhr.readyState !== 4) {
31778             this.arrange();
31779             this.fireEvent('exception', this, xhr);
31780             return;
31781         }
31782
31783         var response = Roo.decode(xhr.responseText);
31784         
31785         if(!response.success){
31786             this.arrange();
31787             this.fireEvent('exception', this, xhr);
31788             return;
31789         }
31790         
31791         var file = this.renderPreview(response.data);
31792         
31793         this.files.push(file);
31794         
31795         this.arrange();
31796         
31797         this.fireEvent('afterupload', this, xhr);
31798         
31799     },
31800     
31801     xhrOnError : function(xhr)
31802     {
31803         Roo.log('xhr on error');
31804         
31805         var response = Roo.decode(xhr.responseText);
31806           
31807         Roo.log(response);
31808         
31809         this.arrange();
31810     },
31811     
31812     process : function(file)
31813     {
31814         if(this.fireEvent('process', this, file) !== false){
31815             if(this.editable && file.type.indexOf('image') != -1){
31816                 this.fireEvent('edit', this, file);
31817                 return;
31818             }
31819
31820             this.uploadStart(file, false);
31821
31822             return;
31823         }
31824         
31825     },
31826     
31827     uploadStart : function(file, crop)
31828     {
31829         this.xhr = new XMLHttpRequest();
31830         
31831         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31832             this.arrange();
31833             return;
31834         }
31835         
31836         file.xhr = this.xhr;
31837             
31838         this.managerEl.createChild({
31839             tag : 'div',
31840             cls : 'roo-document-manager-loading',
31841             cn : [
31842                 {
31843                     tag : 'div',
31844                     tooltip : file.name,
31845                     cls : 'roo-document-manager-thumb',
31846                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31847                 }
31848             ]
31849
31850         });
31851
31852         this.xhr.open(this.method, this.url, true);
31853         
31854         var headers = {
31855             "Accept": "application/json",
31856             "Cache-Control": "no-cache",
31857             "X-Requested-With": "XMLHttpRequest"
31858         };
31859         
31860         for (var headerName in headers) {
31861             var headerValue = headers[headerName];
31862             if (headerValue) {
31863                 this.xhr.setRequestHeader(headerName, headerValue);
31864             }
31865         }
31866         
31867         var _this = this;
31868         
31869         this.xhr.onload = function()
31870         {
31871             _this.xhrOnLoad(_this.xhr);
31872         }
31873         
31874         this.xhr.onerror = function()
31875         {
31876             _this.xhrOnError(_this.xhr);
31877         }
31878         
31879         var formData = new FormData();
31880
31881         formData.append('returnHTML', 'NO');
31882         
31883         if(crop){
31884             formData.append('crop', crop);
31885         }
31886         
31887         formData.append(this.paramName, file, file.name);
31888         
31889         var options = {
31890             file : file, 
31891             manually : false
31892         };
31893         
31894         if(this.fireEvent('prepare', this, formData, options) != false){
31895             
31896             if(options.manually){
31897                 return;
31898             }
31899             
31900             this.xhr.send(formData);
31901             return;
31902         };
31903         
31904         this.uploadCancel();
31905     },
31906     
31907     uploadCancel : function()
31908     {
31909         if (this.xhr) {
31910             this.xhr.abort();
31911         }
31912         
31913         this.delegates = [];
31914         
31915         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31916             el.remove();
31917         }, this);
31918         
31919         this.arrange();
31920     },
31921     
31922     renderPreview : function(file)
31923     {
31924         if(typeof(file.target) != 'undefined' && file.target){
31925             return file;
31926         }
31927         
31928         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31929         
31930         var previewEl = this.managerEl.createChild({
31931             tag : 'div',
31932             cls : 'roo-document-manager-preview',
31933             cn : [
31934                 {
31935                     tag : 'div',
31936                     tooltip : file[this.toolTipName],
31937                     cls : 'roo-document-manager-thumb',
31938                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31939                 },
31940                 {
31941                     tag : 'button',
31942                     cls : 'close',
31943                     html : '<i class="fa fa-times-circle"></i>'
31944                 }
31945             ]
31946         });
31947
31948         var close = previewEl.select('button.close', true).first();
31949
31950         close.on('click', this.onRemove, this, file);
31951
31952         file.target = previewEl;
31953
31954         var image = previewEl.select('img', true).first();
31955         
31956         var _this = this;
31957         
31958         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31959         
31960         image.on('click', this.onClick, this, file);
31961         
31962         this.fireEvent('previewrendered', this, file);
31963         
31964         return file;
31965         
31966     },
31967     
31968     onPreviewLoad : function(file, image)
31969     {
31970         if(typeof(file.target) == 'undefined' || !file.target){
31971             return;
31972         }
31973         
31974         var width = image.dom.naturalWidth || image.dom.width;
31975         var height = image.dom.naturalHeight || image.dom.height;
31976         
31977         if(!this.previewResize) {
31978             return;
31979         }
31980         
31981         if(width > height){
31982             file.target.addClass('wide');
31983             return;
31984         }
31985         
31986         file.target.addClass('tall');
31987         return;
31988         
31989     },
31990     
31991     uploadFromSource : function(file, crop)
31992     {
31993         this.xhr = new XMLHttpRequest();
31994         
31995         this.managerEl.createChild({
31996             tag : 'div',
31997             cls : 'roo-document-manager-loading',
31998             cn : [
31999                 {
32000                     tag : 'div',
32001                     tooltip : file.name,
32002                     cls : 'roo-document-manager-thumb',
32003                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32004                 }
32005             ]
32006
32007         });
32008
32009         this.xhr.open(this.method, this.url, true);
32010         
32011         var headers = {
32012             "Accept": "application/json",
32013             "Cache-Control": "no-cache",
32014             "X-Requested-With": "XMLHttpRequest"
32015         };
32016         
32017         for (var headerName in headers) {
32018             var headerValue = headers[headerName];
32019             if (headerValue) {
32020                 this.xhr.setRequestHeader(headerName, headerValue);
32021             }
32022         }
32023         
32024         var _this = this;
32025         
32026         this.xhr.onload = function()
32027         {
32028             _this.xhrOnLoad(_this.xhr);
32029         }
32030         
32031         this.xhr.onerror = function()
32032         {
32033             _this.xhrOnError(_this.xhr);
32034         }
32035         
32036         var formData = new FormData();
32037
32038         formData.append('returnHTML', 'NO');
32039         
32040         formData.append('crop', crop);
32041         
32042         if(typeof(file.filename) != 'undefined'){
32043             formData.append('filename', file.filename);
32044         }
32045         
32046         if(typeof(file.mimetype) != 'undefined'){
32047             formData.append('mimetype', file.mimetype);
32048         }
32049         
32050         Roo.log(formData);
32051         
32052         if(this.fireEvent('prepare', this, formData) != false){
32053             this.xhr.send(formData);
32054         };
32055     }
32056 });
32057
32058 /*
32059 * Licence: LGPL
32060 */
32061
32062 /**
32063  * @class Roo.bootstrap.DocumentViewer
32064  * @extends Roo.bootstrap.Component
32065  * Bootstrap DocumentViewer class
32066  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32067  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32068  * 
32069  * @constructor
32070  * Create a new DocumentViewer
32071  * @param {Object} config The config object
32072  */
32073
32074 Roo.bootstrap.DocumentViewer = function(config){
32075     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32076     
32077     this.addEvents({
32078         /**
32079          * @event initial
32080          * Fire after initEvent
32081          * @param {Roo.bootstrap.DocumentViewer} this
32082          */
32083         "initial" : true,
32084         /**
32085          * @event click
32086          * Fire after click
32087          * @param {Roo.bootstrap.DocumentViewer} this
32088          */
32089         "click" : true,
32090         /**
32091          * @event download
32092          * Fire after download button
32093          * @param {Roo.bootstrap.DocumentViewer} this
32094          */
32095         "download" : true,
32096         /**
32097          * @event trash
32098          * Fire after trash button
32099          * @param {Roo.bootstrap.DocumentViewer} this
32100          */
32101         "trash" : true
32102         
32103     });
32104 };
32105
32106 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32107     
32108     showDownload : true,
32109     
32110     showTrash : true,
32111     
32112     getAutoCreate : function()
32113     {
32114         var cfg = {
32115             tag : 'div',
32116             cls : 'roo-document-viewer',
32117             cn : [
32118                 {
32119                     tag : 'div',
32120                     cls : 'roo-document-viewer-body',
32121                     cn : [
32122                         {
32123                             tag : 'div',
32124                             cls : 'roo-document-viewer-thumb',
32125                             cn : [
32126                                 {
32127                                     tag : 'img',
32128                                     cls : 'roo-document-viewer-image'
32129                                 }
32130                             ]
32131                         }
32132                     ]
32133                 },
32134                 {
32135                     tag : 'div',
32136                     cls : 'roo-document-viewer-footer',
32137                     cn : {
32138                         tag : 'div',
32139                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32140                         cn : [
32141                             {
32142                                 tag : 'div',
32143                                 cls : 'btn-group roo-document-viewer-download',
32144                                 cn : [
32145                                     {
32146                                         tag : 'button',
32147                                         cls : 'btn btn-default',
32148                                         html : '<i class="fa fa-download"></i>'
32149                                     }
32150                                 ]
32151                             },
32152                             {
32153                                 tag : 'div',
32154                                 cls : 'btn-group roo-document-viewer-trash',
32155                                 cn : [
32156                                     {
32157                                         tag : 'button',
32158                                         cls : 'btn btn-default',
32159                                         html : '<i class="fa fa-trash"></i>'
32160                                     }
32161                                 ]
32162                             }
32163                         ]
32164                     }
32165                 }
32166             ]
32167         };
32168         
32169         return cfg;
32170     },
32171     
32172     initEvents : function()
32173     {
32174         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32175         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32176         
32177         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32178         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32179         
32180         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32181         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32182         
32183         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32184         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32185         
32186         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32187         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32188         
32189         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32190         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32191         
32192         this.bodyEl.on('click', this.onClick, this);
32193         this.downloadBtn.on('click', this.onDownload, this);
32194         this.trashBtn.on('click', this.onTrash, this);
32195         
32196         this.downloadBtn.hide();
32197         this.trashBtn.hide();
32198         
32199         if(this.showDownload){
32200             this.downloadBtn.show();
32201         }
32202         
32203         if(this.showTrash){
32204             this.trashBtn.show();
32205         }
32206         
32207         if(!this.showDownload && !this.showTrash) {
32208             this.footerEl.hide();
32209         }
32210         
32211     },
32212     
32213     initial : function()
32214     {
32215         this.fireEvent('initial', this);
32216         
32217     },
32218     
32219     onClick : function(e)
32220     {
32221         e.preventDefault();
32222         
32223         this.fireEvent('click', this);
32224     },
32225     
32226     onDownload : function(e)
32227     {
32228         e.preventDefault();
32229         
32230         this.fireEvent('download', this);
32231     },
32232     
32233     onTrash : function(e)
32234     {
32235         e.preventDefault();
32236         
32237         this.fireEvent('trash', this);
32238     }
32239     
32240 });
32241 /*
32242  * - LGPL
32243  *
32244  * nav progress bar
32245  * 
32246  */
32247
32248 /**
32249  * @class Roo.bootstrap.NavProgressBar
32250  * @extends Roo.bootstrap.Component
32251  * Bootstrap NavProgressBar class
32252  * 
32253  * @constructor
32254  * Create a new nav progress bar
32255  * @param {Object} config The config object
32256  */
32257
32258 Roo.bootstrap.NavProgressBar = function(config){
32259     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32260
32261     this.bullets = this.bullets || [];
32262    
32263 //    Roo.bootstrap.NavProgressBar.register(this);
32264      this.addEvents({
32265         /**
32266              * @event changed
32267              * Fires when the active item changes
32268              * @param {Roo.bootstrap.NavProgressBar} this
32269              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32270              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32271          */
32272         'changed': true
32273      });
32274     
32275 };
32276
32277 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32278     
32279     bullets : [],
32280     barItems : [],
32281     
32282     getAutoCreate : function()
32283     {
32284         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32285         
32286         cfg = {
32287             tag : 'div',
32288             cls : 'roo-navigation-bar-group',
32289             cn : [
32290                 {
32291                     tag : 'div',
32292                     cls : 'roo-navigation-top-bar'
32293                 },
32294                 {
32295                     tag : 'div',
32296                     cls : 'roo-navigation-bullets-bar',
32297                     cn : [
32298                         {
32299                             tag : 'ul',
32300                             cls : 'roo-navigation-bar'
32301                         }
32302                     ]
32303                 },
32304                 
32305                 {
32306                     tag : 'div',
32307                     cls : 'roo-navigation-bottom-bar'
32308                 }
32309             ]
32310             
32311         };
32312         
32313         return cfg;
32314         
32315     },
32316     
32317     initEvents: function() 
32318     {
32319         
32320     },
32321     
32322     onRender : function(ct, position) 
32323     {
32324         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32325         
32326         if(this.bullets.length){
32327             Roo.each(this.bullets, function(b){
32328                this.addItem(b);
32329             }, this);
32330         }
32331         
32332         this.format();
32333         
32334     },
32335     
32336     addItem : function(cfg)
32337     {
32338         var item = new Roo.bootstrap.NavProgressItem(cfg);
32339         
32340         item.parentId = this.id;
32341         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32342         
32343         if(cfg.html){
32344             var top = new Roo.bootstrap.Element({
32345                 tag : 'div',
32346                 cls : 'roo-navigation-bar-text'
32347             });
32348             
32349             var bottom = new Roo.bootstrap.Element({
32350                 tag : 'div',
32351                 cls : 'roo-navigation-bar-text'
32352             });
32353             
32354             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32355             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32356             
32357             var topText = new Roo.bootstrap.Element({
32358                 tag : 'span',
32359                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32360             });
32361             
32362             var bottomText = new Roo.bootstrap.Element({
32363                 tag : 'span',
32364                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32365             });
32366             
32367             topText.onRender(top.el, null);
32368             bottomText.onRender(bottom.el, null);
32369             
32370             item.topEl = top;
32371             item.bottomEl = bottom;
32372         }
32373         
32374         this.barItems.push(item);
32375         
32376         return item;
32377     },
32378     
32379     getActive : function()
32380     {
32381         var active = false;
32382         
32383         Roo.each(this.barItems, function(v){
32384             
32385             if (!v.isActive()) {
32386                 return;
32387             }
32388             
32389             active = v;
32390             return false;
32391             
32392         });
32393         
32394         return active;
32395     },
32396     
32397     setActiveItem : function(item)
32398     {
32399         var prev = false;
32400         
32401         Roo.each(this.barItems, function(v){
32402             if (v.rid == item.rid) {
32403                 return ;
32404             }
32405             
32406             if (v.isActive()) {
32407                 v.setActive(false);
32408                 prev = v;
32409             }
32410         });
32411
32412         item.setActive(true);
32413         
32414         this.fireEvent('changed', this, item, prev);
32415     },
32416     
32417     getBarItem: function(rid)
32418     {
32419         var ret = false;
32420         
32421         Roo.each(this.barItems, function(e) {
32422             if (e.rid != rid) {
32423                 return;
32424             }
32425             
32426             ret =  e;
32427             return false;
32428         });
32429         
32430         return ret;
32431     },
32432     
32433     indexOfItem : function(item)
32434     {
32435         var index = false;
32436         
32437         Roo.each(this.barItems, function(v, i){
32438             
32439             if (v.rid != item.rid) {
32440                 return;
32441             }
32442             
32443             index = i;
32444             return false
32445         });
32446         
32447         return index;
32448     },
32449     
32450     setActiveNext : function()
32451     {
32452         var i = this.indexOfItem(this.getActive());
32453         
32454         if (i > this.barItems.length) {
32455             return;
32456         }
32457         
32458         this.setActiveItem(this.barItems[i+1]);
32459     },
32460     
32461     setActivePrev : function()
32462     {
32463         var i = this.indexOfItem(this.getActive());
32464         
32465         if (i  < 1) {
32466             return;
32467         }
32468         
32469         this.setActiveItem(this.barItems[i-1]);
32470     },
32471     
32472     format : function()
32473     {
32474         if(!this.barItems.length){
32475             return;
32476         }
32477      
32478         var width = 100 / this.barItems.length;
32479         
32480         Roo.each(this.barItems, function(i){
32481             i.el.setStyle('width', width + '%');
32482             i.topEl.el.setStyle('width', width + '%');
32483             i.bottomEl.el.setStyle('width', width + '%');
32484         }, this);
32485         
32486     }
32487     
32488 });
32489 /*
32490  * - LGPL
32491  *
32492  * Nav Progress Item
32493  * 
32494  */
32495
32496 /**
32497  * @class Roo.bootstrap.NavProgressItem
32498  * @extends Roo.bootstrap.Component
32499  * Bootstrap NavProgressItem class
32500  * @cfg {String} rid the reference id
32501  * @cfg {Boolean} active (true|false) Is item active default false
32502  * @cfg {Boolean} disabled (true|false) Is item active default false
32503  * @cfg {String} html
32504  * @cfg {String} position (top|bottom) text position default bottom
32505  * @cfg {String} icon show icon instead of number
32506  * 
32507  * @constructor
32508  * Create a new NavProgressItem
32509  * @param {Object} config The config object
32510  */
32511 Roo.bootstrap.NavProgressItem = function(config){
32512     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32513     this.addEvents({
32514         // raw events
32515         /**
32516          * @event click
32517          * The raw click event for the entire grid.
32518          * @param {Roo.bootstrap.NavProgressItem} this
32519          * @param {Roo.EventObject} e
32520          */
32521         "click" : true
32522     });
32523    
32524 };
32525
32526 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32527     
32528     rid : '',
32529     active : false,
32530     disabled : false,
32531     html : '',
32532     position : 'bottom',
32533     icon : false,
32534     
32535     getAutoCreate : function()
32536     {
32537         var iconCls = 'roo-navigation-bar-item-icon';
32538         
32539         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32540         
32541         var cfg = {
32542             tag: 'li',
32543             cls: 'roo-navigation-bar-item',
32544             cn : [
32545                 {
32546                     tag : 'i',
32547                     cls : iconCls
32548                 }
32549             ]
32550         };
32551         
32552         if(this.active){
32553             cfg.cls += ' active';
32554         }
32555         if(this.disabled){
32556             cfg.cls += ' disabled';
32557         }
32558         
32559         return cfg;
32560     },
32561     
32562     disable : function()
32563     {
32564         this.setDisabled(true);
32565     },
32566     
32567     enable : function()
32568     {
32569         this.setDisabled(false);
32570     },
32571     
32572     initEvents: function() 
32573     {
32574         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32575         
32576         this.iconEl.on('click', this.onClick, this);
32577     },
32578     
32579     onClick : function(e)
32580     {
32581         e.preventDefault();
32582         
32583         if(this.disabled){
32584             return;
32585         }
32586         
32587         if(this.fireEvent('click', this, e) === false){
32588             return;
32589         };
32590         
32591         this.parent().setActiveItem(this);
32592     },
32593     
32594     isActive: function () 
32595     {
32596         return this.active;
32597     },
32598     
32599     setActive : function(state)
32600     {
32601         if(this.active == state){
32602             return;
32603         }
32604         
32605         this.active = state;
32606         
32607         if (state) {
32608             this.el.addClass('active');
32609             return;
32610         }
32611         
32612         this.el.removeClass('active');
32613         
32614         return;
32615     },
32616     
32617     setDisabled : function(state)
32618     {
32619         if(this.disabled == state){
32620             return;
32621         }
32622         
32623         this.disabled = state;
32624         
32625         if (state) {
32626             this.el.addClass('disabled');
32627             return;
32628         }
32629         
32630         this.el.removeClass('disabled');
32631     },
32632     
32633     tooltipEl : function()
32634     {
32635         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32636     }
32637 });
32638  
32639
32640  /*
32641  * - LGPL
32642  *
32643  * FieldLabel
32644  * 
32645  */
32646
32647 /**
32648  * @class Roo.bootstrap.FieldLabel
32649  * @extends Roo.bootstrap.Component
32650  * Bootstrap FieldLabel class
32651  * @cfg {String} html contents of the element
32652  * @cfg {String} tag tag of the element default label
32653  * @cfg {String} cls class of the element
32654  * @cfg {String} target label target 
32655  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32656  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32657  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32658  * @cfg {String} iconTooltip default "This field is required"
32659  * @cfg {String} indicatorpos (left|right) default left
32660  * 
32661  * @constructor
32662  * Create a new FieldLabel
32663  * @param {Object} config The config object
32664  */
32665
32666 Roo.bootstrap.FieldLabel = function(config){
32667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32668     
32669     this.addEvents({
32670             /**
32671              * @event invalid
32672              * Fires after the field has been marked as invalid.
32673              * @param {Roo.form.FieldLabel} this
32674              * @param {String} msg The validation message
32675              */
32676             invalid : true,
32677             /**
32678              * @event valid
32679              * Fires after the field has been validated with no errors.
32680              * @param {Roo.form.FieldLabel} this
32681              */
32682             valid : true
32683         });
32684 };
32685
32686 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32687     
32688     tag: 'label',
32689     cls: '',
32690     html: '',
32691     target: '',
32692     allowBlank : true,
32693     invalidClass : 'has-warning',
32694     validClass : 'has-success',
32695     iconTooltip : 'This field is required',
32696     indicatorpos : 'left',
32697     
32698     getAutoCreate : function(){
32699         
32700         var cls = "";
32701         if (!this.allowBlank) {
32702             cls  = "visible";
32703         }
32704         
32705         var cfg = {
32706             tag : this.tag,
32707             cls : 'roo-bootstrap-field-label ' + this.cls,
32708             for : this.target,
32709             cn : [
32710                 {
32711                     tag : 'i',
32712                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32713                     tooltip : this.iconTooltip
32714                 },
32715                 {
32716                     tag : 'span',
32717                     html : this.html
32718                 }
32719             ] 
32720         };
32721         
32722         if(this.indicatorpos == 'right'){
32723             var cfg = {
32724                 tag : this.tag,
32725                 cls : 'roo-bootstrap-field-label ' + this.cls,
32726                 for : this.target,
32727                 cn : [
32728                     {
32729                         tag : 'span',
32730                         html : this.html
32731                     },
32732                     {
32733                         tag : 'i',
32734                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32735                         tooltip : this.iconTooltip
32736                     }
32737                 ] 
32738             };
32739         }
32740         
32741         return cfg;
32742     },
32743     
32744     initEvents: function() 
32745     {
32746         Roo.bootstrap.Element.superclass.initEvents.call(this);
32747         
32748         this.indicator = this.indicatorEl();
32749         
32750         if(this.indicator){
32751             this.indicator.removeClass('visible');
32752             this.indicator.addClass('invisible');
32753         }
32754         
32755         Roo.bootstrap.FieldLabel.register(this);
32756     },
32757     
32758     indicatorEl : function()
32759     {
32760         var indicator = this.el.select('i.roo-required-indicator',true).first();
32761         
32762         if(!indicator){
32763             return false;
32764         }
32765         
32766         return indicator;
32767         
32768     },
32769     
32770     /**
32771      * Mark this field as valid
32772      */
32773     markValid : function()
32774     {
32775         if(this.indicator){
32776             this.indicator.removeClass('visible');
32777             this.indicator.addClass('invisible');
32778         }
32779         if (Roo.bootstrap.version == 3) {
32780             this.el.removeClass(this.invalidClass);
32781             this.el.addClass(this.validClass);
32782         } else {
32783             this.el.removeClass('is-invalid');
32784             this.el.addClass('is-valid');
32785         }
32786         
32787         
32788         this.fireEvent('valid', this);
32789     },
32790     
32791     /**
32792      * Mark this field as invalid
32793      * @param {String} msg The validation message
32794      */
32795     markInvalid : function(msg)
32796     {
32797         if(this.indicator){
32798             this.indicator.removeClass('invisible');
32799             this.indicator.addClass('visible');
32800         }
32801           if (Roo.bootstrap.version == 3) {
32802             this.el.removeClass(this.validClass);
32803             this.el.addClass(this.invalidClass);
32804         } else {
32805             this.el.removeClass('is-valid');
32806             this.el.addClass('is-invalid');
32807         }
32808         
32809         
32810         this.fireEvent('invalid', this, msg);
32811     }
32812     
32813    
32814 });
32815
32816 Roo.apply(Roo.bootstrap.FieldLabel, {
32817     
32818     groups: {},
32819     
32820      /**
32821     * register a FieldLabel Group
32822     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32823     */
32824     register : function(label)
32825     {
32826         if(this.groups.hasOwnProperty(label.target)){
32827             return;
32828         }
32829      
32830         this.groups[label.target] = label;
32831         
32832     },
32833     /**
32834     * fetch a FieldLabel Group based on the target
32835     * @param {string} target
32836     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32837     */
32838     get: function(target) {
32839         if (typeof(this.groups[target]) == 'undefined') {
32840             return false;
32841         }
32842         
32843         return this.groups[target] ;
32844     }
32845 });
32846
32847  
32848
32849  /*
32850  * - LGPL
32851  *
32852  * page DateSplitField.
32853  * 
32854  */
32855
32856
32857 /**
32858  * @class Roo.bootstrap.DateSplitField
32859  * @extends Roo.bootstrap.Component
32860  * Bootstrap DateSplitField class
32861  * @cfg {string} fieldLabel - the label associated
32862  * @cfg {Number} labelWidth set the width of label (0-12)
32863  * @cfg {String} labelAlign (top|left)
32864  * @cfg {Boolean} dayAllowBlank (true|false) default false
32865  * @cfg {Boolean} monthAllowBlank (true|false) default false
32866  * @cfg {Boolean} yearAllowBlank (true|false) default false
32867  * @cfg {string} dayPlaceholder 
32868  * @cfg {string} monthPlaceholder
32869  * @cfg {string} yearPlaceholder
32870  * @cfg {string} dayFormat default 'd'
32871  * @cfg {string} monthFormat default 'm'
32872  * @cfg {string} yearFormat default 'Y'
32873  * @cfg {Number} labellg set the width of label (1-12)
32874  * @cfg {Number} labelmd set the width of label (1-12)
32875  * @cfg {Number} labelsm set the width of label (1-12)
32876  * @cfg {Number} labelxs set the width of label (1-12)
32877
32878  *     
32879  * @constructor
32880  * Create a new DateSplitField
32881  * @param {Object} config The config object
32882  */
32883
32884 Roo.bootstrap.DateSplitField = function(config){
32885     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32886     
32887     this.addEvents({
32888         // raw events
32889          /**
32890          * @event years
32891          * getting the data of years
32892          * @param {Roo.bootstrap.DateSplitField} this
32893          * @param {Object} years
32894          */
32895         "years" : true,
32896         /**
32897          * @event days
32898          * getting the data of days
32899          * @param {Roo.bootstrap.DateSplitField} this
32900          * @param {Object} days
32901          */
32902         "days" : true,
32903         /**
32904          * @event invalid
32905          * Fires after the field has been marked as invalid.
32906          * @param {Roo.form.Field} this
32907          * @param {String} msg The validation message
32908          */
32909         invalid : true,
32910        /**
32911          * @event valid
32912          * Fires after the field has been validated with no errors.
32913          * @param {Roo.form.Field} this
32914          */
32915         valid : true
32916     });
32917 };
32918
32919 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32920     
32921     fieldLabel : '',
32922     labelAlign : 'top',
32923     labelWidth : 3,
32924     dayAllowBlank : false,
32925     monthAllowBlank : false,
32926     yearAllowBlank : false,
32927     dayPlaceholder : '',
32928     monthPlaceholder : '',
32929     yearPlaceholder : '',
32930     dayFormat : 'd',
32931     monthFormat : 'm',
32932     yearFormat : 'Y',
32933     isFormField : true,
32934     labellg : 0,
32935     labelmd : 0,
32936     labelsm : 0,
32937     labelxs : 0,
32938     
32939     getAutoCreate : function()
32940     {
32941         var cfg = {
32942             tag : 'div',
32943             cls : 'row roo-date-split-field-group',
32944             cn : [
32945                 {
32946                     tag : 'input',
32947                     type : 'hidden',
32948                     cls : 'form-hidden-field roo-date-split-field-group-value',
32949                     name : this.name
32950                 }
32951             ]
32952         };
32953         
32954         var labelCls = 'col-md-12';
32955         var contentCls = 'col-md-4';
32956         
32957         if(this.fieldLabel){
32958             
32959             var label = {
32960                 tag : 'div',
32961                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32962                 cn : [
32963                     {
32964                         tag : 'label',
32965                         html : this.fieldLabel
32966                     }
32967                 ]
32968             };
32969             
32970             if(this.labelAlign == 'left'){
32971             
32972                 if(this.labelWidth > 12){
32973                     label.style = "width: " + this.labelWidth + 'px';
32974                 }
32975
32976                 if(this.labelWidth < 13 && this.labelmd == 0){
32977                     this.labelmd = this.labelWidth;
32978                 }
32979
32980                 if(this.labellg > 0){
32981                     labelCls = ' col-lg-' + this.labellg;
32982                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32983                 }
32984
32985                 if(this.labelmd > 0){
32986                     labelCls = ' col-md-' + this.labelmd;
32987                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32988                 }
32989
32990                 if(this.labelsm > 0){
32991                     labelCls = ' col-sm-' + this.labelsm;
32992                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32993                 }
32994
32995                 if(this.labelxs > 0){
32996                     labelCls = ' col-xs-' + this.labelxs;
32997                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32998                 }
32999             }
33000             
33001             label.cls += ' ' + labelCls;
33002             
33003             cfg.cn.push(label);
33004         }
33005         
33006         Roo.each(['day', 'month', 'year'], function(t){
33007             cfg.cn.push({
33008                 tag : 'div',
33009                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33010             });
33011         }, this);
33012         
33013         return cfg;
33014     },
33015     
33016     inputEl: function ()
33017     {
33018         return this.el.select('.roo-date-split-field-group-value', true).first();
33019     },
33020     
33021     onRender : function(ct, position) 
33022     {
33023         var _this = this;
33024         
33025         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33026         
33027         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33028         
33029         this.dayField = new Roo.bootstrap.ComboBox({
33030             allowBlank : this.dayAllowBlank,
33031             alwaysQuery : true,
33032             displayField : 'value',
33033             editable : false,
33034             fieldLabel : '',
33035             forceSelection : true,
33036             mode : 'local',
33037             placeholder : this.dayPlaceholder,
33038             selectOnFocus : true,
33039             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33040             triggerAction : 'all',
33041             typeAhead : true,
33042             valueField : 'value',
33043             store : new Roo.data.SimpleStore({
33044                 data : (function() {    
33045                     var days = [];
33046                     _this.fireEvent('days', _this, days);
33047                     return days;
33048                 })(),
33049                 fields : [ 'value' ]
33050             }),
33051             listeners : {
33052                 select : function (_self, record, index)
33053                 {
33054                     _this.setValue(_this.getValue());
33055                 }
33056             }
33057         });
33058
33059         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33060         
33061         this.monthField = new Roo.bootstrap.MonthField({
33062             after : '<i class=\"fa fa-calendar\"></i>',
33063             allowBlank : this.monthAllowBlank,
33064             placeholder : this.monthPlaceholder,
33065             readOnly : true,
33066             listeners : {
33067                 render : function (_self)
33068                 {
33069                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33070                         e.preventDefault();
33071                         _self.focus();
33072                     });
33073                 },
33074                 select : function (_self, oldvalue, newvalue)
33075                 {
33076                     _this.setValue(_this.getValue());
33077                 }
33078             }
33079         });
33080         
33081         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33082         
33083         this.yearField = new Roo.bootstrap.ComboBox({
33084             allowBlank : this.yearAllowBlank,
33085             alwaysQuery : true,
33086             displayField : 'value',
33087             editable : false,
33088             fieldLabel : '',
33089             forceSelection : true,
33090             mode : 'local',
33091             placeholder : this.yearPlaceholder,
33092             selectOnFocus : true,
33093             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33094             triggerAction : 'all',
33095             typeAhead : true,
33096             valueField : 'value',
33097             store : new Roo.data.SimpleStore({
33098                 data : (function() {
33099                     var years = [];
33100                     _this.fireEvent('years', _this, years);
33101                     return years;
33102                 })(),
33103                 fields : [ 'value' ]
33104             }),
33105             listeners : {
33106                 select : function (_self, record, index)
33107                 {
33108                     _this.setValue(_this.getValue());
33109                 }
33110             }
33111         });
33112
33113         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33114     },
33115     
33116     setValue : function(v, format)
33117     {
33118         this.inputEl.dom.value = v;
33119         
33120         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33121         
33122         var d = Date.parseDate(v, f);
33123         
33124         if(!d){
33125             this.validate();
33126             return;
33127         }
33128         
33129         this.setDay(d.format(this.dayFormat));
33130         this.setMonth(d.format(this.monthFormat));
33131         this.setYear(d.format(this.yearFormat));
33132         
33133         this.validate();
33134         
33135         return;
33136     },
33137     
33138     setDay : function(v)
33139     {
33140         this.dayField.setValue(v);
33141         this.inputEl.dom.value = this.getValue();
33142         this.validate();
33143         return;
33144     },
33145     
33146     setMonth : function(v)
33147     {
33148         this.monthField.setValue(v, true);
33149         this.inputEl.dom.value = this.getValue();
33150         this.validate();
33151         return;
33152     },
33153     
33154     setYear : function(v)
33155     {
33156         this.yearField.setValue(v);
33157         this.inputEl.dom.value = this.getValue();
33158         this.validate();
33159         return;
33160     },
33161     
33162     getDay : function()
33163     {
33164         return this.dayField.getValue();
33165     },
33166     
33167     getMonth : function()
33168     {
33169         return this.monthField.getValue();
33170     },
33171     
33172     getYear : function()
33173     {
33174         return this.yearField.getValue();
33175     },
33176     
33177     getValue : function()
33178     {
33179         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33180         
33181         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33182         
33183         return date;
33184     },
33185     
33186     reset : function()
33187     {
33188         this.setDay('');
33189         this.setMonth('');
33190         this.setYear('');
33191         this.inputEl.dom.value = '';
33192         this.validate();
33193         return;
33194     },
33195     
33196     validate : function()
33197     {
33198         var d = this.dayField.validate();
33199         var m = this.monthField.validate();
33200         var y = this.yearField.validate();
33201         
33202         var valid = true;
33203         
33204         if(
33205                 (!this.dayAllowBlank && !d) ||
33206                 (!this.monthAllowBlank && !m) ||
33207                 (!this.yearAllowBlank && !y)
33208         ){
33209             valid = false;
33210         }
33211         
33212         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33213             return valid;
33214         }
33215         
33216         if(valid){
33217             this.markValid();
33218             return valid;
33219         }
33220         
33221         this.markInvalid();
33222         
33223         return valid;
33224     },
33225     
33226     markValid : function()
33227     {
33228         
33229         var label = this.el.select('label', true).first();
33230         var icon = this.el.select('i.fa-star', true).first();
33231
33232         if(label && icon){
33233             icon.remove();
33234         }
33235         
33236         this.fireEvent('valid', this);
33237     },
33238     
33239      /**
33240      * Mark this field as invalid
33241      * @param {String} msg The validation message
33242      */
33243     markInvalid : function(msg)
33244     {
33245         
33246         var label = this.el.select('label', true).first();
33247         var icon = this.el.select('i.fa-star', true).first();
33248
33249         if(label && !icon){
33250             this.el.select('.roo-date-split-field-label', true).createChild({
33251                 tag : 'i',
33252                 cls : 'text-danger fa fa-lg fa-star',
33253                 tooltip : 'This field is required',
33254                 style : 'margin-right:5px;'
33255             }, label, true);
33256         }
33257         
33258         this.fireEvent('invalid', this, msg);
33259     },
33260     
33261     clearInvalid : function()
33262     {
33263         var label = this.el.select('label', true).first();
33264         var icon = this.el.select('i.fa-star', true).first();
33265
33266         if(label && icon){
33267             icon.remove();
33268         }
33269         
33270         this.fireEvent('valid', this);
33271     },
33272     
33273     getName: function()
33274     {
33275         return this.name;
33276     }
33277     
33278 });
33279
33280  /**
33281  *
33282  * This is based on 
33283  * http://masonry.desandro.com
33284  *
33285  * The idea is to render all the bricks based on vertical width...
33286  *
33287  * The original code extends 'outlayer' - we might need to use that....
33288  * 
33289  */
33290
33291
33292 /**
33293  * @class Roo.bootstrap.LayoutMasonry
33294  * @extends Roo.bootstrap.Component
33295  * Bootstrap Layout Masonry class
33296  * 
33297  * @constructor
33298  * Create a new Element
33299  * @param {Object} config The config object
33300  */
33301
33302 Roo.bootstrap.LayoutMasonry = function(config){
33303     
33304     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33305     
33306     this.bricks = [];
33307     
33308     Roo.bootstrap.LayoutMasonry.register(this);
33309     
33310     this.addEvents({
33311         // raw events
33312         /**
33313          * @event layout
33314          * Fire after layout the items
33315          * @param {Roo.bootstrap.LayoutMasonry} this
33316          * @param {Roo.EventObject} e
33317          */
33318         "layout" : true
33319     });
33320     
33321 };
33322
33323 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33324     
33325     /**
33326      * @cfg {Boolean} isLayoutInstant = no animation?
33327      */   
33328     isLayoutInstant : false, // needed?
33329    
33330     /**
33331      * @cfg {Number} boxWidth  width of the columns
33332      */   
33333     boxWidth : 450,
33334     
33335       /**
33336      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33337      */   
33338     boxHeight : 0,
33339     
33340     /**
33341      * @cfg {Number} padWidth padding below box..
33342      */   
33343     padWidth : 10, 
33344     
33345     /**
33346      * @cfg {Number} gutter gutter width..
33347      */   
33348     gutter : 10,
33349     
33350      /**
33351      * @cfg {Number} maxCols maximum number of columns
33352      */   
33353     
33354     maxCols: 0,
33355     
33356     /**
33357      * @cfg {Boolean} isAutoInitial defalut true
33358      */   
33359     isAutoInitial : true, 
33360     
33361     containerWidth: 0,
33362     
33363     /**
33364      * @cfg {Boolean} isHorizontal defalut false
33365      */   
33366     isHorizontal : false, 
33367
33368     currentSize : null,
33369     
33370     tag: 'div',
33371     
33372     cls: '',
33373     
33374     bricks: null, //CompositeElement
33375     
33376     cols : 1,
33377     
33378     _isLayoutInited : false,
33379     
33380 //    isAlternative : false, // only use for vertical layout...
33381     
33382     /**
33383      * @cfg {Number} alternativePadWidth padding below box..
33384      */   
33385     alternativePadWidth : 50,
33386     
33387     selectedBrick : [],
33388     
33389     getAutoCreate : function(){
33390         
33391         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33392         
33393         var cfg = {
33394             tag: this.tag,
33395             cls: 'blog-masonary-wrapper ' + this.cls,
33396             cn : {
33397                 cls : 'mas-boxes masonary'
33398             }
33399         };
33400         
33401         return cfg;
33402     },
33403     
33404     getChildContainer: function( )
33405     {
33406         if (this.boxesEl) {
33407             return this.boxesEl;
33408         }
33409         
33410         this.boxesEl = this.el.select('.mas-boxes').first();
33411         
33412         return this.boxesEl;
33413     },
33414     
33415     
33416     initEvents : function()
33417     {
33418         var _this = this;
33419         
33420         if(this.isAutoInitial){
33421             Roo.log('hook children rendered');
33422             this.on('childrenrendered', function() {
33423                 Roo.log('children rendered');
33424                 _this.initial();
33425             } ,this);
33426         }
33427     },
33428     
33429     initial : function()
33430     {
33431         this.selectedBrick = [];
33432         
33433         this.currentSize = this.el.getBox(true);
33434         
33435         Roo.EventManager.onWindowResize(this.resize, this); 
33436
33437         if(!this.isAutoInitial){
33438             this.layout();
33439             return;
33440         }
33441         
33442         this.layout();
33443         
33444         return;
33445         //this.layout.defer(500,this);
33446         
33447     },
33448     
33449     resize : function()
33450     {
33451         var cs = this.el.getBox(true);
33452         
33453         if (
33454                 this.currentSize.width == cs.width && 
33455                 this.currentSize.x == cs.x && 
33456                 this.currentSize.height == cs.height && 
33457                 this.currentSize.y == cs.y 
33458         ) {
33459             Roo.log("no change in with or X or Y");
33460             return;
33461         }
33462         
33463         this.currentSize = cs;
33464         
33465         this.layout();
33466         
33467     },
33468     
33469     layout : function()
33470     {   
33471         this._resetLayout();
33472         
33473         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33474         
33475         this.layoutItems( isInstant );
33476       
33477         this._isLayoutInited = true;
33478         
33479         this.fireEvent('layout', this);
33480         
33481     },
33482     
33483     _resetLayout : function()
33484     {
33485         if(this.isHorizontal){
33486             this.horizontalMeasureColumns();
33487             return;
33488         }
33489         
33490         this.verticalMeasureColumns();
33491         
33492     },
33493     
33494     verticalMeasureColumns : function()
33495     {
33496         this.getContainerWidth();
33497         
33498 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33499 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33500 //            return;
33501 //        }
33502         
33503         var boxWidth = this.boxWidth + this.padWidth;
33504         
33505         if(this.containerWidth < this.boxWidth){
33506             boxWidth = this.containerWidth
33507         }
33508         
33509         var containerWidth = this.containerWidth;
33510         
33511         var cols = Math.floor(containerWidth / boxWidth);
33512         
33513         this.cols = Math.max( cols, 1 );
33514         
33515         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33516         
33517         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33518         
33519         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33520         
33521         this.colWidth = boxWidth + avail - this.padWidth;
33522         
33523         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33524         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33525     },
33526     
33527     horizontalMeasureColumns : function()
33528     {
33529         this.getContainerWidth();
33530         
33531         var boxWidth = this.boxWidth;
33532         
33533         if(this.containerWidth < boxWidth){
33534             boxWidth = this.containerWidth;
33535         }
33536         
33537         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33538         
33539         this.el.setHeight(boxWidth);
33540         
33541     },
33542     
33543     getContainerWidth : function()
33544     {
33545         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33546     },
33547     
33548     layoutItems : function( isInstant )
33549     {
33550         Roo.log(this.bricks);
33551         
33552         var items = Roo.apply([], this.bricks);
33553         
33554         if(this.isHorizontal){
33555             this._horizontalLayoutItems( items , isInstant );
33556             return;
33557         }
33558         
33559 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33560 //            this._verticalAlternativeLayoutItems( items , isInstant );
33561 //            return;
33562 //        }
33563         
33564         this._verticalLayoutItems( items , isInstant );
33565         
33566     },
33567     
33568     _verticalLayoutItems : function ( items , isInstant)
33569     {
33570         if ( !items || !items.length ) {
33571             return;
33572         }
33573         
33574         var standard = [
33575             ['xs', 'xs', 'xs', 'tall'],
33576             ['xs', 'xs', 'tall'],
33577             ['xs', 'xs', 'sm'],
33578             ['xs', 'xs', 'xs'],
33579             ['xs', 'tall'],
33580             ['xs', 'sm'],
33581             ['xs', 'xs'],
33582             ['xs'],
33583             
33584             ['sm', 'xs', 'xs'],
33585             ['sm', 'xs'],
33586             ['sm'],
33587             
33588             ['tall', 'xs', 'xs', 'xs'],
33589             ['tall', 'xs', 'xs'],
33590             ['tall', 'xs'],
33591             ['tall']
33592             
33593         ];
33594         
33595         var queue = [];
33596         
33597         var boxes = [];
33598         
33599         var box = [];
33600         
33601         Roo.each(items, function(item, k){
33602             
33603             switch (item.size) {
33604                 // these layouts take up a full box,
33605                 case 'md' :
33606                 case 'md-left' :
33607                 case 'md-right' :
33608                 case 'wide' :
33609                     
33610                     if(box.length){
33611                         boxes.push(box);
33612                         box = [];
33613                     }
33614                     
33615                     boxes.push([item]);
33616                     
33617                     break;
33618                     
33619                 case 'xs' :
33620                 case 'sm' :
33621                 case 'tall' :
33622                     
33623                     box.push(item);
33624                     
33625                     break;
33626                 default :
33627                     break;
33628                     
33629             }
33630             
33631         }, this);
33632         
33633         if(box.length){
33634             boxes.push(box);
33635             box = [];
33636         }
33637         
33638         var filterPattern = function(box, length)
33639         {
33640             if(!box.length){
33641                 return;
33642             }
33643             
33644             var match = false;
33645             
33646             var pattern = box.slice(0, length);
33647             
33648             var format = [];
33649             
33650             Roo.each(pattern, function(i){
33651                 format.push(i.size);
33652             }, this);
33653             
33654             Roo.each(standard, function(s){
33655                 
33656                 if(String(s) != String(format)){
33657                     return;
33658                 }
33659                 
33660                 match = true;
33661                 return false;
33662                 
33663             }, this);
33664             
33665             if(!match && length == 1){
33666                 return;
33667             }
33668             
33669             if(!match){
33670                 filterPattern(box, length - 1);
33671                 return;
33672             }
33673                 
33674             queue.push(pattern);
33675
33676             box = box.slice(length, box.length);
33677
33678             filterPattern(box, 4);
33679
33680             return;
33681             
33682         }
33683         
33684         Roo.each(boxes, function(box, k){
33685             
33686             if(!box.length){
33687                 return;
33688             }
33689             
33690             if(box.length == 1){
33691                 queue.push(box);
33692                 return;
33693             }
33694             
33695             filterPattern(box, 4);
33696             
33697         }, this);
33698         
33699         this._processVerticalLayoutQueue( queue, isInstant );
33700         
33701     },
33702     
33703 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33704 //    {
33705 //        if ( !items || !items.length ) {
33706 //            return;
33707 //        }
33708 //
33709 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33710 //        
33711 //    },
33712     
33713     _horizontalLayoutItems : function ( items , isInstant)
33714     {
33715         if ( !items || !items.length || items.length < 3) {
33716             return;
33717         }
33718         
33719         items.reverse();
33720         
33721         var eItems = items.slice(0, 3);
33722         
33723         items = items.slice(3, items.length);
33724         
33725         var standard = [
33726             ['xs', 'xs', 'xs', 'wide'],
33727             ['xs', 'xs', 'wide'],
33728             ['xs', 'xs', 'sm'],
33729             ['xs', 'xs', 'xs'],
33730             ['xs', 'wide'],
33731             ['xs', 'sm'],
33732             ['xs', 'xs'],
33733             ['xs'],
33734             
33735             ['sm', 'xs', 'xs'],
33736             ['sm', 'xs'],
33737             ['sm'],
33738             
33739             ['wide', 'xs', 'xs', 'xs'],
33740             ['wide', 'xs', 'xs'],
33741             ['wide', 'xs'],
33742             ['wide'],
33743             
33744             ['wide-thin']
33745         ];
33746         
33747         var queue = [];
33748         
33749         var boxes = [];
33750         
33751         var box = [];
33752         
33753         Roo.each(items, function(item, k){
33754             
33755             switch (item.size) {
33756                 case 'md' :
33757                 case 'md-left' :
33758                 case 'md-right' :
33759                 case 'tall' :
33760                     
33761                     if(box.length){
33762                         boxes.push(box);
33763                         box = [];
33764                     }
33765                     
33766                     boxes.push([item]);
33767                     
33768                     break;
33769                     
33770                 case 'xs' :
33771                 case 'sm' :
33772                 case 'wide' :
33773                 case 'wide-thin' :
33774                     
33775                     box.push(item);
33776                     
33777                     break;
33778                 default :
33779                     break;
33780                     
33781             }
33782             
33783         }, this);
33784         
33785         if(box.length){
33786             boxes.push(box);
33787             box = [];
33788         }
33789         
33790         var filterPattern = function(box, length)
33791         {
33792             if(!box.length){
33793                 return;
33794             }
33795             
33796             var match = false;
33797             
33798             var pattern = box.slice(0, length);
33799             
33800             var format = [];
33801             
33802             Roo.each(pattern, function(i){
33803                 format.push(i.size);
33804             }, this);
33805             
33806             Roo.each(standard, function(s){
33807                 
33808                 if(String(s) != String(format)){
33809                     return;
33810                 }
33811                 
33812                 match = true;
33813                 return false;
33814                 
33815             }, this);
33816             
33817             if(!match && length == 1){
33818                 return;
33819             }
33820             
33821             if(!match){
33822                 filterPattern(box, length - 1);
33823                 return;
33824             }
33825                 
33826             queue.push(pattern);
33827
33828             box = box.slice(length, box.length);
33829
33830             filterPattern(box, 4);
33831
33832             return;
33833             
33834         }
33835         
33836         Roo.each(boxes, function(box, k){
33837             
33838             if(!box.length){
33839                 return;
33840             }
33841             
33842             if(box.length == 1){
33843                 queue.push(box);
33844                 return;
33845             }
33846             
33847             filterPattern(box, 4);
33848             
33849         }, this);
33850         
33851         
33852         var prune = [];
33853         
33854         var pos = this.el.getBox(true);
33855         
33856         var minX = pos.x;
33857         
33858         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33859         
33860         var hit_end = false;
33861         
33862         Roo.each(queue, function(box){
33863             
33864             if(hit_end){
33865                 
33866                 Roo.each(box, function(b){
33867                 
33868                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33869                     b.el.hide();
33870
33871                 }, this);
33872
33873                 return;
33874             }
33875             
33876             var mx = 0;
33877             
33878             Roo.each(box, function(b){
33879                 
33880                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33881                 b.el.show();
33882
33883                 mx = Math.max(mx, b.x);
33884                 
33885             }, this);
33886             
33887             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33888             
33889             if(maxX < minX){
33890                 
33891                 Roo.each(box, function(b){
33892                 
33893                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33894                     b.el.hide();
33895                     
33896                 }, this);
33897                 
33898                 hit_end = true;
33899                 
33900                 return;
33901             }
33902             
33903             prune.push(box);
33904             
33905         }, this);
33906         
33907         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33908     },
33909     
33910     /** Sets position of item in DOM
33911     * @param {Element} item
33912     * @param {Number} x - horizontal position
33913     * @param {Number} y - vertical position
33914     * @param {Boolean} isInstant - disables transitions
33915     */
33916     _processVerticalLayoutQueue : function( queue, isInstant )
33917     {
33918         var pos = this.el.getBox(true);
33919         var x = pos.x;
33920         var y = pos.y;
33921         var maxY = [];
33922         
33923         for (var i = 0; i < this.cols; i++){
33924             maxY[i] = pos.y;
33925         }
33926         
33927         Roo.each(queue, function(box, k){
33928             
33929             var col = k % this.cols;
33930             
33931             Roo.each(box, function(b,kk){
33932                 
33933                 b.el.position('absolute');
33934                 
33935                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33936                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33937                 
33938                 if(b.size == 'md-left' || b.size == 'md-right'){
33939                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33940                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33941                 }
33942                 
33943                 b.el.setWidth(width);
33944                 b.el.setHeight(height);
33945                 // iframe?
33946                 b.el.select('iframe',true).setSize(width,height);
33947                 
33948             }, this);
33949             
33950             for (var i = 0; i < this.cols; i++){
33951                 
33952                 if(maxY[i] < maxY[col]){
33953                     col = i;
33954                     continue;
33955                 }
33956                 
33957                 col = Math.min(col, i);
33958                 
33959             }
33960             
33961             x = pos.x + col * (this.colWidth + this.padWidth);
33962             
33963             y = maxY[col];
33964             
33965             var positions = [];
33966             
33967             switch (box.length){
33968                 case 1 :
33969                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33970                     break;
33971                 case 2 :
33972                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33973                     break;
33974                 case 3 :
33975                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33976                     break;
33977                 case 4 :
33978                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33979                     break;
33980                 default :
33981                     break;
33982             }
33983             
33984             Roo.each(box, function(b,kk){
33985                 
33986                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33987                 
33988                 var sz = b.el.getSize();
33989                 
33990                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33991                 
33992             }, this);
33993             
33994         }, this);
33995         
33996         var mY = 0;
33997         
33998         for (var i = 0; i < this.cols; i++){
33999             mY = Math.max(mY, maxY[i]);
34000         }
34001         
34002         this.el.setHeight(mY - pos.y);
34003         
34004     },
34005     
34006 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34007 //    {
34008 //        var pos = this.el.getBox(true);
34009 //        var x = pos.x;
34010 //        var y = pos.y;
34011 //        var maxX = pos.right;
34012 //        
34013 //        var maxHeight = 0;
34014 //        
34015 //        Roo.each(items, function(item, k){
34016 //            
34017 //            var c = k % 2;
34018 //            
34019 //            item.el.position('absolute');
34020 //                
34021 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34022 //
34023 //            item.el.setWidth(width);
34024 //
34025 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34026 //
34027 //            item.el.setHeight(height);
34028 //            
34029 //            if(c == 0){
34030 //                item.el.setXY([x, y], isInstant ? false : true);
34031 //            } else {
34032 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34033 //            }
34034 //            
34035 //            y = y + height + this.alternativePadWidth;
34036 //            
34037 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34038 //            
34039 //        }, this);
34040 //        
34041 //        this.el.setHeight(maxHeight);
34042 //        
34043 //    },
34044     
34045     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34046     {
34047         var pos = this.el.getBox(true);
34048         
34049         var minX = pos.x;
34050         var minY = pos.y;
34051         
34052         var maxX = pos.right;
34053         
34054         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34055         
34056         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34057         
34058         Roo.each(queue, function(box, k){
34059             
34060             Roo.each(box, function(b, kk){
34061                 
34062                 b.el.position('absolute');
34063                 
34064                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34065                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34066                 
34067                 if(b.size == 'md-left' || b.size == 'md-right'){
34068                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34069                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34070                 }
34071                 
34072                 b.el.setWidth(width);
34073                 b.el.setHeight(height);
34074                 
34075             }, this);
34076             
34077             if(!box.length){
34078                 return;
34079             }
34080             
34081             var positions = [];
34082             
34083             switch (box.length){
34084                 case 1 :
34085                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34086                     break;
34087                 case 2 :
34088                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34089                     break;
34090                 case 3 :
34091                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34092                     break;
34093                 case 4 :
34094                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34095                     break;
34096                 default :
34097                     break;
34098             }
34099             
34100             Roo.each(box, function(b,kk){
34101                 
34102                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34103                 
34104                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34105                 
34106             }, this);
34107             
34108         }, this);
34109         
34110     },
34111     
34112     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34113     {
34114         Roo.each(eItems, function(b,k){
34115             
34116             b.size = (k == 0) ? 'sm' : 'xs';
34117             b.x = (k == 0) ? 2 : 1;
34118             b.y = (k == 0) ? 2 : 1;
34119             
34120             b.el.position('absolute');
34121             
34122             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34123                 
34124             b.el.setWidth(width);
34125             
34126             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34127             
34128             b.el.setHeight(height);
34129             
34130         }, this);
34131
34132         var positions = [];
34133         
34134         positions.push({
34135             x : maxX - this.unitWidth * 2 - this.gutter,
34136             y : minY
34137         });
34138         
34139         positions.push({
34140             x : maxX - this.unitWidth,
34141             y : minY + (this.unitWidth + this.gutter) * 2
34142         });
34143         
34144         positions.push({
34145             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34146             y : minY
34147         });
34148         
34149         Roo.each(eItems, function(b,k){
34150             
34151             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34152
34153         }, this);
34154         
34155     },
34156     
34157     getVerticalOneBoxColPositions : function(x, y, box)
34158     {
34159         var pos = [];
34160         
34161         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34162         
34163         if(box[0].size == 'md-left'){
34164             rand = 0;
34165         }
34166         
34167         if(box[0].size == 'md-right'){
34168             rand = 1;
34169         }
34170         
34171         pos.push({
34172             x : x + (this.unitWidth + this.gutter) * rand,
34173             y : y
34174         });
34175         
34176         return pos;
34177     },
34178     
34179     getVerticalTwoBoxColPositions : function(x, y, box)
34180     {
34181         var pos = [];
34182         
34183         if(box[0].size == 'xs'){
34184             
34185             pos.push({
34186                 x : x,
34187                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34188             });
34189
34190             pos.push({
34191                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34192                 y : y
34193             });
34194             
34195             return pos;
34196             
34197         }
34198         
34199         pos.push({
34200             x : x,
34201             y : y
34202         });
34203
34204         pos.push({
34205             x : x + (this.unitWidth + this.gutter) * 2,
34206             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34207         });
34208         
34209         return pos;
34210         
34211     },
34212     
34213     getVerticalThreeBoxColPositions : function(x, y, box)
34214     {
34215         var pos = [];
34216         
34217         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34218             
34219             pos.push({
34220                 x : x,
34221                 y : y
34222             });
34223
34224             pos.push({
34225                 x : x + (this.unitWidth + this.gutter) * 1,
34226                 y : y
34227             });
34228             
34229             pos.push({
34230                 x : x + (this.unitWidth + this.gutter) * 2,
34231                 y : y
34232             });
34233             
34234             return pos;
34235             
34236         }
34237         
34238         if(box[0].size == 'xs' && box[1].size == 'xs'){
34239             
34240             pos.push({
34241                 x : x,
34242                 y : y
34243             });
34244
34245             pos.push({
34246                 x : x,
34247                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34248             });
34249             
34250             pos.push({
34251                 x : x + (this.unitWidth + this.gutter) * 1,
34252                 y : y
34253             });
34254             
34255             return pos;
34256             
34257         }
34258         
34259         pos.push({
34260             x : x,
34261             y : y
34262         });
34263
34264         pos.push({
34265             x : x + (this.unitWidth + this.gutter) * 2,
34266             y : y
34267         });
34268
34269         pos.push({
34270             x : x + (this.unitWidth + this.gutter) * 2,
34271             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34272         });
34273             
34274         return pos;
34275         
34276     },
34277     
34278     getVerticalFourBoxColPositions : function(x, y, box)
34279     {
34280         var pos = [];
34281         
34282         if(box[0].size == 'xs'){
34283             
34284             pos.push({
34285                 x : x,
34286                 y : y
34287             });
34288
34289             pos.push({
34290                 x : x,
34291                 y : y + (this.unitHeight + this.gutter) * 1
34292             });
34293             
34294             pos.push({
34295                 x : x,
34296                 y : y + (this.unitHeight + this.gutter) * 2
34297             });
34298             
34299             pos.push({
34300                 x : x + (this.unitWidth + this.gutter) * 1,
34301                 y : y
34302             });
34303             
34304             return pos;
34305             
34306         }
34307         
34308         pos.push({
34309             x : x,
34310             y : y
34311         });
34312
34313         pos.push({
34314             x : x + (this.unitWidth + this.gutter) * 2,
34315             y : y
34316         });
34317
34318         pos.push({
34319             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34320             y : y + (this.unitHeight + this.gutter) * 1
34321         });
34322
34323         pos.push({
34324             x : x + (this.unitWidth + this.gutter) * 2,
34325             y : y + (this.unitWidth + this.gutter) * 2
34326         });
34327
34328         return pos;
34329         
34330     },
34331     
34332     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34333     {
34334         var pos = [];
34335         
34336         if(box[0].size == 'md-left'){
34337             pos.push({
34338                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34339                 y : minY
34340             });
34341             
34342             return pos;
34343         }
34344         
34345         if(box[0].size == 'md-right'){
34346             pos.push({
34347                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34348                 y : minY + (this.unitWidth + this.gutter) * 1
34349             });
34350             
34351             return pos;
34352         }
34353         
34354         var rand = Math.floor(Math.random() * (4 - box[0].y));
34355         
34356         pos.push({
34357             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34358             y : minY + (this.unitWidth + this.gutter) * rand
34359         });
34360         
34361         return pos;
34362         
34363     },
34364     
34365     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34366     {
34367         var pos = [];
34368         
34369         if(box[0].size == 'xs'){
34370             
34371             pos.push({
34372                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34373                 y : minY
34374             });
34375
34376             pos.push({
34377                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34378                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34379             });
34380             
34381             return pos;
34382             
34383         }
34384         
34385         pos.push({
34386             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34387             y : minY
34388         });
34389
34390         pos.push({
34391             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34392             y : minY + (this.unitWidth + this.gutter) * 2
34393         });
34394         
34395         return pos;
34396         
34397     },
34398     
34399     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34400     {
34401         var pos = [];
34402         
34403         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34404             
34405             pos.push({
34406                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34407                 y : minY
34408             });
34409
34410             pos.push({
34411                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34412                 y : minY + (this.unitWidth + this.gutter) * 1
34413             });
34414             
34415             pos.push({
34416                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34417                 y : minY + (this.unitWidth + this.gutter) * 2
34418             });
34419             
34420             return pos;
34421             
34422         }
34423         
34424         if(box[0].size == 'xs' && box[1].size == 'xs'){
34425             
34426             pos.push({
34427                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34428                 y : minY
34429             });
34430
34431             pos.push({
34432                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34433                 y : minY
34434             });
34435             
34436             pos.push({
34437                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34438                 y : minY + (this.unitWidth + this.gutter) * 1
34439             });
34440             
34441             return pos;
34442             
34443         }
34444         
34445         pos.push({
34446             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34447             y : minY
34448         });
34449
34450         pos.push({
34451             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34452             y : minY + (this.unitWidth + this.gutter) * 2
34453         });
34454
34455         pos.push({
34456             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34457             y : minY + (this.unitWidth + this.gutter) * 2
34458         });
34459             
34460         return pos;
34461         
34462     },
34463     
34464     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34465     {
34466         var pos = [];
34467         
34468         if(box[0].size == 'xs'){
34469             
34470             pos.push({
34471                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34472                 y : minY
34473             });
34474
34475             pos.push({
34476                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34477                 y : minY
34478             });
34479             
34480             pos.push({
34481                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34482                 y : minY
34483             });
34484             
34485             pos.push({
34486                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34487                 y : minY + (this.unitWidth + this.gutter) * 1
34488             });
34489             
34490             return pos;
34491             
34492         }
34493         
34494         pos.push({
34495             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34496             y : minY
34497         });
34498         
34499         pos.push({
34500             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34501             y : minY + (this.unitWidth + this.gutter) * 2
34502         });
34503         
34504         pos.push({
34505             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34506             y : minY + (this.unitWidth + this.gutter) * 2
34507         });
34508         
34509         pos.push({
34510             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34511             y : minY + (this.unitWidth + this.gutter) * 2
34512         });
34513
34514         return pos;
34515         
34516     },
34517     
34518     /**
34519     * remove a Masonry Brick
34520     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34521     */
34522     removeBrick : function(brick_id)
34523     {
34524         if (!brick_id) {
34525             return;
34526         }
34527         
34528         for (var i = 0; i<this.bricks.length; i++) {
34529             if (this.bricks[i].id == brick_id) {
34530                 this.bricks.splice(i,1);
34531                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34532                 this.initial();
34533             }
34534         }
34535     },
34536     
34537     /**
34538     * adds a Masonry Brick
34539     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34540     */
34541     addBrick : function(cfg)
34542     {
34543         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34544         //this.register(cn);
34545         cn.parentId = this.id;
34546         cn.render(this.el);
34547         return cn;
34548     },
34549     
34550     /**
34551     * register a Masonry Brick
34552     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34553     */
34554     
34555     register : function(brick)
34556     {
34557         this.bricks.push(brick);
34558         brick.masonryId = this.id;
34559     },
34560     
34561     /**
34562     * clear all the Masonry Brick
34563     */
34564     clearAll : function()
34565     {
34566         this.bricks = [];
34567         //this.getChildContainer().dom.innerHTML = "";
34568         this.el.dom.innerHTML = '';
34569     },
34570     
34571     getSelected : function()
34572     {
34573         if (!this.selectedBrick) {
34574             return false;
34575         }
34576         
34577         return this.selectedBrick;
34578     }
34579 });
34580
34581 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34582     
34583     groups: {},
34584      /**
34585     * register a Masonry Layout
34586     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34587     */
34588     
34589     register : function(layout)
34590     {
34591         this.groups[layout.id] = layout;
34592     },
34593     /**
34594     * fetch a  Masonry Layout based on the masonry layout ID
34595     * @param {string} the masonry layout to add
34596     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34597     */
34598     
34599     get: function(layout_id) {
34600         if (typeof(this.groups[layout_id]) == 'undefined') {
34601             return false;
34602         }
34603         return this.groups[layout_id] ;
34604     }
34605     
34606     
34607     
34608 });
34609
34610  
34611
34612  /**
34613  *
34614  * This is based on 
34615  * http://masonry.desandro.com
34616  *
34617  * The idea is to render all the bricks based on vertical width...
34618  *
34619  * The original code extends 'outlayer' - we might need to use that....
34620  * 
34621  */
34622
34623
34624 /**
34625  * @class Roo.bootstrap.LayoutMasonryAuto
34626  * @extends Roo.bootstrap.Component
34627  * Bootstrap Layout Masonry class
34628  * 
34629  * @constructor
34630  * Create a new Element
34631  * @param {Object} config The config object
34632  */
34633
34634 Roo.bootstrap.LayoutMasonryAuto = function(config){
34635     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34636 };
34637
34638 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34639     
34640       /**
34641      * @cfg {Boolean} isFitWidth  - resize the width..
34642      */   
34643     isFitWidth : false,  // options..
34644     /**
34645      * @cfg {Boolean} isOriginLeft = left align?
34646      */   
34647     isOriginLeft : true,
34648     /**
34649      * @cfg {Boolean} isOriginTop = top align?
34650      */   
34651     isOriginTop : false,
34652     /**
34653      * @cfg {Boolean} isLayoutInstant = no animation?
34654      */   
34655     isLayoutInstant : false, // needed?
34656     /**
34657      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34658      */   
34659     isResizingContainer : true,
34660     /**
34661      * @cfg {Number} columnWidth  width of the columns 
34662      */   
34663     
34664     columnWidth : 0,
34665     
34666     /**
34667      * @cfg {Number} maxCols maximum number of columns
34668      */   
34669     
34670     maxCols: 0,
34671     /**
34672      * @cfg {Number} padHeight padding below box..
34673      */   
34674     
34675     padHeight : 10, 
34676     
34677     /**
34678      * @cfg {Boolean} isAutoInitial defalut true
34679      */   
34680     
34681     isAutoInitial : true, 
34682     
34683     // private?
34684     gutter : 0,
34685     
34686     containerWidth: 0,
34687     initialColumnWidth : 0,
34688     currentSize : null,
34689     
34690     colYs : null, // array.
34691     maxY : 0,
34692     padWidth: 10,
34693     
34694     
34695     tag: 'div',
34696     cls: '',
34697     bricks: null, //CompositeElement
34698     cols : 0, // array?
34699     // element : null, // wrapped now this.el
34700     _isLayoutInited : null, 
34701     
34702     
34703     getAutoCreate : function(){
34704         
34705         var cfg = {
34706             tag: this.tag,
34707             cls: 'blog-masonary-wrapper ' + this.cls,
34708             cn : {
34709                 cls : 'mas-boxes masonary'
34710             }
34711         };
34712         
34713         return cfg;
34714     },
34715     
34716     getChildContainer: function( )
34717     {
34718         if (this.boxesEl) {
34719             return this.boxesEl;
34720         }
34721         
34722         this.boxesEl = this.el.select('.mas-boxes').first();
34723         
34724         return this.boxesEl;
34725     },
34726     
34727     
34728     initEvents : function()
34729     {
34730         var _this = this;
34731         
34732         if(this.isAutoInitial){
34733             Roo.log('hook children rendered');
34734             this.on('childrenrendered', function() {
34735                 Roo.log('children rendered');
34736                 _this.initial();
34737             } ,this);
34738         }
34739         
34740     },
34741     
34742     initial : function()
34743     {
34744         this.reloadItems();
34745
34746         this.currentSize = this.el.getBox(true);
34747
34748         /// was window resize... - let's see if this works..
34749         Roo.EventManager.onWindowResize(this.resize, this); 
34750
34751         if(!this.isAutoInitial){
34752             this.layout();
34753             return;
34754         }
34755         
34756         this.layout.defer(500,this);
34757     },
34758     
34759     reloadItems: function()
34760     {
34761         this.bricks = this.el.select('.masonry-brick', true);
34762         
34763         this.bricks.each(function(b) {
34764             //Roo.log(b.getSize());
34765             if (!b.attr('originalwidth')) {
34766                 b.attr('originalwidth',  b.getSize().width);
34767             }
34768             
34769         });
34770         
34771         Roo.log(this.bricks.elements.length);
34772     },
34773     
34774     resize : function()
34775     {
34776         Roo.log('resize');
34777         var cs = this.el.getBox(true);
34778         
34779         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34780             Roo.log("no change in with or X");
34781             return;
34782         }
34783         this.currentSize = cs;
34784         this.layout();
34785     },
34786     
34787     layout : function()
34788     {
34789          Roo.log('layout');
34790         this._resetLayout();
34791         //this._manageStamps();
34792       
34793         // don't animate first layout
34794         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34795         this.layoutItems( isInstant );
34796       
34797         // flag for initalized
34798         this._isLayoutInited = true;
34799     },
34800     
34801     layoutItems : function( isInstant )
34802     {
34803         //var items = this._getItemsForLayout( this.items );
34804         // original code supports filtering layout items.. we just ignore it..
34805         
34806         this._layoutItems( this.bricks , isInstant );
34807       
34808         this._postLayout();
34809     },
34810     _layoutItems : function ( items , isInstant)
34811     {
34812        //this.fireEvent( 'layout', this, items );
34813     
34814
34815         if ( !items || !items.elements.length ) {
34816           // no items, emit event with empty array
34817             return;
34818         }
34819
34820         var queue = [];
34821         items.each(function(item) {
34822             Roo.log("layout item");
34823             Roo.log(item);
34824             // get x/y object from method
34825             var position = this._getItemLayoutPosition( item );
34826             // enqueue
34827             position.item = item;
34828             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34829             queue.push( position );
34830         }, this);
34831       
34832         this._processLayoutQueue( queue );
34833     },
34834     /** Sets position of item in DOM
34835     * @param {Element} item
34836     * @param {Number} x - horizontal position
34837     * @param {Number} y - vertical position
34838     * @param {Boolean} isInstant - disables transitions
34839     */
34840     _processLayoutQueue : function( queue )
34841     {
34842         for ( var i=0, len = queue.length; i < len; i++ ) {
34843             var obj = queue[i];
34844             obj.item.position('absolute');
34845             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34846         }
34847     },
34848       
34849     
34850     /**
34851     * Any logic you want to do after each layout,
34852     * i.e. size the container
34853     */
34854     _postLayout : function()
34855     {
34856         this.resizeContainer();
34857     },
34858     
34859     resizeContainer : function()
34860     {
34861         if ( !this.isResizingContainer ) {
34862             return;
34863         }
34864         var size = this._getContainerSize();
34865         if ( size ) {
34866             this.el.setSize(size.width,size.height);
34867             this.boxesEl.setSize(size.width,size.height);
34868         }
34869     },
34870     
34871     
34872     
34873     _resetLayout : function()
34874     {
34875         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34876         this.colWidth = this.el.getWidth();
34877         //this.gutter = this.el.getWidth(); 
34878         
34879         this.measureColumns();
34880
34881         // reset column Y
34882         var i = this.cols;
34883         this.colYs = [];
34884         while (i--) {
34885             this.colYs.push( 0 );
34886         }
34887     
34888         this.maxY = 0;
34889     },
34890
34891     measureColumns : function()
34892     {
34893         this.getContainerWidth();
34894       // if columnWidth is 0, default to outerWidth of first item
34895         if ( !this.columnWidth ) {
34896             var firstItem = this.bricks.first();
34897             Roo.log(firstItem);
34898             this.columnWidth  = this.containerWidth;
34899             if (firstItem && firstItem.attr('originalwidth') ) {
34900                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34901             }
34902             // columnWidth fall back to item of first element
34903             Roo.log("set column width?");
34904                         this.initialColumnWidth = this.columnWidth  ;
34905
34906             // if first elem has no width, default to size of container
34907             
34908         }
34909         
34910         
34911         if (this.initialColumnWidth) {
34912             this.columnWidth = this.initialColumnWidth;
34913         }
34914         
34915         
34916             
34917         // column width is fixed at the top - however if container width get's smaller we should
34918         // reduce it...
34919         
34920         // this bit calcs how man columns..
34921             
34922         var columnWidth = this.columnWidth += this.gutter;
34923       
34924         // calculate columns
34925         var containerWidth = this.containerWidth + this.gutter;
34926         
34927         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34928         // fix rounding errors, typically with gutters
34929         var excess = columnWidth - containerWidth % columnWidth;
34930         
34931         
34932         // if overshoot is less than a pixel, round up, otherwise floor it
34933         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34934         cols = Math[ mathMethod ]( cols );
34935         this.cols = Math.max( cols, 1 );
34936         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34937         
34938          // padding positioning..
34939         var totalColWidth = this.cols * this.columnWidth;
34940         var padavail = this.containerWidth - totalColWidth;
34941         // so for 2 columns - we need 3 'pads'
34942         
34943         var padNeeded = (1+this.cols) * this.padWidth;
34944         
34945         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34946         
34947         this.columnWidth += padExtra
34948         //this.padWidth = Math.floor(padavail /  ( this.cols));
34949         
34950         // adjust colum width so that padding is fixed??
34951         
34952         // we have 3 columns ... total = width * 3
34953         // we have X left over... that should be used by 
34954         
34955         //if (this.expandC) {
34956             
34957         //}
34958         
34959         
34960         
34961     },
34962     
34963     getContainerWidth : function()
34964     {
34965        /* // container is parent if fit width
34966         var container = this.isFitWidth ? this.element.parentNode : this.element;
34967         // check that this.size and size are there
34968         // IE8 triggers resize on body size change, so they might not be
34969         
34970         var size = getSize( container );  //FIXME
34971         this.containerWidth = size && size.innerWidth; //FIXME
34972         */
34973          
34974         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34975         
34976     },
34977     
34978     _getItemLayoutPosition : function( item )  // what is item?
34979     {
34980         // we resize the item to our columnWidth..
34981       
34982         item.setWidth(this.columnWidth);
34983         item.autoBoxAdjust  = false;
34984         
34985         var sz = item.getSize();
34986  
34987         // how many columns does this brick span
34988         var remainder = this.containerWidth % this.columnWidth;
34989         
34990         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34991         // round if off by 1 pixel, otherwise use ceil
34992         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34993         colSpan = Math.min( colSpan, this.cols );
34994         
34995         // normally this should be '1' as we dont' currently allow multi width columns..
34996         
34997         var colGroup = this._getColGroup( colSpan );
34998         // get the minimum Y value from the columns
34999         var minimumY = Math.min.apply( Math, colGroup );
35000         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35001         
35002         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35003          
35004         // position the brick
35005         var position = {
35006             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35007             y: this.currentSize.y + minimumY + this.padHeight
35008         };
35009         
35010         Roo.log(position);
35011         // apply setHeight to necessary columns
35012         var setHeight = minimumY + sz.height + this.padHeight;
35013         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35014         
35015         var setSpan = this.cols + 1 - colGroup.length;
35016         for ( var i = 0; i < setSpan; i++ ) {
35017           this.colYs[ shortColIndex + i ] = setHeight ;
35018         }
35019       
35020         return position;
35021     },
35022     
35023     /**
35024      * @param {Number} colSpan - number of columns the element spans
35025      * @returns {Array} colGroup
35026      */
35027     _getColGroup : function( colSpan )
35028     {
35029         if ( colSpan < 2 ) {
35030           // if brick spans only one column, use all the column Ys
35031           return this.colYs;
35032         }
35033       
35034         var colGroup = [];
35035         // how many different places could this brick fit horizontally
35036         var groupCount = this.cols + 1 - colSpan;
35037         // for each group potential horizontal position
35038         for ( var i = 0; i < groupCount; i++ ) {
35039           // make an array of colY values for that one group
35040           var groupColYs = this.colYs.slice( i, i + colSpan );
35041           // and get the max value of the array
35042           colGroup[i] = Math.max.apply( Math, groupColYs );
35043         }
35044         return colGroup;
35045     },
35046     /*
35047     _manageStamp : function( stamp )
35048     {
35049         var stampSize =  stamp.getSize();
35050         var offset = stamp.getBox();
35051         // get the columns that this stamp affects
35052         var firstX = this.isOriginLeft ? offset.x : offset.right;
35053         var lastX = firstX + stampSize.width;
35054         var firstCol = Math.floor( firstX / this.columnWidth );
35055         firstCol = Math.max( 0, firstCol );
35056         
35057         var lastCol = Math.floor( lastX / this.columnWidth );
35058         // lastCol should not go over if multiple of columnWidth #425
35059         lastCol -= lastX % this.columnWidth ? 0 : 1;
35060         lastCol = Math.min( this.cols - 1, lastCol );
35061         
35062         // set colYs to bottom of the stamp
35063         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35064             stampSize.height;
35065             
35066         for ( var i = firstCol; i <= lastCol; i++ ) {
35067           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35068         }
35069     },
35070     */
35071     
35072     _getContainerSize : function()
35073     {
35074         this.maxY = Math.max.apply( Math, this.colYs );
35075         var size = {
35076             height: this.maxY
35077         };
35078       
35079         if ( this.isFitWidth ) {
35080             size.width = this._getContainerFitWidth();
35081         }
35082       
35083         return size;
35084     },
35085     
35086     _getContainerFitWidth : function()
35087     {
35088         var unusedCols = 0;
35089         // count unused columns
35090         var i = this.cols;
35091         while ( --i ) {
35092           if ( this.colYs[i] !== 0 ) {
35093             break;
35094           }
35095           unusedCols++;
35096         }
35097         // fit container to columns that have been used
35098         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35099     },
35100     
35101     needsResizeLayout : function()
35102     {
35103         var previousWidth = this.containerWidth;
35104         this.getContainerWidth();
35105         return previousWidth !== this.containerWidth;
35106     }
35107  
35108 });
35109
35110  
35111
35112  /*
35113  * - LGPL
35114  *
35115  * element
35116  * 
35117  */
35118
35119 /**
35120  * @class Roo.bootstrap.MasonryBrick
35121  * @extends Roo.bootstrap.Component
35122  * Bootstrap MasonryBrick class
35123  * 
35124  * @constructor
35125  * Create a new MasonryBrick
35126  * @param {Object} config The config object
35127  */
35128
35129 Roo.bootstrap.MasonryBrick = function(config){
35130     
35131     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35132     
35133     Roo.bootstrap.MasonryBrick.register(this);
35134     
35135     this.addEvents({
35136         // raw events
35137         /**
35138          * @event click
35139          * When a MasonryBrick is clcik
35140          * @param {Roo.bootstrap.MasonryBrick} this
35141          * @param {Roo.EventObject} e
35142          */
35143         "click" : true
35144     });
35145 };
35146
35147 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35148     
35149     /**
35150      * @cfg {String} title
35151      */   
35152     title : '',
35153     /**
35154      * @cfg {String} html
35155      */   
35156     html : '',
35157     /**
35158      * @cfg {String} bgimage
35159      */   
35160     bgimage : '',
35161     /**
35162      * @cfg {String} videourl
35163      */   
35164     videourl : '',
35165     /**
35166      * @cfg {String} cls
35167      */   
35168     cls : '',
35169     /**
35170      * @cfg {String} href
35171      */   
35172     href : '',
35173     /**
35174      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35175      */   
35176     size : 'xs',
35177     
35178     /**
35179      * @cfg {String} placetitle (center|bottom)
35180      */   
35181     placetitle : '',
35182     
35183     /**
35184      * @cfg {Boolean} isFitContainer defalut true
35185      */   
35186     isFitContainer : true, 
35187     
35188     /**
35189      * @cfg {Boolean} preventDefault defalut false
35190      */   
35191     preventDefault : false, 
35192     
35193     /**
35194      * @cfg {Boolean} inverse defalut false
35195      */   
35196     maskInverse : false, 
35197     
35198     getAutoCreate : function()
35199     {
35200         if(!this.isFitContainer){
35201             return this.getSplitAutoCreate();
35202         }
35203         
35204         var cls = 'masonry-brick masonry-brick-full';
35205         
35206         if(this.href.length){
35207             cls += ' masonry-brick-link';
35208         }
35209         
35210         if(this.bgimage.length){
35211             cls += ' masonry-brick-image';
35212         }
35213         
35214         if(this.maskInverse){
35215             cls += ' mask-inverse';
35216         }
35217         
35218         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35219             cls += ' enable-mask';
35220         }
35221         
35222         if(this.size){
35223             cls += ' masonry-' + this.size + '-brick';
35224         }
35225         
35226         if(this.placetitle.length){
35227             
35228             switch (this.placetitle) {
35229                 case 'center' :
35230                     cls += ' masonry-center-title';
35231                     break;
35232                 case 'bottom' :
35233                     cls += ' masonry-bottom-title';
35234                     break;
35235                 default:
35236                     break;
35237             }
35238             
35239         } else {
35240             if(!this.html.length && !this.bgimage.length){
35241                 cls += ' masonry-center-title';
35242             }
35243
35244             if(!this.html.length && this.bgimage.length){
35245                 cls += ' masonry-bottom-title';
35246             }
35247         }
35248         
35249         if(this.cls){
35250             cls += ' ' + this.cls;
35251         }
35252         
35253         var cfg = {
35254             tag: (this.href.length) ? 'a' : 'div',
35255             cls: cls,
35256             cn: [
35257                 {
35258                     tag: 'div',
35259                     cls: 'masonry-brick-mask'
35260                 },
35261                 {
35262                     tag: 'div',
35263                     cls: 'masonry-brick-paragraph',
35264                     cn: []
35265                 }
35266             ]
35267         };
35268         
35269         if(this.href.length){
35270             cfg.href = this.href;
35271         }
35272         
35273         var cn = cfg.cn[1].cn;
35274         
35275         if(this.title.length){
35276             cn.push({
35277                 tag: 'h4',
35278                 cls: 'masonry-brick-title',
35279                 html: this.title
35280             });
35281         }
35282         
35283         if(this.html.length){
35284             cn.push({
35285                 tag: 'p',
35286                 cls: 'masonry-brick-text',
35287                 html: this.html
35288             });
35289         }
35290         
35291         if (!this.title.length && !this.html.length) {
35292             cfg.cn[1].cls += ' hide';
35293         }
35294         
35295         if(this.bgimage.length){
35296             cfg.cn.push({
35297                 tag: 'img',
35298                 cls: 'masonry-brick-image-view',
35299                 src: this.bgimage
35300             });
35301         }
35302         
35303         if(this.videourl.length){
35304             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35305             // youtube support only?
35306             cfg.cn.push({
35307                 tag: 'iframe',
35308                 cls: 'masonry-brick-image-view',
35309                 src: vurl,
35310                 frameborder : 0,
35311                 allowfullscreen : true
35312             });
35313         }
35314         
35315         return cfg;
35316         
35317     },
35318     
35319     getSplitAutoCreate : function()
35320     {
35321         var cls = 'masonry-brick masonry-brick-split';
35322         
35323         if(this.href.length){
35324             cls += ' masonry-brick-link';
35325         }
35326         
35327         if(this.bgimage.length){
35328             cls += ' masonry-brick-image';
35329         }
35330         
35331         if(this.size){
35332             cls += ' masonry-' + this.size + '-brick';
35333         }
35334         
35335         switch (this.placetitle) {
35336             case 'center' :
35337                 cls += ' masonry-center-title';
35338                 break;
35339             case 'bottom' :
35340                 cls += ' masonry-bottom-title';
35341                 break;
35342             default:
35343                 if(!this.bgimage.length){
35344                     cls += ' masonry-center-title';
35345                 }
35346
35347                 if(this.bgimage.length){
35348                     cls += ' masonry-bottom-title';
35349                 }
35350                 break;
35351         }
35352         
35353         if(this.cls){
35354             cls += ' ' + this.cls;
35355         }
35356         
35357         var cfg = {
35358             tag: (this.href.length) ? 'a' : 'div',
35359             cls: cls,
35360             cn: [
35361                 {
35362                     tag: 'div',
35363                     cls: 'masonry-brick-split-head',
35364                     cn: [
35365                         {
35366                             tag: 'div',
35367                             cls: 'masonry-brick-paragraph',
35368                             cn: []
35369                         }
35370                     ]
35371                 },
35372                 {
35373                     tag: 'div',
35374                     cls: 'masonry-brick-split-body',
35375                     cn: []
35376                 }
35377             ]
35378         };
35379         
35380         if(this.href.length){
35381             cfg.href = this.href;
35382         }
35383         
35384         if(this.title.length){
35385             cfg.cn[0].cn[0].cn.push({
35386                 tag: 'h4',
35387                 cls: 'masonry-brick-title',
35388                 html: this.title
35389             });
35390         }
35391         
35392         if(this.html.length){
35393             cfg.cn[1].cn.push({
35394                 tag: 'p',
35395                 cls: 'masonry-brick-text',
35396                 html: this.html
35397             });
35398         }
35399
35400         if(this.bgimage.length){
35401             cfg.cn[0].cn.push({
35402                 tag: 'img',
35403                 cls: 'masonry-brick-image-view',
35404                 src: this.bgimage
35405             });
35406         }
35407         
35408         if(this.videourl.length){
35409             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35410             // youtube support only?
35411             cfg.cn[0].cn.cn.push({
35412                 tag: 'iframe',
35413                 cls: 'masonry-brick-image-view',
35414                 src: vurl,
35415                 frameborder : 0,
35416                 allowfullscreen : true
35417             });
35418         }
35419         
35420         return cfg;
35421     },
35422     
35423     initEvents: function() 
35424     {
35425         switch (this.size) {
35426             case 'xs' :
35427                 this.x = 1;
35428                 this.y = 1;
35429                 break;
35430             case 'sm' :
35431                 this.x = 2;
35432                 this.y = 2;
35433                 break;
35434             case 'md' :
35435             case 'md-left' :
35436             case 'md-right' :
35437                 this.x = 3;
35438                 this.y = 3;
35439                 break;
35440             case 'tall' :
35441                 this.x = 2;
35442                 this.y = 3;
35443                 break;
35444             case 'wide' :
35445                 this.x = 3;
35446                 this.y = 2;
35447                 break;
35448             case 'wide-thin' :
35449                 this.x = 3;
35450                 this.y = 1;
35451                 break;
35452                         
35453             default :
35454                 break;
35455         }
35456         
35457         if(Roo.isTouch){
35458             this.el.on('touchstart', this.onTouchStart, this);
35459             this.el.on('touchmove', this.onTouchMove, this);
35460             this.el.on('touchend', this.onTouchEnd, this);
35461             this.el.on('contextmenu', this.onContextMenu, this);
35462         } else {
35463             this.el.on('mouseenter'  ,this.enter, this);
35464             this.el.on('mouseleave', this.leave, this);
35465             this.el.on('click', this.onClick, this);
35466         }
35467         
35468         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35469             this.parent().bricks.push(this);   
35470         }
35471         
35472     },
35473     
35474     onClick: function(e, el)
35475     {
35476         var time = this.endTimer - this.startTimer;
35477         // Roo.log(e.preventDefault());
35478         if(Roo.isTouch){
35479             if(time > 1000){
35480                 e.preventDefault();
35481                 return;
35482             }
35483         }
35484         
35485         if(!this.preventDefault){
35486             return;
35487         }
35488         
35489         e.preventDefault();
35490         
35491         if (this.activeClass != '') {
35492             this.selectBrick();
35493         }
35494         
35495         this.fireEvent('click', this, e);
35496     },
35497     
35498     enter: function(e, el)
35499     {
35500         e.preventDefault();
35501         
35502         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35503             return;
35504         }
35505         
35506         if(this.bgimage.length && this.html.length){
35507             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35508         }
35509     },
35510     
35511     leave: function(e, el)
35512     {
35513         e.preventDefault();
35514         
35515         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35516             return;
35517         }
35518         
35519         if(this.bgimage.length && this.html.length){
35520             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35521         }
35522     },
35523     
35524     onTouchStart: function(e, el)
35525     {
35526 //        e.preventDefault();
35527         
35528         this.touchmoved = false;
35529         
35530         if(!this.isFitContainer){
35531             return;
35532         }
35533         
35534         if(!this.bgimage.length || !this.html.length){
35535             return;
35536         }
35537         
35538         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35539         
35540         this.timer = new Date().getTime();
35541         
35542     },
35543     
35544     onTouchMove: function(e, el)
35545     {
35546         this.touchmoved = true;
35547     },
35548     
35549     onContextMenu : function(e,el)
35550     {
35551         e.preventDefault();
35552         e.stopPropagation();
35553         return false;
35554     },
35555     
35556     onTouchEnd: function(e, el)
35557     {
35558 //        e.preventDefault();
35559         
35560         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35561         
35562             this.leave(e,el);
35563             
35564             return;
35565         }
35566         
35567         if(!this.bgimage.length || !this.html.length){
35568             
35569             if(this.href.length){
35570                 window.location.href = this.href;
35571             }
35572             
35573             return;
35574         }
35575         
35576         if(!this.isFitContainer){
35577             return;
35578         }
35579         
35580         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35581         
35582         window.location.href = this.href;
35583     },
35584     
35585     //selection on single brick only
35586     selectBrick : function() {
35587         
35588         if (!this.parentId) {
35589             return;
35590         }
35591         
35592         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35593         var index = m.selectedBrick.indexOf(this.id);
35594         
35595         if ( index > -1) {
35596             m.selectedBrick.splice(index,1);
35597             this.el.removeClass(this.activeClass);
35598             return;
35599         }
35600         
35601         for(var i = 0; i < m.selectedBrick.length; i++) {
35602             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35603             b.el.removeClass(b.activeClass);
35604         }
35605         
35606         m.selectedBrick = [];
35607         
35608         m.selectedBrick.push(this.id);
35609         this.el.addClass(this.activeClass);
35610         return;
35611     },
35612     
35613     isSelected : function(){
35614         return this.el.hasClass(this.activeClass);
35615         
35616     }
35617 });
35618
35619 Roo.apply(Roo.bootstrap.MasonryBrick, {
35620     
35621     //groups: {},
35622     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35623      /**
35624     * register a Masonry Brick
35625     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35626     */
35627     
35628     register : function(brick)
35629     {
35630         //this.groups[brick.id] = brick;
35631         this.groups.add(brick.id, brick);
35632     },
35633     /**
35634     * fetch a  masonry brick based on the masonry brick ID
35635     * @param {string} the masonry brick to add
35636     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35637     */
35638     
35639     get: function(brick_id) 
35640     {
35641         // if (typeof(this.groups[brick_id]) == 'undefined') {
35642         //     return false;
35643         // }
35644         // return this.groups[brick_id] ;
35645         
35646         if(this.groups.key(brick_id)) {
35647             return this.groups.key(brick_id);
35648         }
35649         
35650         return false;
35651     }
35652     
35653     
35654     
35655 });
35656
35657  /*
35658  * - LGPL
35659  *
35660  * element
35661  * 
35662  */
35663
35664 /**
35665  * @class Roo.bootstrap.Brick
35666  * @extends Roo.bootstrap.Component
35667  * Bootstrap Brick class
35668  * 
35669  * @constructor
35670  * Create a new Brick
35671  * @param {Object} config The config object
35672  */
35673
35674 Roo.bootstrap.Brick = function(config){
35675     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35676     
35677     this.addEvents({
35678         // raw events
35679         /**
35680          * @event click
35681          * When a Brick is click
35682          * @param {Roo.bootstrap.Brick} this
35683          * @param {Roo.EventObject} e
35684          */
35685         "click" : true
35686     });
35687 };
35688
35689 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35690     
35691     /**
35692      * @cfg {String} title
35693      */   
35694     title : '',
35695     /**
35696      * @cfg {String} html
35697      */   
35698     html : '',
35699     /**
35700      * @cfg {String} bgimage
35701      */   
35702     bgimage : '',
35703     /**
35704      * @cfg {String} cls
35705      */   
35706     cls : '',
35707     /**
35708      * @cfg {String} href
35709      */   
35710     href : '',
35711     /**
35712      * @cfg {String} video
35713      */   
35714     video : '',
35715     /**
35716      * @cfg {Boolean} square
35717      */   
35718     square : true,
35719     
35720     getAutoCreate : function()
35721     {
35722         var cls = 'roo-brick';
35723         
35724         if(this.href.length){
35725             cls += ' roo-brick-link';
35726         }
35727         
35728         if(this.bgimage.length){
35729             cls += ' roo-brick-image';
35730         }
35731         
35732         if(!this.html.length && !this.bgimage.length){
35733             cls += ' roo-brick-center-title';
35734         }
35735         
35736         if(!this.html.length && this.bgimage.length){
35737             cls += ' roo-brick-bottom-title';
35738         }
35739         
35740         if(this.cls){
35741             cls += ' ' + this.cls;
35742         }
35743         
35744         var cfg = {
35745             tag: (this.href.length) ? 'a' : 'div',
35746             cls: cls,
35747             cn: [
35748                 {
35749                     tag: 'div',
35750                     cls: 'roo-brick-paragraph',
35751                     cn: []
35752                 }
35753             ]
35754         };
35755         
35756         if(this.href.length){
35757             cfg.href = this.href;
35758         }
35759         
35760         var cn = cfg.cn[0].cn;
35761         
35762         if(this.title.length){
35763             cn.push({
35764                 tag: 'h4',
35765                 cls: 'roo-brick-title',
35766                 html: this.title
35767             });
35768         }
35769         
35770         if(this.html.length){
35771             cn.push({
35772                 tag: 'p',
35773                 cls: 'roo-brick-text',
35774                 html: this.html
35775             });
35776         } else {
35777             cn.cls += ' hide';
35778         }
35779         
35780         if(this.bgimage.length){
35781             cfg.cn.push({
35782                 tag: 'img',
35783                 cls: 'roo-brick-image-view',
35784                 src: this.bgimage
35785             });
35786         }
35787         
35788         return cfg;
35789     },
35790     
35791     initEvents: function() 
35792     {
35793         if(this.title.length || this.html.length){
35794             this.el.on('mouseenter'  ,this.enter, this);
35795             this.el.on('mouseleave', this.leave, this);
35796         }
35797         
35798         Roo.EventManager.onWindowResize(this.resize, this); 
35799         
35800         if(this.bgimage.length){
35801             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35802             this.imageEl.on('load', this.onImageLoad, this);
35803             return;
35804         }
35805         
35806         this.resize();
35807     },
35808     
35809     onImageLoad : function()
35810     {
35811         this.resize();
35812     },
35813     
35814     resize : function()
35815     {
35816         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35817         
35818         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35819         
35820         if(this.bgimage.length){
35821             var image = this.el.select('.roo-brick-image-view', true).first();
35822             
35823             image.setWidth(paragraph.getWidth());
35824             
35825             if(this.square){
35826                 image.setHeight(paragraph.getWidth());
35827             }
35828             
35829             this.el.setHeight(image.getHeight());
35830             paragraph.setHeight(image.getHeight());
35831             
35832         }
35833         
35834     },
35835     
35836     enter: function(e, el)
35837     {
35838         e.preventDefault();
35839         
35840         if(this.bgimage.length){
35841             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35842             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35843         }
35844     },
35845     
35846     leave: function(e, el)
35847     {
35848         e.preventDefault();
35849         
35850         if(this.bgimage.length){
35851             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35852             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35853         }
35854     }
35855     
35856 });
35857
35858  
35859
35860  /*
35861  * - LGPL
35862  *
35863  * Number field 
35864  */
35865
35866 /**
35867  * @class Roo.bootstrap.NumberField
35868  * @extends Roo.bootstrap.Input
35869  * Bootstrap NumberField class
35870  * 
35871  * 
35872  * 
35873  * 
35874  * @constructor
35875  * Create a new NumberField
35876  * @param {Object} config The config object
35877  */
35878
35879 Roo.bootstrap.NumberField = function(config){
35880     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35881 };
35882
35883 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35884     
35885     /**
35886      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35887      */
35888     allowDecimals : true,
35889     /**
35890      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35891      */
35892     decimalSeparator : ".",
35893     /**
35894      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35895      */
35896     decimalPrecision : 2,
35897     /**
35898      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35899      */
35900     allowNegative : true,
35901     
35902     /**
35903      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35904      */
35905     allowZero: true,
35906     /**
35907      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35908      */
35909     minValue : Number.NEGATIVE_INFINITY,
35910     /**
35911      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35912      */
35913     maxValue : Number.MAX_VALUE,
35914     /**
35915      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35916      */
35917     minText : "The minimum value for this field is {0}",
35918     /**
35919      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35920      */
35921     maxText : "The maximum value for this field is {0}",
35922     /**
35923      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35924      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35925      */
35926     nanText : "{0} is not a valid number",
35927     /**
35928      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35929      */
35930     thousandsDelimiter : false,
35931     /**
35932      * @cfg {String} valueAlign alignment of value
35933      */
35934     valueAlign : "left",
35935
35936     getAutoCreate : function()
35937     {
35938         var hiddenInput = {
35939             tag: 'input',
35940             type: 'hidden',
35941             id: Roo.id(),
35942             cls: 'hidden-number-input'
35943         };
35944         
35945         if (this.name) {
35946             hiddenInput.name = this.name;
35947         }
35948         
35949         this.name = '';
35950         
35951         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35952         
35953         this.name = hiddenInput.name;
35954         
35955         if(cfg.cn.length > 0) {
35956             cfg.cn.push(hiddenInput);
35957         }
35958         
35959         return cfg;
35960     },
35961
35962     // private
35963     initEvents : function()
35964     {   
35965         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35966         
35967         var allowed = "0123456789";
35968         
35969         if(this.allowDecimals){
35970             allowed += this.decimalSeparator;
35971         }
35972         
35973         if(this.allowNegative){
35974             allowed += "-";
35975         }
35976         
35977         if(this.thousandsDelimiter) {
35978             allowed += ",";
35979         }
35980         
35981         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35982         
35983         var keyPress = function(e){
35984             
35985             var k = e.getKey();
35986             
35987             var c = e.getCharCode();
35988             
35989             if(
35990                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35991                     allowed.indexOf(String.fromCharCode(c)) === -1
35992             ){
35993                 e.stopEvent();
35994                 return;
35995             }
35996             
35997             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35998                 return;
35999             }
36000             
36001             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36002                 e.stopEvent();
36003             }
36004         };
36005         
36006         this.el.on("keypress", keyPress, this);
36007     },
36008     
36009     validateValue : function(value)
36010     {
36011         
36012         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36013             return false;
36014         }
36015         
36016         var num = this.parseValue(value);
36017         
36018         if(isNaN(num)){
36019             this.markInvalid(String.format(this.nanText, value));
36020             return false;
36021         }
36022         
36023         if(num < this.minValue){
36024             this.markInvalid(String.format(this.minText, this.minValue));
36025             return false;
36026         }
36027         
36028         if(num > this.maxValue){
36029             this.markInvalid(String.format(this.maxText, this.maxValue));
36030             return false;
36031         }
36032         
36033         return true;
36034     },
36035
36036     getValue : function()
36037     {
36038         var v = this.hiddenEl().getValue();
36039         
36040         return this.fixPrecision(this.parseValue(v));
36041     },
36042
36043     parseValue : function(value)
36044     {
36045         if(this.thousandsDelimiter) {
36046             value += "";
36047             r = new RegExp(",", "g");
36048             value = value.replace(r, "");
36049         }
36050         
36051         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36052         return isNaN(value) ? '' : value;
36053     },
36054
36055     fixPrecision : function(value)
36056     {
36057         if(this.thousandsDelimiter) {
36058             value += "";
36059             r = new RegExp(",", "g");
36060             value = value.replace(r, "");
36061         }
36062         
36063         var nan = isNaN(value);
36064         
36065         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36066             return nan ? '' : value;
36067         }
36068         return parseFloat(value).toFixed(this.decimalPrecision);
36069     },
36070
36071     setValue : function(v)
36072     {
36073         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36074         
36075         this.value = v;
36076         
36077         if(this.rendered){
36078             
36079             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36080             
36081             this.inputEl().dom.value = (v == '') ? '' :
36082                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36083             
36084             if(!this.allowZero && v === '0') {
36085                 this.hiddenEl().dom.value = '';
36086                 this.inputEl().dom.value = '';
36087             }
36088             
36089             this.validate();
36090         }
36091     },
36092
36093     decimalPrecisionFcn : function(v)
36094     {
36095         return Math.floor(v);
36096     },
36097
36098     beforeBlur : function()
36099     {
36100         var v = this.parseValue(this.getRawValue());
36101         
36102         if(v || v === 0 || v === ''){
36103             this.setValue(v);
36104         }
36105     },
36106     
36107     hiddenEl : function()
36108     {
36109         return this.el.select('input.hidden-number-input',true).first();
36110     }
36111     
36112 });
36113
36114  
36115
36116 /*
36117 * Licence: LGPL
36118 */
36119
36120 /**
36121  * @class Roo.bootstrap.DocumentSlider
36122  * @extends Roo.bootstrap.Component
36123  * Bootstrap DocumentSlider class
36124  * 
36125  * @constructor
36126  * Create a new DocumentViewer
36127  * @param {Object} config The config object
36128  */
36129
36130 Roo.bootstrap.DocumentSlider = function(config){
36131     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36132     
36133     this.files = [];
36134     
36135     this.addEvents({
36136         /**
36137          * @event initial
36138          * Fire after initEvent
36139          * @param {Roo.bootstrap.DocumentSlider} this
36140          */
36141         "initial" : true,
36142         /**
36143          * @event update
36144          * Fire after update
36145          * @param {Roo.bootstrap.DocumentSlider} this
36146          */
36147         "update" : true,
36148         /**
36149          * @event click
36150          * Fire after click
36151          * @param {Roo.bootstrap.DocumentSlider} this
36152          */
36153         "click" : true
36154     });
36155 };
36156
36157 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36158     
36159     files : false,
36160     
36161     indicator : 0,
36162     
36163     getAutoCreate : function()
36164     {
36165         var cfg = {
36166             tag : 'div',
36167             cls : 'roo-document-slider',
36168             cn : [
36169                 {
36170                     tag : 'div',
36171                     cls : 'roo-document-slider-header',
36172                     cn : [
36173                         {
36174                             tag : 'div',
36175                             cls : 'roo-document-slider-header-title'
36176                         }
36177                     ]
36178                 },
36179                 {
36180                     tag : 'div',
36181                     cls : 'roo-document-slider-body',
36182                     cn : [
36183                         {
36184                             tag : 'div',
36185                             cls : 'roo-document-slider-prev',
36186                             cn : [
36187                                 {
36188                                     tag : 'i',
36189                                     cls : 'fa fa-chevron-left'
36190                                 }
36191                             ]
36192                         },
36193                         {
36194                             tag : 'div',
36195                             cls : 'roo-document-slider-thumb',
36196                             cn : [
36197                                 {
36198                                     tag : 'img',
36199                                     cls : 'roo-document-slider-image'
36200                                 }
36201                             ]
36202                         },
36203                         {
36204                             tag : 'div',
36205                             cls : 'roo-document-slider-next',
36206                             cn : [
36207                                 {
36208                                     tag : 'i',
36209                                     cls : 'fa fa-chevron-right'
36210                                 }
36211                             ]
36212                         }
36213                     ]
36214                 }
36215             ]
36216         };
36217         
36218         return cfg;
36219     },
36220     
36221     initEvents : function()
36222     {
36223         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36224         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36225         
36226         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36227         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36228         
36229         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36230         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36231         
36232         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36233         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36234         
36235         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36236         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36237         
36238         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36239         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36240         
36241         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36242         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36243         
36244         this.thumbEl.on('click', this.onClick, this);
36245         
36246         this.prevIndicator.on('click', this.prev, this);
36247         
36248         this.nextIndicator.on('click', this.next, this);
36249         
36250     },
36251     
36252     initial : function()
36253     {
36254         if(this.files.length){
36255             this.indicator = 1;
36256             this.update()
36257         }
36258         
36259         this.fireEvent('initial', this);
36260     },
36261     
36262     update : function()
36263     {
36264         this.imageEl.attr('src', this.files[this.indicator - 1]);
36265         
36266         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36267         
36268         this.prevIndicator.show();
36269         
36270         if(this.indicator == 1){
36271             this.prevIndicator.hide();
36272         }
36273         
36274         this.nextIndicator.show();
36275         
36276         if(this.indicator == this.files.length){
36277             this.nextIndicator.hide();
36278         }
36279         
36280         this.thumbEl.scrollTo('top');
36281         
36282         this.fireEvent('update', this);
36283     },
36284     
36285     onClick : function(e)
36286     {
36287         e.preventDefault();
36288         
36289         this.fireEvent('click', this);
36290     },
36291     
36292     prev : function(e)
36293     {
36294         e.preventDefault();
36295         
36296         this.indicator = Math.max(1, this.indicator - 1);
36297         
36298         this.update();
36299     },
36300     
36301     next : function(e)
36302     {
36303         e.preventDefault();
36304         
36305         this.indicator = Math.min(this.files.length, this.indicator + 1);
36306         
36307         this.update();
36308     }
36309 });
36310 /*
36311  * - LGPL
36312  *
36313  * RadioSet
36314  *
36315  *
36316  */
36317
36318 /**
36319  * @class Roo.bootstrap.RadioSet
36320  * @extends Roo.bootstrap.Input
36321  * Bootstrap RadioSet class
36322  * @cfg {String} indicatorpos (left|right) default left
36323  * @cfg {Boolean} inline (true|false) inline the element (default true)
36324  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36325  * @constructor
36326  * Create a new RadioSet
36327  * @param {Object} config The config object
36328  */
36329
36330 Roo.bootstrap.RadioSet = function(config){
36331     
36332     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36333     
36334     this.radioes = [];
36335     
36336     Roo.bootstrap.RadioSet.register(this);
36337     
36338     this.addEvents({
36339         /**
36340         * @event check
36341         * Fires when the element is checked or unchecked.
36342         * @param {Roo.bootstrap.RadioSet} this This radio
36343         * @param {Roo.bootstrap.Radio} item The checked item
36344         */
36345        check : true,
36346        /**
36347         * @event click
36348         * Fires when the element is click.
36349         * @param {Roo.bootstrap.RadioSet} this This radio set
36350         * @param {Roo.bootstrap.Radio} item The checked item
36351         * @param {Roo.EventObject} e The event object
36352         */
36353        click : true
36354     });
36355     
36356 };
36357
36358 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36359
36360     radioes : false,
36361     
36362     inline : true,
36363     
36364     weight : '',
36365     
36366     indicatorpos : 'left',
36367     
36368     getAutoCreate : function()
36369     {
36370         var label = {
36371             tag : 'label',
36372             cls : 'roo-radio-set-label',
36373             cn : [
36374                 {
36375                     tag : 'span',
36376                     html : this.fieldLabel
36377                 }
36378             ]
36379         };
36380         if (Roo.bootstrap.version == 3) {
36381             
36382             
36383             if(this.indicatorpos == 'left'){
36384                 label.cn.unshift({
36385                     tag : 'i',
36386                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36387                     tooltip : 'This field is required'
36388                 });
36389             } else {
36390                 label.cn.push({
36391                     tag : 'i',
36392                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36393                     tooltip : 'This field is required'
36394                 });
36395             }
36396         }
36397         var items = {
36398             tag : 'div',
36399             cls : 'roo-radio-set-items'
36400         };
36401         
36402         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36403         
36404         if (align === 'left' && this.fieldLabel.length) {
36405             
36406             items = {
36407                 cls : "roo-radio-set-right", 
36408                 cn: [
36409                     items
36410                 ]
36411             };
36412             
36413             if(this.labelWidth > 12){
36414                 label.style = "width: " + this.labelWidth + 'px';
36415             }
36416             
36417             if(this.labelWidth < 13 && this.labelmd == 0){
36418                 this.labelmd = this.labelWidth;
36419             }
36420             
36421             if(this.labellg > 0){
36422                 label.cls += ' col-lg-' + this.labellg;
36423                 items.cls += ' col-lg-' + (12 - this.labellg);
36424             }
36425             
36426             if(this.labelmd > 0){
36427                 label.cls += ' col-md-' + this.labelmd;
36428                 items.cls += ' col-md-' + (12 - this.labelmd);
36429             }
36430             
36431             if(this.labelsm > 0){
36432                 label.cls += ' col-sm-' + this.labelsm;
36433                 items.cls += ' col-sm-' + (12 - this.labelsm);
36434             }
36435             
36436             if(this.labelxs > 0){
36437                 label.cls += ' col-xs-' + this.labelxs;
36438                 items.cls += ' col-xs-' + (12 - this.labelxs);
36439             }
36440         }
36441         
36442         var cfg = {
36443             tag : 'div',
36444             cls : 'roo-radio-set',
36445             cn : [
36446                 {
36447                     tag : 'input',
36448                     cls : 'roo-radio-set-input',
36449                     type : 'hidden',
36450                     name : this.name,
36451                     value : this.value ? this.value :  ''
36452                 },
36453                 label,
36454                 items
36455             ]
36456         };
36457         
36458         if(this.weight.length){
36459             cfg.cls += ' roo-radio-' + this.weight;
36460         }
36461         
36462         if(this.inline) {
36463             cfg.cls += ' roo-radio-set-inline';
36464         }
36465         
36466         var settings=this;
36467         ['xs','sm','md','lg'].map(function(size){
36468             if (settings[size]) {
36469                 cfg.cls += ' col-' + size + '-' + settings[size];
36470             }
36471         });
36472         
36473         return cfg;
36474         
36475     },
36476
36477     initEvents : function()
36478     {
36479         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36480         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36481         
36482         if(!this.fieldLabel.length){
36483             this.labelEl.hide();
36484         }
36485         
36486         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36487         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36488         
36489         this.indicator = this.indicatorEl();
36490         
36491         if(this.indicator){
36492             this.indicator.addClass('invisible');
36493         }
36494         
36495         this.originalValue = this.getValue();
36496         
36497     },
36498     
36499     inputEl: function ()
36500     {
36501         return this.el.select('.roo-radio-set-input', true).first();
36502     },
36503     
36504     getChildContainer : function()
36505     {
36506         return this.itemsEl;
36507     },
36508     
36509     register : function(item)
36510     {
36511         this.radioes.push(item);
36512         
36513     },
36514     
36515     validate : function()
36516     {   
36517         if(this.getVisibilityEl().hasClass('hidden')){
36518             return true;
36519         }
36520         
36521         var valid = false;
36522         
36523         Roo.each(this.radioes, function(i){
36524             if(!i.checked){
36525                 return;
36526             }
36527             
36528             valid = true;
36529             return false;
36530         });
36531         
36532         if(this.allowBlank) {
36533             return true;
36534         }
36535         
36536         if(this.disabled || valid){
36537             this.markValid();
36538             return true;
36539         }
36540         
36541         this.markInvalid();
36542         return false;
36543         
36544     },
36545     
36546     markValid : function()
36547     {
36548         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36549             this.indicatorEl().removeClass('visible');
36550             this.indicatorEl().addClass('invisible');
36551         }
36552         
36553         
36554         if (Roo.bootstrap.version == 3) {
36555             this.el.removeClass([this.invalidClass, this.validClass]);
36556             this.el.addClass(this.validClass);
36557         } else {
36558             this.el.removeClass(['is-invalid','is-valid']);
36559             this.el.addClass(['is-valid']);
36560         }
36561         this.fireEvent('valid', this);
36562     },
36563     
36564     markInvalid : function(msg)
36565     {
36566         if(this.allowBlank || this.disabled){
36567             return;
36568         }
36569         
36570         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36571             this.indicatorEl().removeClass('invisible');
36572             this.indicatorEl().addClass('visible');
36573         }
36574         if (Roo.bootstrap.version == 3) {
36575             this.el.removeClass([this.invalidClass, this.validClass]);
36576             this.el.addClass(this.invalidClass);
36577         } else {
36578             this.el.removeClass(['is-invalid','is-valid']);
36579             this.el.addClass(['is-invalid']);
36580         }
36581         
36582         this.fireEvent('invalid', this, msg);
36583         
36584     },
36585     
36586     setValue : function(v, suppressEvent)
36587     {   
36588         if(this.value === v){
36589             return;
36590         }
36591         
36592         this.value = v;
36593         
36594         if(this.rendered){
36595             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36596         }
36597         
36598         Roo.each(this.radioes, function(i){
36599             i.checked = false;
36600             i.el.removeClass('checked');
36601         });
36602         
36603         Roo.each(this.radioes, function(i){
36604             
36605             if(i.value === v || i.value.toString() === v.toString()){
36606                 i.checked = true;
36607                 i.el.addClass('checked');
36608                 
36609                 if(suppressEvent !== true){
36610                     this.fireEvent('check', this, i);
36611                 }
36612                 
36613                 return false;
36614             }
36615             
36616         }, this);
36617         
36618         this.validate();
36619     },
36620     
36621     clearInvalid : function(){
36622         
36623         if(!this.el || this.preventMark){
36624             return;
36625         }
36626         
36627         this.el.removeClass([this.invalidClass]);
36628         
36629         this.fireEvent('valid', this);
36630     }
36631     
36632 });
36633
36634 Roo.apply(Roo.bootstrap.RadioSet, {
36635     
36636     groups: {},
36637     
36638     register : function(set)
36639     {
36640         this.groups[set.name] = set;
36641     },
36642     
36643     get: function(name) 
36644     {
36645         if (typeof(this.groups[name]) == 'undefined') {
36646             return false;
36647         }
36648         
36649         return this.groups[name] ;
36650     }
36651     
36652 });
36653 /*
36654  * Based on:
36655  * Ext JS Library 1.1.1
36656  * Copyright(c) 2006-2007, Ext JS, LLC.
36657  *
36658  * Originally Released Under LGPL - original licence link has changed is not relivant.
36659  *
36660  * Fork - LGPL
36661  * <script type="text/javascript">
36662  */
36663
36664
36665 /**
36666  * @class Roo.bootstrap.SplitBar
36667  * @extends Roo.util.Observable
36668  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36669  * <br><br>
36670  * Usage:
36671  * <pre><code>
36672 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36673                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36674 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36675 split.minSize = 100;
36676 split.maxSize = 600;
36677 split.animate = true;
36678 split.on('moved', splitterMoved);
36679 </code></pre>
36680  * @constructor
36681  * Create a new SplitBar
36682  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36683  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36684  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36685  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36686                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36687                         position of the SplitBar).
36688  */
36689 Roo.bootstrap.SplitBar = function(cfg){
36690     
36691     /** @private */
36692     
36693     //{
36694     //  dragElement : elm
36695     //  resizingElement: el,
36696         // optional..
36697     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36698     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36699         // existingProxy ???
36700     //}
36701     
36702     this.el = Roo.get(cfg.dragElement, true);
36703     this.el.dom.unselectable = "on";
36704     /** @private */
36705     this.resizingEl = Roo.get(cfg.resizingElement, true);
36706
36707     /**
36708      * @private
36709      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36710      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36711      * @type Number
36712      */
36713     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36714     
36715     /**
36716      * The minimum size of the resizing element. (Defaults to 0)
36717      * @type Number
36718      */
36719     this.minSize = 0;
36720     
36721     /**
36722      * The maximum size of the resizing element. (Defaults to 2000)
36723      * @type Number
36724      */
36725     this.maxSize = 2000;
36726     
36727     /**
36728      * Whether to animate the transition to the new size
36729      * @type Boolean
36730      */
36731     this.animate = false;
36732     
36733     /**
36734      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36735      * @type Boolean
36736      */
36737     this.useShim = false;
36738     
36739     /** @private */
36740     this.shim = null;
36741     
36742     if(!cfg.existingProxy){
36743         /** @private */
36744         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36745     }else{
36746         this.proxy = Roo.get(cfg.existingProxy).dom;
36747     }
36748     /** @private */
36749     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36750     
36751     /** @private */
36752     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36753     
36754     /** @private */
36755     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36756     
36757     /** @private */
36758     this.dragSpecs = {};
36759     
36760     /**
36761      * @private The adapter to use to positon and resize elements
36762      */
36763     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36764     this.adapter.init(this);
36765     
36766     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36767         /** @private */
36768         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36769         this.el.addClass("roo-splitbar-h");
36770     }else{
36771         /** @private */
36772         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36773         this.el.addClass("roo-splitbar-v");
36774     }
36775     
36776     this.addEvents({
36777         /**
36778          * @event resize
36779          * Fires when the splitter is moved (alias for {@link #event-moved})
36780          * @param {Roo.bootstrap.SplitBar} this
36781          * @param {Number} newSize the new width or height
36782          */
36783         "resize" : true,
36784         /**
36785          * @event moved
36786          * Fires when the splitter is moved
36787          * @param {Roo.bootstrap.SplitBar} this
36788          * @param {Number} newSize the new width or height
36789          */
36790         "moved" : true,
36791         /**
36792          * @event beforeresize
36793          * Fires before the splitter is dragged
36794          * @param {Roo.bootstrap.SplitBar} this
36795          */
36796         "beforeresize" : true,
36797
36798         "beforeapply" : true
36799     });
36800
36801     Roo.util.Observable.call(this);
36802 };
36803
36804 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36805     onStartProxyDrag : function(x, y){
36806         this.fireEvent("beforeresize", this);
36807         if(!this.overlay){
36808             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36809             o.unselectable();
36810             o.enableDisplayMode("block");
36811             // all splitbars share the same overlay
36812             Roo.bootstrap.SplitBar.prototype.overlay = o;
36813         }
36814         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36815         this.overlay.show();
36816         Roo.get(this.proxy).setDisplayed("block");
36817         var size = this.adapter.getElementSize(this);
36818         this.activeMinSize = this.getMinimumSize();;
36819         this.activeMaxSize = this.getMaximumSize();;
36820         var c1 = size - this.activeMinSize;
36821         var c2 = Math.max(this.activeMaxSize - size, 0);
36822         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36823             this.dd.resetConstraints();
36824             this.dd.setXConstraint(
36825                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36826                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36827             );
36828             this.dd.setYConstraint(0, 0);
36829         }else{
36830             this.dd.resetConstraints();
36831             this.dd.setXConstraint(0, 0);
36832             this.dd.setYConstraint(
36833                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36834                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36835             );
36836          }
36837         this.dragSpecs.startSize = size;
36838         this.dragSpecs.startPoint = [x, y];
36839         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36840     },
36841     
36842     /** 
36843      * @private Called after the drag operation by the DDProxy
36844      */
36845     onEndProxyDrag : function(e){
36846         Roo.get(this.proxy).setDisplayed(false);
36847         var endPoint = Roo.lib.Event.getXY(e);
36848         if(this.overlay){
36849             this.overlay.hide();
36850         }
36851         var newSize;
36852         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36853             newSize = this.dragSpecs.startSize + 
36854                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36855                     endPoint[0] - this.dragSpecs.startPoint[0] :
36856                     this.dragSpecs.startPoint[0] - endPoint[0]
36857                 );
36858         }else{
36859             newSize = this.dragSpecs.startSize + 
36860                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36861                     endPoint[1] - this.dragSpecs.startPoint[1] :
36862                     this.dragSpecs.startPoint[1] - endPoint[1]
36863                 );
36864         }
36865         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36866         if(newSize != this.dragSpecs.startSize){
36867             if(this.fireEvent('beforeapply', this, newSize) !== false){
36868                 this.adapter.setElementSize(this, newSize);
36869                 this.fireEvent("moved", this, newSize);
36870                 this.fireEvent("resize", this, newSize);
36871             }
36872         }
36873     },
36874     
36875     /**
36876      * Get the adapter this SplitBar uses
36877      * @return The adapter object
36878      */
36879     getAdapter : function(){
36880         return this.adapter;
36881     },
36882     
36883     /**
36884      * Set the adapter this SplitBar uses
36885      * @param {Object} adapter A SplitBar adapter object
36886      */
36887     setAdapter : function(adapter){
36888         this.adapter = adapter;
36889         this.adapter.init(this);
36890     },
36891     
36892     /**
36893      * Gets the minimum size for the resizing element
36894      * @return {Number} The minimum size
36895      */
36896     getMinimumSize : function(){
36897         return this.minSize;
36898     },
36899     
36900     /**
36901      * Sets the minimum size for the resizing element
36902      * @param {Number} minSize The minimum size
36903      */
36904     setMinimumSize : function(minSize){
36905         this.minSize = minSize;
36906     },
36907     
36908     /**
36909      * Gets the maximum size for the resizing element
36910      * @return {Number} The maximum size
36911      */
36912     getMaximumSize : function(){
36913         return this.maxSize;
36914     },
36915     
36916     /**
36917      * Sets the maximum size for the resizing element
36918      * @param {Number} maxSize The maximum size
36919      */
36920     setMaximumSize : function(maxSize){
36921         this.maxSize = maxSize;
36922     },
36923     
36924     /**
36925      * Sets the initialize size for the resizing element
36926      * @param {Number} size The initial size
36927      */
36928     setCurrentSize : function(size){
36929         var oldAnimate = this.animate;
36930         this.animate = false;
36931         this.adapter.setElementSize(this, size);
36932         this.animate = oldAnimate;
36933     },
36934     
36935     /**
36936      * Destroy this splitbar. 
36937      * @param {Boolean} removeEl True to remove the element
36938      */
36939     destroy : function(removeEl){
36940         if(this.shim){
36941             this.shim.remove();
36942         }
36943         this.dd.unreg();
36944         this.proxy.parentNode.removeChild(this.proxy);
36945         if(removeEl){
36946             this.el.remove();
36947         }
36948     }
36949 });
36950
36951 /**
36952  * @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.
36953  */
36954 Roo.bootstrap.SplitBar.createProxy = function(dir){
36955     var proxy = new Roo.Element(document.createElement("div"));
36956     proxy.unselectable();
36957     var cls = 'roo-splitbar-proxy';
36958     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36959     document.body.appendChild(proxy.dom);
36960     return proxy.dom;
36961 };
36962
36963 /** 
36964  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36965  * Default Adapter. It assumes the splitter and resizing element are not positioned
36966  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36967  */
36968 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36969 };
36970
36971 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36972     // do nothing for now
36973     init : function(s){
36974     
36975     },
36976     /**
36977      * Called before drag operations to get the current size of the resizing element. 
36978      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36979      */
36980      getElementSize : function(s){
36981         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36982             return s.resizingEl.getWidth();
36983         }else{
36984             return s.resizingEl.getHeight();
36985         }
36986     },
36987     
36988     /**
36989      * Called after drag operations to set the size of the resizing element.
36990      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36991      * @param {Number} newSize The new size to set
36992      * @param {Function} onComplete A function to be invoked when resizing is complete
36993      */
36994     setElementSize : function(s, newSize, onComplete){
36995         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36996             if(!s.animate){
36997                 s.resizingEl.setWidth(newSize);
36998                 if(onComplete){
36999                     onComplete(s, newSize);
37000                 }
37001             }else{
37002                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37003             }
37004         }else{
37005             
37006             if(!s.animate){
37007                 s.resizingEl.setHeight(newSize);
37008                 if(onComplete){
37009                     onComplete(s, newSize);
37010                 }
37011             }else{
37012                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37013             }
37014         }
37015     }
37016 };
37017
37018 /** 
37019  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37020  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37021  * Adapter that  moves the splitter element to align with the resized sizing element. 
37022  * Used with an absolute positioned SplitBar.
37023  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37024  * document.body, make sure you assign an id to the body element.
37025  */
37026 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37027     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37028     this.container = Roo.get(container);
37029 };
37030
37031 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37032     init : function(s){
37033         this.basic.init(s);
37034     },
37035     
37036     getElementSize : function(s){
37037         return this.basic.getElementSize(s);
37038     },
37039     
37040     setElementSize : function(s, newSize, onComplete){
37041         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37042     },
37043     
37044     moveSplitter : function(s){
37045         var yes = Roo.bootstrap.SplitBar;
37046         switch(s.placement){
37047             case yes.LEFT:
37048                 s.el.setX(s.resizingEl.getRight());
37049                 break;
37050             case yes.RIGHT:
37051                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37052                 break;
37053             case yes.TOP:
37054                 s.el.setY(s.resizingEl.getBottom());
37055                 break;
37056             case yes.BOTTOM:
37057                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37058                 break;
37059         }
37060     }
37061 };
37062
37063 /**
37064  * Orientation constant - Create a vertical SplitBar
37065  * @static
37066  * @type Number
37067  */
37068 Roo.bootstrap.SplitBar.VERTICAL = 1;
37069
37070 /**
37071  * Orientation constant - Create a horizontal SplitBar
37072  * @static
37073  * @type Number
37074  */
37075 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37076
37077 /**
37078  * Placement constant - The resizing element is to the left of the splitter element
37079  * @static
37080  * @type Number
37081  */
37082 Roo.bootstrap.SplitBar.LEFT = 1;
37083
37084 /**
37085  * Placement constant - The resizing element is to the right of the splitter element
37086  * @static
37087  * @type Number
37088  */
37089 Roo.bootstrap.SplitBar.RIGHT = 2;
37090
37091 /**
37092  * Placement constant - The resizing element is positioned above the splitter element
37093  * @static
37094  * @type Number
37095  */
37096 Roo.bootstrap.SplitBar.TOP = 3;
37097
37098 /**
37099  * Placement constant - The resizing element is positioned under splitter element
37100  * @static
37101  * @type Number
37102  */
37103 Roo.bootstrap.SplitBar.BOTTOM = 4;
37104 Roo.namespace("Roo.bootstrap.layout");/*
37105  * Based on:
37106  * Ext JS Library 1.1.1
37107  * Copyright(c) 2006-2007, Ext JS, LLC.
37108  *
37109  * Originally Released Under LGPL - original licence link has changed is not relivant.
37110  *
37111  * Fork - LGPL
37112  * <script type="text/javascript">
37113  */
37114
37115 /**
37116  * @class Roo.bootstrap.layout.Manager
37117  * @extends Roo.bootstrap.Component
37118  * Base class for layout managers.
37119  */
37120 Roo.bootstrap.layout.Manager = function(config)
37121 {
37122     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37123
37124
37125
37126
37127
37128     /** false to disable window resize monitoring @type Boolean */
37129     this.monitorWindowResize = true;
37130     this.regions = {};
37131     this.addEvents({
37132         /**
37133          * @event layout
37134          * Fires when a layout is performed.
37135          * @param {Roo.LayoutManager} this
37136          */
37137         "layout" : true,
37138         /**
37139          * @event regionresized
37140          * Fires when the user resizes a region.
37141          * @param {Roo.LayoutRegion} region The resized region
37142          * @param {Number} newSize The new size (width for east/west, height for north/south)
37143          */
37144         "regionresized" : true,
37145         /**
37146          * @event regioncollapsed
37147          * Fires when a region is collapsed.
37148          * @param {Roo.LayoutRegion} region The collapsed region
37149          */
37150         "regioncollapsed" : true,
37151         /**
37152          * @event regionexpanded
37153          * Fires when a region is expanded.
37154          * @param {Roo.LayoutRegion} region The expanded region
37155          */
37156         "regionexpanded" : true
37157     });
37158     this.updating = false;
37159
37160     if (config.el) {
37161         this.el = Roo.get(config.el);
37162         this.initEvents();
37163     }
37164
37165 };
37166
37167 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37168
37169
37170     regions : null,
37171
37172     monitorWindowResize : true,
37173
37174
37175     updating : false,
37176
37177
37178     onRender : function(ct, position)
37179     {
37180         if(!this.el){
37181             this.el = Roo.get(ct);
37182             this.initEvents();
37183         }
37184         //this.fireEvent('render',this);
37185     },
37186
37187
37188     initEvents: function()
37189     {
37190
37191
37192         // ie scrollbar fix
37193         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37194             document.body.scroll = "no";
37195         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37196             this.el.position('relative');
37197         }
37198         this.id = this.el.id;
37199         this.el.addClass("roo-layout-container");
37200         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37201         if(this.el.dom != document.body ) {
37202             this.el.on('resize', this.layout,this);
37203             this.el.on('show', this.layout,this);
37204         }
37205
37206     },
37207
37208     /**
37209      * Returns true if this layout is currently being updated
37210      * @return {Boolean}
37211      */
37212     isUpdating : function(){
37213         return this.updating;
37214     },
37215
37216     /**
37217      * Suspend the LayoutManager from doing auto-layouts while
37218      * making multiple add or remove calls
37219      */
37220     beginUpdate : function(){
37221         this.updating = true;
37222     },
37223
37224     /**
37225      * Restore auto-layouts and optionally disable the manager from performing a layout
37226      * @param {Boolean} noLayout true to disable a layout update
37227      */
37228     endUpdate : function(noLayout){
37229         this.updating = false;
37230         if(!noLayout){
37231             this.layout();
37232         }
37233     },
37234
37235     layout: function(){
37236         // abstract...
37237     },
37238
37239     onRegionResized : function(region, newSize){
37240         this.fireEvent("regionresized", region, newSize);
37241         this.layout();
37242     },
37243
37244     onRegionCollapsed : function(region){
37245         this.fireEvent("regioncollapsed", region);
37246     },
37247
37248     onRegionExpanded : function(region){
37249         this.fireEvent("regionexpanded", region);
37250     },
37251
37252     /**
37253      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37254      * performs box-model adjustments.
37255      * @return {Object} The size as an object {width: (the width), height: (the height)}
37256      */
37257     getViewSize : function()
37258     {
37259         var size;
37260         if(this.el.dom != document.body){
37261             size = this.el.getSize();
37262         }else{
37263             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37264         }
37265         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37266         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37267         return size;
37268     },
37269
37270     /**
37271      * Returns the Element this layout is bound to.
37272      * @return {Roo.Element}
37273      */
37274     getEl : function(){
37275         return this.el;
37276     },
37277
37278     /**
37279      * Returns the specified region.
37280      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37281      * @return {Roo.LayoutRegion}
37282      */
37283     getRegion : function(target){
37284         return this.regions[target.toLowerCase()];
37285     },
37286
37287     onWindowResize : function(){
37288         if(this.monitorWindowResize){
37289             this.layout();
37290         }
37291     }
37292 });
37293 /*
37294  * Based on:
37295  * Ext JS Library 1.1.1
37296  * Copyright(c) 2006-2007, Ext JS, LLC.
37297  *
37298  * Originally Released Under LGPL - original licence link has changed is not relivant.
37299  *
37300  * Fork - LGPL
37301  * <script type="text/javascript">
37302  */
37303 /**
37304  * @class Roo.bootstrap.layout.Border
37305  * @extends Roo.bootstrap.layout.Manager
37306  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37307  * please see: examples/bootstrap/nested.html<br><br>
37308  
37309 <b>The container the layout is rendered into can be either the body element or any other element.
37310 If it is not the body element, the container needs to either be an absolute positioned element,
37311 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37312 the container size if it is not the body element.</b>
37313
37314 * @constructor
37315 * Create a new Border
37316 * @param {Object} config Configuration options
37317  */
37318 Roo.bootstrap.layout.Border = function(config){
37319     config = config || {};
37320     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37321     
37322     
37323     
37324     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37325         if(config[region]){
37326             config[region].region = region;
37327             this.addRegion(config[region]);
37328         }
37329     },this);
37330     
37331 };
37332
37333 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37334
37335 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37336     
37337     parent : false, // this might point to a 'nest' or a ???
37338     
37339     /**
37340      * Creates and adds a new region if it doesn't already exist.
37341      * @param {String} target The target region key (north, south, east, west or center).
37342      * @param {Object} config The regions config object
37343      * @return {BorderLayoutRegion} The new region
37344      */
37345     addRegion : function(config)
37346     {
37347         if(!this.regions[config.region]){
37348             var r = this.factory(config);
37349             this.bindRegion(r);
37350         }
37351         return this.regions[config.region];
37352     },
37353
37354     // private (kinda)
37355     bindRegion : function(r){
37356         this.regions[r.config.region] = r;
37357         
37358         r.on("visibilitychange",    this.layout, this);
37359         r.on("paneladded",          this.layout, this);
37360         r.on("panelremoved",        this.layout, this);
37361         r.on("invalidated",         this.layout, this);
37362         r.on("resized",             this.onRegionResized, this);
37363         r.on("collapsed",           this.onRegionCollapsed, this);
37364         r.on("expanded",            this.onRegionExpanded, this);
37365     },
37366
37367     /**
37368      * Performs a layout update.
37369      */
37370     layout : function()
37371     {
37372         if(this.updating) {
37373             return;
37374         }
37375         
37376         // render all the rebions if they have not been done alreayd?
37377         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37378             if(this.regions[region] && !this.regions[region].bodyEl){
37379                 this.regions[region].onRender(this.el)
37380             }
37381         },this);
37382         
37383         var size = this.getViewSize();
37384         var w = size.width;
37385         var h = size.height;
37386         var centerW = w;
37387         var centerH = h;
37388         var centerY = 0;
37389         var centerX = 0;
37390         //var x = 0, y = 0;
37391
37392         var rs = this.regions;
37393         var north = rs["north"];
37394         var south = rs["south"]; 
37395         var west = rs["west"];
37396         var east = rs["east"];
37397         var center = rs["center"];
37398         //if(this.hideOnLayout){ // not supported anymore
37399             //c.el.setStyle("display", "none");
37400         //}
37401         if(north && north.isVisible()){
37402             var b = north.getBox();
37403             var m = north.getMargins();
37404             b.width = w - (m.left+m.right);
37405             b.x = m.left;
37406             b.y = m.top;
37407             centerY = b.height + b.y + m.bottom;
37408             centerH -= centerY;
37409             north.updateBox(this.safeBox(b));
37410         }
37411         if(south && south.isVisible()){
37412             var b = south.getBox();
37413             var m = south.getMargins();
37414             b.width = w - (m.left+m.right);
37415             b.x = m.left;
37416             var totalHeight = (b.height + m.top + m.bottom);
37417             b.y = h - totalHeight + m.top;
37418             centerH -= totalHeight;
37419             south.updateBox(this.safeBox(b));
37420         }
37421         if(west && west.isVisible()){
37422             var b = west.getBox();
37423             var m = west.getMargins();
37424             b.height = centerH - (m.top+m.bottom);
37425             b.x = m.left;
37426             b.y = centerY + m.top;
37427             var totalWidth = (b.width + m.left + m.right);
37428             centerX += totalWidth;
37429             centerW -= totalWidth;
37430             west.updateBox(this.safeBox(b));
37431         }
37432         if(east && east.isVisible()){
37433             var b = east.getBox();
37434             var m = east.getMargins();
37435             b.height = centerH - (m.top+m.bottom);
37436             var totalWidth = (b.width + m.left + m.right);
37437             b.x = w - totalWidth + m.left;
37438             b.y = centerY + m.top;
37439             centerW -= totalWidth;
37440             east.updateBox(this.safeBox(b));
37441         }
37442         if(center){
37443             var m = center.getMargins();
37444             var centerBox = {
37445                 x: centerX + m.left,
37446                 y: centerY + m.top,
37447                 width: centerW - (m.left+m.right),
37448                 height: centerH - (m.top+m.bottom)
37449             };
37450             //if(this.hideOnLayout){
37451                 //center.el.setStyle("display", "block");
37452             //}
37453             center.updateBox(this.safeBox(centerBox));
37454         }
37455         this.el.repaint();
37456         this.fireEvent("layout", this);
37457     },
37458
37459     // private
37460     safeBox : function(box){
37461         box.width = Math.max(0, box.width);
37462         box.height = Math.max(0, box.height);
37463         return box;
37464     },
37465
37466     /**
37467      * Adds a ContentPanel (or subclass) to this layout.
37468      * @param {String} target The target region key (north, south, east, west or center).
37469      * @param {Roo.ContentPanel} panel The panel to add
37470      * @return {Roo.ContentPanel} The added panel
37471      */
37472     add : function(target, panel){
37473          
37474         target = target.toLowerCase();
37475         return this.regions[target].add(panel);
37476     },
37477
37478     /**
37479      * Remove a ContentPanel (or subclass) to this layout.
37480      * @param {String} target The target region key (north, south, east, west or center).
37481      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37482      * @return {Roo.ContentPanel} The removed panel
37483      */
37484     remove : function(target, panel){
37485         target = target.toLowerCase();
37486         return this.regions[target].remove(panel);
37487     },
37488
37489     /**
37490      * Searches all regions for a panel with the specified id
37491      * @param {String} panelId
37492      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37493      */
37494     findPanel : function(panelId){
37495         var rs = this.regions;
37496         for(var target in rs){
37497             if(typeof rs[target] != "function"){
37498                 var p = rs[target].getPanel(panelId);
37499                 if(p){
37500                     return p;
37501                 }
37502             }
37503         }
37504         return null;
37505     },
37506
37507     /**
37508      * Searches all regions for a panel with the specified id and activates (shows) it.
37509      * @param {String/ContentPanel} panelId The panels id or the panel itself
37510      * @return {Roo.ContentPanel} The shown panel or null
37511      */
37512     showPanel : function(panelId) {
37513       var rs = this.regions;
37514       for(var target in rs){
37515          var r = rs[target];
37516          if(typeof r != "function"){
37517             if(r.hasPanel(panelId)){
37518                return r.showPanel(panelId);
37519             }
37520          }
37521       }
37522       return null;
37523    },
37524
37525    /**
37526      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37527      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37528      */
37529    /*
37530     restoreState : function(provider){
37531         if(!provider){
37532             provider = Roo.state.Manager;
37533         }
37534         var sm = new Roo.LayoutStateManager();
37535         sm.init(this, provider);
37536     },
37537 */
37538  
37539  
37540     /**
37541      * Adds a xtype elements to the layout.
37542      * <pre><code>
37543
37544 layout.addxtype({
37545        xtype : 'ContentPanel',
37546        region: 'west',
37547        items: [ .... ]
37548    }
37549 );
37550
37551 layout.addxtype({
37552         xtype : 'NestedLayoutPanel',
37553         region: 'west',
37554         layout: {
37555            center: { },
37556            west: { }   
37557         },
37558         items : [ ... list of content panels or nested layout panels.. ]
37559    }
37560 );
37561 </code></pre>
37562      * @param {Object} cfg Xtype definition of item to add.
37563      */
37564     addxtype : function(cfg)
37565     {
37566         // basically accepts a pannel...
37567         // can accept a layout region..!?!?
37568         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37569         
37570         
37571         // theory?  children can only be panels??
37572         
37573         //if (!cfg.xtype.match(/Panel$/)) {
37574         //    return false;
37575         //}
37576         var ret = false;
37577         
37578         if (typeof(cfg.region) == 'undefined') {
37579             Roo.log("Failed to add Panel, region was not set");
37580             Roo.log(cfg);
37581             return false;
37582         }
37583         var region = cfg.region;
37584         delete cfg.region;
37585         
37586           
37587         var xitems = [];
37588         if (cfg.items) {
37589             xitems = cfg.items;
37590             delete cfg.items;
37591         }
37592         var nb = false;
37593         
37594         if ( region == 'center') {
37595             Roo.log("Center: " + cfg.title);
37596         }
37597         
37598         
37599         switch(cfg.xtype) 
37600         {
37601             case 'Content':  // ContentPanel (el, cfg)
37602             case 'Scroll':  // ContentPanel (el, cfg)
37603             case 'View': 
37604                 cfg.autoCreate = cfg.autoCreate || true;
37605                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37606                 //} else {
37607                 //    var el = this.el.createChild();
37608                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37609                 //}
37610                 
37611                 this.add(region, ret);
37612                 break;
37613             
37614             /*
37615             case 'TreePanel': // our new panel!
37616                 cfg.el = this.el.createChild();
37617                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37618                 this.add(region, ret);
37619                 break;
37620             */
37621             
37622             case 'Nest': 
37623                 // create a new Layout (which is  a Border Layout...
37624                 
37625                 var clayout = cfg.layout;
37626                 clayout.el  = this.el.createChild();
37627                 clayout.items   = clayout.items  || [];
37628                 
37629                 delete cfg.layout;
37630                 
37631                 // replace this exitems with the clayout ones..
37632                 xitems = clayout.items;
37633                  
37634                 // force background off if it's in center...
37635                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37636                     cfg.background = false;
37637                 }
37638                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37639                 
37640                 
37641                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37642                 //console.log('adding nested layout panel '  + cfg.toSource());
37643                 this.add(region, ret);
37644                 nb = {}; /// find first...
37645                 break;
37646             
37647             case 'Grid':
37648                 
37649                 // needs grid and region
37650                 
37651                 //var el = this.getRegion(region).el.createChild();
37652                 /*
37653                  *var el = this.el.createChild();
37654                 // create the grid first...
37655                 cfg.grid.container = el;
37656                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37657                 */
37658                 
37659                 if (region == 'center' && this.active ) {
37660                     cfg.background = false;
37661                 }
37662                 
37663                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37664                 
37665                 this.add(region, ret);
37666                 /*
37667                 if (cfg.background) {
37668                     // render grid on panel activation (if panel background)
37669                     ret.on('activate', function(gp) {
37670                         if (!gp.grid.rendered) {
37671                     //        gp.grid.render(el);
37672                         }
37673                     });
37674                 } else {
37675                   //  cfg.grid.render(el);
37676                 }
37677                 */
37678                 break;
37679            
37680            
37681             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37682                 // it was the old xcomponent building that caused this before.
37683                 // espeically if border is the top element in the tree.
37684                 ret = this;
37685                 break; 
37686                 
37687                     
37688                 
37689                 
37690                 
37691             default:
37692                 /*
37693                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37694                     
37695                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37696                     this.add(region, ret);
37697                 } else {
37698                 */
37699                     Roo.log(cfg);
37700                     throw "Can not add '" + cfg.xtype + "' to Border";
37701                     return null;
37702              
37703                                 
37704              
37705         }
37706         this.beginUpdate();
37707         // add children..
37708         var region = '';
37709         var abn = {};
37710         Roo.each(xitems, function(i)  {
37711             region = nb && i.region ? i.region : false;
37712             
37713             var add = ret.addxtype(i);
37714            
37715             if (region) {
37716                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37717                 if (!i.background) {
37718                     abn[region] = nb[region] ;
37719                 }
37720             }
37721             
37722         });
37723         this.endUpdate();
37724
37725         // make the last non-background panel active..
37726         //if (nb) { Roo.log(abn); }
37727         if (nb) {
37728             
37729             for(var r in abn) {
37730                 region = this.getRegion(r);
37731                 if (region) {
37732                     // tried using nb[r], but it does not work..
37733                      
37734                     region.showPanel(abn[r]);
37735                    
37736                 }
37737             }
37738         }
37739         return ret;
37740         
37741     },
37742     
37743     
37744 // private
37745     factory : function(cfg)
37746     {
37747         
37748         var validRegions = Roo.bootstrap.layout.Border.regions;
37749
37750         var target = cfg.region;
37751         cfg.mgr = this;
37752         
37753         var r = Roo.bootstrap.layout;
37754         Roo.log(target);
37755         switch(target){
37756             case "north":
37757                 return new r.North(cfg);
37758             case "south":
37759                 return new r.South(cfg);
37760             case "east":
37761                 return new r.East(cfg);
37762             case "west":
37763                 return new r.West(cfg);
37764             case "center":
37765                 return new r.Center(cfg);
37766         }
37767         throw 'Layout region "'+target+'" not supported.';
37768     }
37769     
37770     
37771 });
37772  /*
37773  * Based on:
37774  * Ext JS Library 1.1.1
37775  * Copyright(c) 2006-2007, Ext JS, LLC.
37776  *
37777  * Originally Released Under LGPL - original licence link has changed is not relivant.
37778  *
37779  * Fork - LGPL
37780  * <script type="text/javascript">
37781  */
37782  
37783 /**
37784  * @class Roo.bootstrap.layout.Basic
37785  * @extends Roo.util.Observable
37786  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37787  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37788  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37789  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37790  * @cfg {string}   region  the region that it inhabits..
37791  * @cfg {bool}   skipConfig skip config?
37792  * 
37793
37794  */
37795 Roo.bootstrap.layout.Basic = function(config){
37796     
37797     this.mgr = config.mgr;
37798     
37799     this.position = config.region;
37800     
37801     var skipConfig = config.skipConfig;
37802     
37803     this.events = {
37804         /**
37805          * @scope Roo.BasicLayoutRegion
37806          */
37807         
37808         /**
37809          * @event beforeremove
37810          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37811          * @param {Roo.LayoutRegion} this
37812          * @param {Roo.ContentPanel} panel The panel
37813          * @param {Object} e The cancel event object
37814          */
37815         "beforeremove" : true,
37816         /**
37817          * @event invalidated
37818          * Fires when the layout for this region is changed.
37819          * @param {Roo.LayoutRegion} this
37820          */
37821         "invalidated" : true,
37822         /**
37823          * @event visibilitychange
37824          * Fires when this region is shown or hidden 
37825          * @param {Roo.LayoutRegion} this
37826          * @param {Boolean} visibility true or false
37827          */
37828         "visibilitychange" : true,
37829         /**
37830          * @event paneladded
37831          * Fires when a panel is added. 
37832          * @param {Roo.LayoutRegion} this
37833          * @param {Roo.ContentPanel} panel The panel
37834          */
37835         "paneladded" : true,
37836         /**
37837          * @event panelremoved
37838          * Fires when a panel is removed. 
37839          * @param {Roo.LayoutRegion} this
37840          * @param {Roo.ContentPanel} panel The panel
37841          */
37842         "panelremoved" : true,
37843         /**
37844          * @event beforecollapse
37845          * Fires when this region before collapse.
37846          * @param {Roo.LayoutRegion} this
37847          */
37848         "beforecollapse" : true,
37849         /**
37850          * @event collapsed
37851          * Fires when this region is collapsed.
37852          * @param {Roo.LayoutRegion} this
37853          */
37854         "collapsed" : true,
37855         /**
37856          * @event expanded
37857          * Fires when this region is expanded.
37858          * @param {Roo.LayoutRegion} this
37859          */
37860         "expanded" : true,
37861         /**
37862          * @event slideshow
37863          * Fires when this region is slid into view.
37864          * @param {Roo.LayoutRegion} this
37865          */
37866         "slideshow" : true,
37867         /**
37868          * @event slidehide
37869          * Fires when this region slides out of view. 
37870          * @param {Roo.LayoutRegion} this
37871          */
37872         "slidehide" : true,
37873         /**
37874          * @event panelactivated
37875          * Fires when a panel is activated. 
37876          * @param {Roo.LayoutRegion} this
37877          * @param {Roo.ContentPanel} panel The activated panel
37878          */
37879         "panelactivated" : true,
37880         /**
37881          * @event resized
37882          * Fires when the user resizes this region. 
37883          * @param {Roo.LayoutRegion} this
37884          * @param {Number} newSize The new size (width for east/west, height for north/south)
37885          */
37886         "resized" : true
37887     };
37888     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37889     this.panels = new Roo.util.MixedCollection();
37890     this.panels.getKey = this.getPanelId.createDelegate(this);
37891     this.box = null;
37892     this.activePanel = null;
37893     // ensure listeners are added...
37894     
37895     if (config.listeners || config.events) {
37896         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37897             listeners : config.listeners || {},
37898             events : config.events || {}
37899         });
37900     }
37901     
37902     if(skipConfig !== true){
37903         this.applyConfig(config);
37904     }
37905 };
37906
37907 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37908 {
37909     getPanelId : function(p){
37910         return p.getId();
37911     },
37912     
37913     applyConfig : function(config){
37914         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37915         this.config = config;
37916         
37917     },
37918     
37919     /**
37920      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37921      * the width, for horizontal (north, south) the height.
37922      * @param {Number} newSize The new width or height
37923      */
37924     resizeTo : function(newSize){
37925         var el = this.el ? this.el :
37926                  (this.activePanel ? this.activePanel.getEl() : null);
37927         if(el){
37928             switch(this.position){
37929                 case "east":
37930                 case "west":
37931                     el.setWidth(newSize);
37932                     this.fireEvent("resized", this, newSize);
37933                 break;
37934                 case "north":
37935                 case "south":
37936                     el.setHeight(newSize);
37937                     this.fireEvent("resized", this, newSize);
37938                 break;                
37939             }
37940         }
37941     },
37942     
37943     getBox : function(){
37944         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37945     },
37946     
37947     getMargins : function(){
37948         return this.margins;
37949     },
37950     
37951     updateBox : function(box){
37952         this.box = box;
37953         var el = this.activePanel.getEl();
37954         el.dom.style.left = box.x + "px";
37955         el.dom.style.top = box.y + "px";
37956         this.activePanel.setSize(box.width, box.height);
37957     },
37958     
37959     /**
37960      * Returns the container element for this region.
37961      * @return {Roo.Element}
37962      */
37963     getEl : function(){
37964         return this.activePanel;
37965     },
37966     
37967     /**
37968      * Returns true if this region is currently visible.
37969      * @return {Boolean}
37970      */
37971     isVisible : function(){
37972         return this.activePanel ? true : false;
37973     },
37974     
37975     setActivePanel : function(panel){
37976         panel = this.getPanel(panel);
37977         if(this.activePanel && this.activePanel != panel){
37978             this.activePanel.setActiveState(false);
37979             this.activePanel.getEl().setLeftTop(-10000,-10000);
37980         }
37981         this.activePanel = panel;
37982         panel.setActiveState(true);
37983         if(this.box){
37984             panel.setSize(this.box.width, this.box.height);
37985         }
37986         this.fireEvent("panelactivated", this, panel);
37987         this.fireEvent("invalidated");
37988     },
37989     
37990     /**
37991      * Show the specified panel.
37992      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37993      * @return {Roo.ContentPanel} The shown panel or null
37994      */
37995     showPanel : function(panel){
37996         panel = this.getPanel(panel);
37997         if(panel){
37998             this.setActivePanel(panel);
37999         }
38000         return panel;
38001     },
38002     
38003     /**
38004      * Get the active panel for this region.
38005      * @return {Roo.ContentPanel} The active panel or null
38006      */
38007     getActivePanel : function(){
38008         return this.activePanel;
38009     },
38010     
38011     /**
38012      * Add the passed ContentPanel(s)
38013      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38014      * @return {Roo.ContentPanel} The panel added (if only one was added)
38015      */
38016     add : function(panel){
38017         if(arguments.length > 1){
38018             for(var i = 0, len = arguments.length; i < len; i++) {
38019                 this.add(arguments[i]);
38020             }
38021             return null;
38022         }
38023         if(this.hasPanel(panel)){
38024             this.showPanel(panel);
38025             return panel;
38026         }
38027         var el = panel.getEl();
38028         if(el.dom.parentNode != this.mgr.el.dom){
38029             this.mgr.el.dom.appendChild(el.dom);
38030         }
38031         if(panel.setRegion){
38032             panel.setRegion(this);
38033         }
38034         this.panels.add(panel);
38035         el.setStyle("position", "absolute");
38036         if(!panel.background){
38037             this.setActivePanel(panel);
38038             if(this.config.initialSize && this.panels.getCount()==1){
38039                 this.resizeTo(this.config.initialSize);
38040             }
38041         }
38042         this.fireEvent("paneladded", this, panel);
38043         return panel;
38044     },
38045     
38046     /**
38047      * Returns true if the panel is in this region.
38048      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38049      * @return {Boolean}
38050      */
38051     hasPanel : function(panel){
38052         if(typeof panel == "object"){ // must be panel obj
38053             panel = panel.getId();
38054         }
38055         return this.getPanel(panel) ? true : false;
38056     },
38057     
38058     /**
38059      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38060      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38061      * @param {Boolean} preservePanel Overrides the config preservePanel option
38062      * @return {Roo.ContentPanel} The panel that was removed
38063      */
38064     remove : function(panel, preservePanel){
38065         panel = this.getPanel(panel);
38066         if(!panel){
38067             return null;
38068         }
38069         var e = {};
38070         this.fireEvent("beforeremove", this, panel, e);
38071         if(e.cancel === true){
38072             return null;
38073         }
38074         var panelId = panel.getId();
38075         this.panels.removeKey(panelId);
38076         return panel;
38077     },
38078     
38079     /**
38080      * Returns the panel specified or null if it's not in this region.
38081      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38082      * @return {Roo.ContentPanel}
38083      */
38084     getPanel : function(id){
38085         if(typeof id == "object"){ // must be panel obj
38086             return id;
38087         }
38088         return this.panels.get(id);
38089     },
38090     
38091     /**
38092      * Returns this regions position (north/south/east/west/center).
38093      * @return {String} 
38094      */
38095     getPosition: function(){
38096         return this.position;    
38097     }
38098 });/*
38099  * Based on:
38100  * Ext JS Library 1.1.1
38101  * Copyright(c) 2006-2007, Ext JS, LLC.
38102  *
38103  * Originally Released Under LGPL - original licence link has changed is not relivant.
38104  *
38105  * Fork - LGPL
38106  * <script type="text/javascript">
38107  */
38108  
38109 /**
38110  * @class Roo.bootstrap.layout.Region
38111  * @extends Roo.bootstrap.layout.Basic
38112  * This class represents a region in a layout manager.
38113  
38114  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38115  * @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})
38116  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38117  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38118  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38119  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38120  * @cfg {String}    title           The title for the region (overrides panel titles)
38121  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38122  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38123  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38124  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38125  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38126  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38127  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38128  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38129  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38130  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38131
38132  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38133  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38134  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38135  * @cfg {Number}    width           For East/West panels
38136  * @cfg {Number}    height          For North/South panels
38137  * @cfg {Boolean}   split           To show the splitter
38138  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38139  * 
38140  * @cfg {string}   cls             Extra CSS classes to add to region
38141  * 
38142  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38143  * @cfg {string}   region  the region that it inhabits..
38144  *
38145
38146  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38147  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38148
38149  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38150  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38151  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38152  */
38153 Roo.bootstrap.layout.Region = function(config)
38154 {
38155     this.applyConfig(config);
38156
38157     var mgr = config.mgr;
38158     var pos = config.region;
38159     config.skipConfig = true;
38160     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38161     
38162     if (mgr.el) {
38163         this.onRender(mgr.el);   
38164     }
38165      
38166     this.visible = true;
38167     this.collapsed = false;
38168     this.unrendered_panels = [];
38169 };
38170
38171 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38172
38173     position: '', // set by wrapper (eg. north/south etc..)
38174     unrendered_panels : null,  // unrendered panels.
38175     
38176     tabPosition : false,
38177     
38178     mgr: false, // points to 'Border'
38179     
38180     
38181     createBody : function(){
38182         /** This region's body element 
38183         * @type Roo.Element */
38184         this.bodyEl = this.el.createChild({
38185                 tag: "div",
38186                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38187         });
38188     },
38189
38190     onRender: function(ctr, pos)
38191     {
38192         var dh = Roo.DomHelper;
38193         /** This region's container element 
38194         * @type Roo.Element */
38195         this.el = dh.append(ctr.dom, {
38196                 tag: "div",
38197                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38198             }, true);
38199         /** This region's title element 
38200         * @type Roo.Element */
38201     
38202         this.titleEl = dh.append(this.el.dom,  {
38203                 tag: "div",
38204                 unselectable: "on",
38205                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38206                 children:[
38207                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38208                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38209                 ]
38210             }, true);
38211         
38212         this.titleEl.enableDisplayMode();
38213         /** This region's title text element 
38214         * @type HTMLElement */
38215         this.titleTextEl = this.titleEl.dom.firstChild;
38216         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38217         /*
38218         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38219         this.closeBtn.enableDisplayMode();
38220         this.closeBtn.on("click", this.closeClicked, this);
38221         this.closeBtn.hide();
38222     */
38223         this.createBody(this.config);
38224         if(this.config.hideWhenEmpty){
38225             this.hide();
38226             this.on("paneladded", this.validateVisibility, this);
38227             this.on("panelremoved", this.validateVisibility, this);
38228         }
38229         if(this.autoScroll){
38230             this.bodyEl.setStyle("overflow", "auto");
38231         }else{
38232             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38233         }
38234         //if(c.titlebar !== false){
38235             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38236                 this.titleEl.hide();
38237             }else{
38238                 this.titleEl.show();
38239                 if(this.config.title){
38240                     this.titleTextEl.innerHTML = this.config.title;
38241                 }
38242             }
38243         //}
38244         if(this.config.collapsed){
38245             this.collapse(true);
38246         }
38247         if(this.config.hidden){
38248             this.hide();
38249         }
38250         
38251         if (this.unrendered_panels && this.unrendered_panels.length) {
38252             for (var i =0;i< this.unrendered_panels.length; i++) {
38253                 this.add(this.unrendered_panels[i]);
38254             }
38255             this.unrendered_panels = null;
38256             
38257         }
38258         
38259     },
38260     
38261     applyConfig : function(c)
38262     {
38263         /*
38264          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38265             var dh = Roo.DomHelper;
38266             if(c.titlebar !== false){
38267                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38268                 this.collapseBtn.on("click", this.collapse, this);
38269                 this.collapseBtn.enableDisplayMode();
38270                 /*
38271                 if(c.showPin === true || this.showPin){
38272                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38273                     this.stickBtn.enableDisplayMode();
38274                     this.stickBtn.on("click", this.expand, this);
38275                     this.stickBtn.hide();
38276                 }
38277                 
38278             }
38279             */
38280             /** This region's collapsed element
38281             * @type Roo.Element */
38282             /*
38283              *
38284             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38285                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38286             ]}, true);
38287             
38288             if(c.floatable !== false){
38289                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38290                this.collapsedEl.on("click", this.collapseClick, this);
38291             }
38292
38293             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38294                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38295                    id: "message", unselectable: "on", style:{"float":"left"}});
38296                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38297              }
38298             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38299             this.expandBtn.on("click", this.expand, this);
38300             
38301         }
38302         
38303         if(this.collapseBtn){
38304             this.collapseBtn.setVisible(c.collapsible == true);
38305         }
38306         
38307         this.cmargins = c.cmargins || this.cmargins ||
38308                          (this.position == "west" || this.position == "east" ?
38309                              {top: 0, left: 2, right:2, bottom: 0} :
38310                              {top: 2, left: 0, right:0, bottom: 2});
38311         */
38312         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38313         
38314         
38315         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38316         
38317         this.autoScroll = c.autoScroll || false;
38318         
38319         
38320        
38321         
38322         this.duration = c.duration || .30;
38323         this.slideDuration = c.slideDuration || .45;
38324         this.config = c;
38325        
38326     },
38327     /**
38328      * Returns true if this region is currently visible.
38329      * @return {Boolean}
38330      */
38331     isVisible : function(){
38332         return this.visible;
38333     },
38334
38335     /**
38336      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38337      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38338      */
38339     //setCollapsedTitle : function(title){
38340     //    title = title || "&#160;";
38341      //   if(this.collapsedTitleTextEl){
38342       //      this.collapsedTitleTextEl.innerHTML = title;
38343        // }
38344     //},
38345
38346     getBox : function(){
38347         var b;
38348       //  if(!this.collapsed){
38349             b = this.el.getBox(false, true);
38350        // }else{
38351           //  b = this.collapsedEl.getBox(false, true);
38352         //}
38353         return b;
38354     },
38355
38356     getMargins : function(){
38357         return this.margins;
38358         //return this.collapsed ? this.cmargins : this.margins;
38359     },
38360 /*
38361     highlight : function(){
38362         this.el.addClass("x-layout-panel-dragover");
38363     },
38364
38365     unhighlight : function(){
38366         this.el.removeClass("x-layout-panel-dragover");
38367     },
38368 */
38369     updateBox : function(box)
38370     {
38371         if (!this.bodyEl) {
38372             return; // not rendered yet..
38373         }
38374         
38375         this.box = box;
38376         if(!this.collapsed){
38377             this.el.dom.style.left = box.x + "px";
38378             this.el.dom.style.top = box.y + "px";
38379             this.updateBody(box.width, box.height);
38380         }else{
38381             this.collapsedEl.dom.style.left = box.x + "px";
38382             this.collapsedEl.dom.style.top = box.y + "px";
38383             this.collapsedEl.setSize(box.width, box.height);
38384         }
38385         if(this.tabs){
38386             this.tabs.autoSizeTabs();
38387         }
38388     },
38389
38390     updateBody : function(w, h)
38391     {
38392         if(w !== null){
38393             this.el.setWidth(w);
38394             w -= this.el.getBorderWidth("rl");
38395             if(this.config.adjustments){
38396                 w += this.config.adjustments[0];
38397             }
38398         }
38399         if(h !== null && h > 0){
38400             this.el.setHeight(h);
38401             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38402             h -= this.el.getBorderWidth("tb");
38403             if(this.config.adjustments){
38404                 h += this.config.adjustments[1];
38405             }
38406             this.bodyEl.setHeight(h);
38407             if(this.tabs){
38408                 h = this.tabs.syncHeight(h);
38409             }
38410         }
38411         if(this.panelSize){
38412             w = w !== null ? w : this.panelSize.width;
38413             h = h !== null ? h : this.panelSize.height;
38414         }
38415         if(this.activePanel){
38416             var el = this.activePanel.getEl();
38417             w = w !== null ? w : el.getWidth();
38418             h = h !== null ? h : el.getHeight();
38419             this.panelSize = {width: w, height: h};
38420             this.activePanel.setSize(w, h);
38421         }
38422         if(Roo.isIE && this.tabs){
38423             this.tabs.el.repaint();
38424         }
38425     },
38426
38427     /**
38428      * Returns the container element for this region.
38429      * @return {Roo.Element}
38430      */
38431     getEl : function(){
38432         return this.el;
38433     },
38434
38435     /**
38436      * Hides this region.
38437      */
38438     hide : function(){
38439         //if(!this.collapsed){
38440             this.el.dom.style.left = "-2000px";
38441             this.el.hide();
38442         //}else{
38443          //   this.collapsedEl.dom.style.left = "-2000px";
38444          //   this.collapsedEl.hide();
38445        // }
38446         this.visible = false;
38447         this.fireEvent("visibilitychange", this, false);
38448     },
38449
38450     /**
38451      * Shows this region if it was previously hidden.
38452      */
38453     show : function(){
38454         //if(!this.collapsed){
38455             this.el.show();
38456         //}else{
38457         //    this.collapsedEl.show();
38458        // }
38459         this.visible = true;
38460         this.fireEvent("visibilitychange", this, true);
38461     },
38462 /*
38463     closeClicked : function(){
38464         if(this.activePanel){
38465             this.remove(this.activePanel);
38466         }
38467     },
38468
38469     collapseClick : function(e){
38470         if(this.isSlid){
38471            e.stopPropagation();
38472            this.slideIn();
38473         }else{
38474            e.stopPropagation();
38475            this.slideOut();
38476         }
38477     },
38478 */
38479     /**
38480      * Collapses this region.
38481      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38482      */
38483     /*
38484     collapse : function(skipAnim, skipCheck = false){
38485         if(this.collapsed) {
38486             return;
38487         }
38488         
38489         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38490             
38491             this.collapsed = true;
38492             if(this.split){
38493                 this.split.el.hide();
38494             }
38495             if(this.config.animate && skipAnim !== true){
38496                 this.fireEvent("invalidated", this);
38497                 this.animateCollapse();
38498             }else{
38499                 this.el.setLocation(-20000,-20000);
38500                 this.el.hide();
38501                 this.collapsedEl.show();
38502                 this.fireEvent("collapsed", this);
38503                 this.fireEvent("invalidated", this);
38504             }
38505         }
38506         
38507     },
38508 */
38509     animateCollapse : function(){
38510         // overridden
38511     },
38512
38513     /**
38514      * Expands this region if it was previously collapsed.
38515      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38516      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38517      */
38518     /*
38519     expand : function(e, skipAnim){
38520         if(e) {
38521             e.stopPropagation();
38522         }
38523         if(!this.collapsed || this.el.hasActiveFx()) {
38524             return;
38525         }
38526         if(this.isSlid){
38527             this.afterSlideIn();
38528             skipAnim = true;
38529         }
38530         this.collapsed = false;
38531         if(this.config.animate && skipAnim !== true){
38532             this.animateExpand();
38533         }else{
38534             this.el.show();
38535             if(this.split){
38536                 this.split.el.show();
38537             }
38538             this.collapsedEl.setLocation(-2000,-2000);
38539             this.collapsedEl.hide();
38540             this.fireEvent("invalidated", this);
38541             this.fireEvent("expanded", this);
38542         }
38543     },
38544 */
38545     animateExpand : function(){
38546         // overridden
38547     },
38548
38549     initTabs : function()
38550     {
38551         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38552         
38553         var ts = new Roo.bootstrap.panel.Tabs({
38554             el: this.bodyEl.dom,
38555             region : this,
38556             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38557             disableTooltips: this.config.disableTabTips,
38558             toolbar : this.config.toolbar
38559         });
38560         
38561         if(this.config.hideTabs){
38562             ts.stripWrap.setDisplayed(false);
38563         }
38564         this.tabs = ts;
38565         ts.resizeTabs = this.config.resizeTabs === true;
38566         ts.minTabWidth = this.config.minTabWidth || 40;
38567         ts.maxTabWidth = this.config.maxTabWidth || 250;
38568         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38569         ts.monitorResize = false;
38570         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38571         ts.bodyEl.addClass('roo-layout-tabs-body');
38572         this.panels.each(this.initPanelAsTab, this);
38573     },
38574
38575     initPanelAsTab : function(panel){
38576         var ti = this.tabs.addTab(
38577             panel.getEl().id,
38578             panel.getTitle(),
38579             null,
38580             this.config.closeOnTab && panel.isClosable(),
38581             panel.tpl
38582         );
38583         if(panel.tabTip !== undefined){
38584             ti.setTooltip(panel.tabTip);
38585         }
38586         ti.on("activate", function(){
38587               this.setActivePanel(panel);
38588         }, this);
38589         
38590         if(this.config.closeOnTab){
38591             ti.on("beforeclose", function(t, e){
38592                 e.cancel = true;
38593                 this.remove(panel);
38594             }, this);
38595         }
38596         
38597         panel.tabItem = ti;
38598         
38599         return ti;
38600     },
38601
38602     updatePanelTitle : function(panel, title)
38603     {
38604         if(this.activePanel == panel){
38605             this.updateTitle(title);
38606         }
38607         if(this.tabs){
38608             var ti = this.tabs.getTab(panel.getEl().id);
38609             ti.setText(title);
38610             if(panel.tabTip !== undefined){
38611                 ti.setTooltip(panel.tabTip);
38612             }
38613         }
38614     },
38615
38616     updateTitle : function(title){
38617         if(this.titleTextEl && !this.config.title){
38618             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38619         }
38620     },
38621
38622     setActivePanel : function(panel)
38623     {
38624         panel = this.getPanel(panel);
38625         if(this.activePanel && this.activePanel != panel){
38626             if(this.activePanel.setActiveState(false) === false){
38627                 return;
38628             }
38629         }
38630         this.activePanel = panel;
38631         panel.setActiveState(true);
38632         if(this.panelSize){
38633             panel.setSize(this.panelSize.width, this.panelSize.height);
38634         }
38635         if(this.closeBtn){
38636             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38637         }
38638         this.updateTitle(panel.getTitle());
38639         if(this.tabs){
38640             this.fireEvent("invalidated", this);
38641         }
38642         this.fireEvent("panelactivated", this, panel);
38643     },
38644
38645     /**
38646      * Shows the specified panel.
38647      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38648      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38649      */
38650     showPanel : function(panel)
38651     {
38652         panel = this.getPanel(panel);
38653         if(panel){
38654             if(this.tabs){
38655                 var tab = this.tabs.getTab(panel.getEl().id);
38656                 if(tab.isHidden()){
38657                     this.tabs.unhideTab(tab.id);
38658                 }
38659                 tab.activate();
38660             }else{
38661                 this.setActivePanel(panel);
38662             }
38663         }
38664         return panel;
38665     },
38666
38667     /**
38668      * Get the active panel for this region.
38669      * @return {Roo.ContentPanel} The active panel or null
38670      */
38671     getActivePanel : function(){
38672         return this.activePanel;
38673     },
38674
38675     validateVisibility : function(){
38676         if(this.panels.getCount() < 1){
38677             this.updateTitle("&#160;");
38678             this.closeBtn.hide();
38679             this.hide();
38680         }else{
38681             if(!this.isVisible()){
38682                 this.show();
38683             }
38684         }
38685     },
38686
38687     /**
38688      * Adds the passed ContentPanel(s) to this region.
38689      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38690      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38691      */
38692     add : function(panel)
38693     {
38694         if(arguments.length > 1){
38695             for(var i = 0, len = arguments.length; i < len; i++) {
38696                 this.add(arguments[i]);
38697             }
38698             return null;
38699         }
38700         
38701         // if we have not been rendered yet, then we can not really do much of this..
38702         if (!this.bodyEl) {
38703             this.unrendered_panels.push(panel);
38704             return panel;
38705         }
38706         
38707         
38708         
38709         
38710         if(this.hasPanel(panel)){
38711             this.showPanel(panel);
38712             return panel;
38713         }
38714         panel.setRegion(this);
38715         this.panels.add(panel);
38716        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38717             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38718             // and hide them... ???
38719             this.bodyEl.dom.appendChild(panel.getEl().dom);
38720             if(panel.background !== true){
38721                 this.setActivePanel(panel);
38722             }
38723             this.fireEvent("paneladded", this, panel);
38724             return panel;
38725         }
38726         */
38727         if(!this.tabs){
38728             this.initTabs();
38729         }else{
38730             this.initPanelAsTab(panel);
38731         }
38732         
38733         
38734         if(panel.background !== true){
38735             this.tabs.activate(panel.getEl().id);
38736         }
38737         this.fireEvent("paneladded", this, panel);
38738         return panel;
38739     },
38740
38741     /**
38742      * Hides the tab for the specified panel.
38743      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38744      */
38745     hidePanel : function(panel){
38746         if(this.tabs && (panel = this.getPanel(panel))){
38747             this.tabs.hideTab(panel.getEl().id);
38748         }
38749     },
38750
38751     /**
38752      * Unhides the tab for a previously hidden panel.
38753      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38754      */
38755     unhidePanel : function(panel){
38756         if(this.tabs && (panel = this.getPanel(panel))){
38757             this.tabs.unhideTab(panel.getEl().id);
38758         }
38759     },
38760
38761     clearPanels : function(){
38762         while(this.panels.getCount() > 0){
38763              this.remove(this.panels.first());
38764         }
38765     },
38766
38767     /**
38768      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38769      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38770      * @param {Boolean} preservePanel Overrides the config preservePanel option
38771      * @return {Roo.ContentPanel} The panel that was removed
38772      */
38773     remove : function(panel, preservePanel)
38774     {
38775         panel = this.getPanel(panel);
38776         if(!panel){
38777             return null;
38778         }
38779         var e = {};
38780         this.fireEvent("beforeremove", this, panel, e);
38781         if(e.cancel === true){
38782             return null;
38783         }
38784         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38785         var panelId = panel.getId();
38786         this.panels.removeKey(panelId);
38787         if(preservePanel){
38788             document.body.appendChild(panel.getEl().dom);
38789         }
38790         if(this.tabs){
38791             this.tabs.removeTab(panel.getEl().id);
38792         }else if (!preservePanel){
38793             this.bodyEl.dom.removeChild(panel.getEl().dom);
38794         }
38795         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38796             var p = this.panels.first();
38797             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38798             tempEl.appendChild(p.getEl().dom);
38799             this.bodyEl.update("");
38800             this.bodyEl.dom.appendChild(p.getEl().dom);
38801             tempEl = null;
38802             this.updateTitle(p.getTitle());
38803             this.tabs = null;
38804             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38805             this.setActivePanel(p);
38806         }
38807         panel.setRegion(null);
38808         if(this.activePanel == panel){
38809             this.activePanel = null;
38810         }
38811         if(this.config.autoDestroy !== false && preservePanel !== true){
38812             try{panel.destroy();}catch(e){}
38813         }
38814         this.fireEvent("panelremoved", this, panel);
38815         return panel;
38816     },
38817
38818     /**
38819      * Returns the TabPanel component used by this region
38820      * @return {Roo.TabPanel}
38821      */
38822     getTabs : function(){
38823         return this.tabs;
38824     },
38825
38826     createTool : function(parentEl, className){
38827         var btn = Roo.DomHelper.append(parentEl, {
38828             tag: "div",
38829             cls: "x-layout-tools-button",
38830             children: [ {
38831                 tag: "div",
38832                 cls: "roo-layout-tools-button-inner " + className,
38833                 html: "&#160;"
38834             }]
38835         }, true);
38836         btn.addClassOnOver("roo-layout-tools-button-over");
38837         return btn;
38838     }
38839 });/*
38840  * Based on:
38841  * Ext JS Library 1.1.1
38842  * Copyright(c) 2006-2007, Ext JS, LLC.
38843  *
38844  * Originally Released Under LGPL - original licence link has changed is not relivant.
38845  *
38846  * Fork - LGPL
38847  * <script type="text/javascript">
38848  */
38849  
38850
38851
38852 /**
38853  * @class Roo.SplitLayoutRegion
38854  * @extends Roo.LayoutRegion
38855  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38856  */
38857 Roo.bootstrap.layout.Split = function(config){
38858     this.cursor = config.cursor;
38859     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38860 };
38861
38862 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38863 {
38864     splitTip : "Drag to resize.",
38865     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38866     useSplitTips : false,
38867
38868     applyConfig : function(config){
38869         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38870     },
38871     
38872     onRender : function(ctr,pos) {
38873         
38874         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38875         if(!this.config.split){
38876             return;
38877         }
38878         if(!this.split){
38879             
38880             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38881                             tag: "div",
38882                             id: this.el.id + "-split",
38883                             cls: "roo-layout-split roo-layout-split-"+this.position,
38884                             html: "&#160;"
38885             });
38886             /** The SplitBar for this region 
38887             * @type Roo.SplitBar */
38888             // does not exist yet...
38889             Roo.log([this.position, this.orientation]);
38890             
38891             this.split = new Roo.bootstrap.SplitBar({
38892                 dragElement : splitEl,
38893                 resizingElement: this.el,
38894                 orientation : this.orientation
38895             });
38896             
38897             this.split.on("moved", this.onSplitMove, this);
38898             this.split.useShim = this.config.useShim === true;
38899             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38900             if(this.useSplitTips){
38901                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38902             }
38903             //if(config.collapsible){
38904             //    this.split.el.on("dblclick", this.collapse,  this);
38905             //}
38906         }
38907         if(typeof this.config.minSize != "undefined"){
38908             this.split.minSize = this.config.minSize;
38909         }
38910         if(typeof this.config.maxSize != "undefined"){
38911             this.split.maxSize = this.config.maxSize;
38912         }
38913         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38914             this.hideSplitter();
38915         }
38916         
38917     },
38918
38919     getHMaxSize : function(){
38920          var cmax = this.config.maxSize || 10000;
38921          var center = this.mgr.getRegion("center");
38922          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38923     },
38924
38925     getVMaxSize : function(){
38926          var cmax = this.config.maxSize || 10000;
38927          var center = this.mgr.getRegion("center");
38928          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38929     },
38930
38931     onSplitMove : function(split, newSize){
38932         this.fireEvent("resized", this, newSize);
38933     },
38934     
38935     /** 
38936      * Returns the {@link Roo.SplitBar} for this region.
38937      * @return {Roo.SplitBar}
38938      */
38939     getSplitBar : function(){
38940         return this.split;
38941     },
38942     
38943     hide : function(){
38944         this.hideSplitter();
38945         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38946     },
38947
38948     hideSplitter : function(){
38949         if(this.split){
38950             this.split.el.setLocation(-2000,-2000);
38951             this.split.el.hide();
38952         }
38953     },
38954
38955     show : function(){
38956         if(this.split){
38957             this.split.el.show();
38958         }
38959         Roo.bootstrap.layout.Split.superclass.show.call(this);
38960     },
38961     
38962     beforeSlide: function(){
38963         if(Roo.isGecko){// firefox overflow auto bug workaround
38964             this.bodyEl.clip();
38965             if(this.tabs) {
38966                 this.tabs.bodyEl.clip();
38967             }
38968             if(this.activePanel){
38969                 this.activePanel.getEl().clip();
38970                 
38971                 if(this.activePanel.beforeSlide){
38972                     this.activePanel.beforeSlide();
38973                 }
38974             }
38975         }
38976     },
38977     
38978     afterSlide : function(){
38979         if(Roo.isGecko){// firefox overflow auto bug workaround
38980             this.bodyEl.unclip();
38981             if(this.tabs) {
38982                 this.tabs.bodyEl.unclip();
38983             }
38984             if(this.activePanel){
38985                 this.activePanel.getEl().unclip();
38986                 if(this.activePanel.afterSlide){
38987                     this.activePanel.afterSlide();
38988                 }
38989             }
38990         }
38991     },
38992
38993     initAutoHide : function(){
38994         if(this.autoHide !== false){
38995             if(!this.autoHideHd){
38996                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38997                 this.autoHideHd = {
38998                     "mouseout": function(e){
38999                         if(!e.within(this.el, true)){
39000                             st.delay(500);
39001                         }
39002                     },
39003                     "mouseover" : function(e){
39004                         st.cancel();
39005                     },
39006                     scope : this
39007                 };
39008             }
39009             this.el.on(this.autoHideHd);
39010         }
39011     },
39012
39013     clearAutoHide : function(){
39014         if(this.autoHide !== false){
39015             this.el.un("mouseout", this.autoHideHd.mouseout);
39016             this.el.un("mouseover", this.autoHideHd.mouseover);
39017         }
39018     },
39019
39020     clearMonitor : function(){
39021         Roo.get(document).un("click", this.slideInIf, this);
39022     },
39023
39024     // these names are backwards but not changed for compat
39025     slideOut : function(){
39026         if(this.isSlid || this.el.hasActiveFx()){
39027             return;
39028         }
39029         this.isSlid = true;
39030         if(this.collapseBtn){
39031             this.collapseBtn.hide();
39032         }
39033         this.closeBtnState = this.closeBtn.getStyle('display');
39034         this.closeBtn.hide();
39035         if(this.stickBtn){
39036             this.stickBtn.show();
39037         }
39038         this.el.show();
39039         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39040         this.beforeSlide();
39041         this.el.setStyle("z-index", 10001);
39042         this.el.slideIn(this.getSlideAnchor(), {
39043             callback: function(){
39044                 this.afterSlide();
39045                 this.initAutoHide();
39046                 Roo.get(document).on("click", this.slideInIf, this);
39047                 this.fireEvent("slideshow", this);
39048             },
39049             scope: this,
39050             block: true
39051         });
39052     },
39053
39054     afterSlideIn : function(){
39055         this.clearAutoHide();
39056         this.isSlid = false;
39057         this.clearMonitor();
39058         this.el.setStyle("z-index", "");
39059         if(this.collapseBtn){
39060             this.collapseBtn.show();
39061         }
39062         this.closeBtn.setStyle('display', this.closeBtnState);
39063         if(this.stickBtn){
39064             this.stickBtn.hide();
39065         }
39066         this.fireEvent("slidehide", this);
39067     },
39068
39069     slideIn : function(cb){
39070         if(!this.isSlid || this.el.hasActiveFx()){
39071             Roo.callback(cb);
39072             return;
39073         }
39074         this.isSlid = false;
39075         this.beforeSlide();
39076         this.el.slideOut(this.getSlideAnchor(), {
39077             callback: function(){
39078                 this.el.setLeftTop(-10000, -10000);
39079                 this.afterSlide();
39080                 this.afterSlideIn();
39081                 Roo.callback(cb);
39082             },
39083             scope: this,
39084             block: true
39085         });
39086     },
39087     
39088     slideInIf : function(e){
39089         if(!e.within(this.el)){
39090             this.slideIn();
39091         }
39092     },
39093
39094     animateCollapse : function(){
39095         this.beforeSlide();
39096         this.el.setStyle("z-index", 20000);
39097         var anchor = this.getSlideAnchor();
39098         this.el.slideOut(anchor, {
39099             callback : function(){
39100                 this.el.setStyle("z-index", "");
39101                 this.collapsedEl.slideIn(anchor, {duration:.3});
39102                 this.afterSlide();
39103                 this.el.setLocation(-10000,-10000);
39104                 this.el.hide();
39105                 this.fireEvent("collapsed", this);
39106             },
39107             scope: this,
39108             block: true
39109         });
39110     },
39111
39112     animateExpand : function(){
39113         this.beforeSlide();
39114         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39115         this.el.setStyle("z-index", 20000);
39116         this.collapsedEl.hide({
39117             duration:.1
39118         });
39119         this.el.slideIn(this.getSlideAnchor(), {
39120             callback : function(){
39121                 this.el.setStyle("z-index", "");
39122                 this.afterSlide();
39123                 if(this.split){
39124                     this.split.el.show();
39125                 }
39126                 this.fireEvent("invalidated", this);
39127                 this.fireEvent("expanded", this);
39128             },
39129             scope: this,
39130             block: true
39131         });
39132     },
39133
39134     anchors : {
39135         "west" : "left",
39136         "east" : "right",
39137         "north" : "top",
39138         "south" : "bottom"
39139     },
39140
39141     sanchors : {
39142         "west" : "l",
39143         "east" : "r",
39144         "north" : "t",
39145         "south" : "b"
39146     },
39147
39148     canchors : {
39149         "west" : "tl-tr",
39150         "east" : "tr-tl",
39151         "north" : "tl-bl",
39152         "south" : "bl-tl"
39153     },
39154
39155     getAnchor : function(){
39156         return this.anchors[this.position];
39157     },
39158
39159     getCollapseAnchor : function(){
39160         return this.canchors[this.position];
39161     },
39162
39163     getSlideAnchor : function(){
39164         return this.sanchors[this.position];
39165     },
39166
39167     getAlignAdj : function(){
39168         var cm = this.cmargins;
39169         switch(this.position){
39170             case "west":
39171                 return [0, 0];
39172             break;
39173             case "east":
39174                 return [0, 0];
39175             break;
39176             case "north":
39177                 return [0, 0];
39178             break;
39179             case "south":
39180                 return [0, 0];
39181             break;
39182         }
39183     },
39184
39185     getExpandAdj : function(){
39186         var c = this.collapsedEl, cm = this.cmargins;
39187         switch(this.position){
39188             case "west":
39189                 return [-(cm.right+c.getWidth()+cm.left), 0];
39190             break;
39191             case "east":
39192                 return [cm.right+c.getWidth()+cm.left, 0];
39193             break;
39194             case "north":
39195                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39196             break;
39197             case "south":
39198                 return [0, cm.top+cm.bottom+c.getHeight()];
39199             break;
39200         }
39201     }
39202 });/*
39203  * Based on:
39204  * Ext JS Library 1.1.1
39205  * Copyright(c) 2006-2007, Ext JS, LLC.
39206  *
39207  * Originally Released Under LGPL - original licence link has changed is not relivant.
39208  *
39209  * Fork - LGPL
39210  * <script type="text/javascript">
39211  */
39212 /*
39213  * These classes are private internal classes
39214  */
39215 Roo.bootstrap.layout.Center = function(config){
39216     config.region = "center";
39217     Roo.bootstrap.layout.Region.call(this, config);
39218     this.visible = true;
39219     this.minWidth = config.minWidth || 20;
39220     this.minHeight = config.minHeight || 20;
39221 };
39222
39223 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39224     hide : function(){
39225         // center panel can't be hidden
39226     },
39227     
39228     show : function(){
39229         // center panel can't be hidden
39230     },
39231     
39232     getMinWidth: function(){
39233         return this.minWidth;
39234     },
39235     
39236     getMinHeight: function(){
39237         return this.minHeight;
39238     }
39239 });
39240
39241
39242
39243
39244  
39245
39246
39247
39248
39249
39250
39251 Roo.bootstrap.layout.North = function(config)
39252 {
39253     config.region = 'north';
39254     config.cursor = 'n-resize';
39255     
39256     Roo.bootstrap.layout.Split.call(this, config);
39257     
39258     
39259     if(this.split){
39260         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39261         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39262         this.split.el.addClass("roo-layout-split-v");
39263     }
39264     var size = config.initialSize || config.height;
39265     if(typeof size != "undefined"){
39266         this.el.setHeight(size);
39267     }
39268 };
39269 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39270 {
39271     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39272     
39273     
39274     
39275     getBox : function(){
39276         if(this.collapsed){
39277             return this.collapsedEl.getBox();
39278         }
39279         var box = this.el.getBox();
39280         if(this.split){
39281             box.height += this.split.el.getHeight();
39282         }
39283         return box;
39284     },
39285     
39286     updateBox : function(box){
39287         if(this.split && !this.collapsed){
39288             box.height -= this.split.el.getHeight();
39289             this.split.el.setLeft(box.x);
39290             this.split.el.setTop(box.y+box.height);
39291             this.split.el.setWidth(box.width);
39292         }
39293         if(this.collapsed){
39294             this.updateBody(box.width, null);
39295         }
39296         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39297     }
39298 });
39299
39300
39301
39302
39303
39304 Roo.bootstrap.layout.South = function(config){
39305     config.region = 'south';
39306     config.cursor = 's-resize';
39307     Roo.bootstrap.layout.Split.call(this, config);
39308     if(this.split){
39309         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39310         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39311         this.split.el.addClass("roo-layout-split-v");
39312     }
39313     var size = config.initialSize || config.height;
39314     if(typeof size != "undefined"){
39315         this.el.setHeight(size);
39316     }
39317 };
39318
39319 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39320     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39321     getBox : function(){
39322         if(this.collapsed){
39323             return this.collapsedEl.getBox();
39324         }
39325         var box = this.el.getBox();
39326         if(this.split){
39327             var sh = this.split.el.getHeight();
39328             box.height += sh;
39329             box.y -= sh;
39330         }
39331         return box;
39332     },
39333     
39334     updateBox : function(box){
39335         if(this.split && !this.collapsed){
39336             var sh = this.split.el.getHeight();
39337             box.height -= sh;
39338             box.y += sh;
39339             this.split.el.setLeft(box.x);
39340             this.split.el.setTop(box.y-sh);
39341             this.split.el.setWidth(box.width);
39342         }
39343         if(this.collapsed){
39344             this.updateBody(box.width, null);
39345         }
39346         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39347     }
39348 });
39349
39350 Roo.bootstrap.layout.East = function(config){
39351     config.region = "east";
39352     config.cursor = "e-resize";
39353     Roo.bootstrap.layout.Split.call(this, config);
39354     if(this.split){
39355         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39356         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39357         this.split.el.addClass("roo-layout-split-h");
39358     }
39359     var size = config.initialSize || config.width;
39360     if(typeof size != "undefined"){
39361         this.el.setWidth(size);
39362     }
39363 };
39364 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39365     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39366     getBox : function(){
39367         if(this.collapsed){
39368             return this.collapsedEl.getBox();
39369         }
39370         var box = this.el.getBox();
39371         if(this.split){
39372             var sw = this.split.el.getWidth();
39373             box.width += sw;
39374             box.x -= sw;
39375         }
39376         return box;
39377     },
39378
39379     updateBox : function(box){
39380         if(this.split && !this.collapsed){
39381             var sw = this.split.el.getWidth();
39382             box.width -= sw;
39383             this.split.el.setLeft(box.x);
39384             this.split.el.setTop(box.y);
39385             this.split.el.setHeight(box.height);
39386             box.x += sw;
39387         }
39388         if(this.collapsed){
39389             this.updateBody(null, box.height);
39390         }
39391         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39392     }
39393 });
39394
39395 Roo.bootstrap.layout.West = function(config){
39396     config.region = "west";
39397     config.cursor = "w-resize";
39398     
39399     Roo.bootstrap.layout.Split.call(this, config);
39400     if(this.split){
39401         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39402         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39403         this.split.el.addClass("roo-layout-split-h");
39404     }
39405     
39406 };
39407 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39408     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39409     
39410     onRender: function(ctr, pos)
39411     {
39412         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39413         var size = this.config.initialSize || this.config.width;
39414         if(typeof size != "undefined"){
39415             this.el.setWidth(size);
39416         }
39417     },
39418     
39419     getBox : function(){
39420         if(this.collapsed){
39421             return this.collapsedEl.getBox();
39422         }
39423         var box = this.el.getBox();
39424         if(this.split){
39425             box.width += this.split.el.getWidth();
39426         }
39427         return box;
39428     },
39429     
39430     updateBox : function(box){
39431         if(this.split && !this.collapsed){
39432             var sw = this.split.el.getWidth();
39433             box.width -= sw;
39434             this.split.el.setLeft(box.x+box.width);
39435             this.split.el.setTop(box.y);
39436             this.split.el.setHeight(box.height);
39437         }
39438         if(this.collapsed){
39439             this.updateBody(null, box.height);
39440         }
39441         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39442     }
39443 });Roo.namespace("Roo.bootstrap.panel");/*
39444  * Based on:
39445  * Ext JS Library 1.1.1
39446  * Copyright(c) 2006-2007, Ext JS, LLC.
39447  *
39448  * Originally Released Under LGPL - original licence link has changed is not relivant.
39449  *
39450  * Fork - LGPL
39451  * <script type="text/javascript">
39452  */
39453 /**
39454  * @class Roo.ContentPanel
39455  * @extends Roo.util.Observable
39456  * A basic ContentPanel element.
39457  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39458  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39459  * @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
39460  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39461  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39462  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39463  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39464  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39465  * @cfg {String} title          The title for this panel
39466  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39467  * @cfg {String} url            Calls {@link #setUrl} with this value
39468  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39469  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39470  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39471  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39472  * @cfg {Boolean} badges render the badges
39473  * @cfg {String} cls  extra classes to use  
39474  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39475
39476  * @constructor
39477  * Create a new ContentPanel.
39478  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39479  * @param {String/Object} config A string to set only the title or a config object
39480  * @param {String} content (optional) Set the HTML content for this panel
39481  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39482  */
39483 Roo.bootstrap.panel.Content = function( config){
39484     
39485     this.tpl = config.tpl || false;
39486     
39487     var el = config.el;
39488     var content = config.content;
39489
39490     if(config.autoCreate){ // xtype is available if this is called from factory
39491         el = Roo.id();
39492     }
39493     this.el = Roo.get(el);
39494     if(!this.el && config && config.autoCreate){
39495         if(typeof config.autoCreate == "object"){
39496             if(!config.autoCreate.id){
39497                 config.autoCreate.id = config.id||el;
39498             }
39499             this.el = Roo.DomHelper.append(document.body,
39500                         config.autoCreate, true);
39501         }else{
39502             var elcfg =  {
39503                 tag: "div",
39504                 cls: (config.cls || '') +
39505                     (config.background ? ' bg-' + config.background : '') +
39506                     " roo-layout-inactive-content",
39507                 id: config.id||el
39508             };
39509             if (config.html) {
39510                 elcfg.html = config.html;
39511                 
39512             }
39513                         
39514             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39515         }
39516     } 
39517     this.closable = false;
39518     this.loaded = false;
39519     this.active = false;
39520    
39521       
39522     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39523         
39524         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39525         
39526         this.wrapEl = this.el; //this.el.wrap();
39527         var ti = [];
39528         if (config.toolbar.items) {
39529             ti = config.toolbar.items ;
39530             delete config.toolbar.items ;
39531         }
39532         
39533         var nitems = [];
39534         this.toolbar.render(this.wrapEl, 'before');
39535         for(var i =0;i < ti.length;i++) {
39536           //  Roo.log(['add child', items[i]]);
39537             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39538         }
39539         this.toolbar.items = nitems;
39540         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39541         delete config.toolbar;
39542         
39543     }
39544     /*
39545     // xtype created footer. - not sure if will work as we normally have to render first..
39546     if (this.footer && !this.footer.el && this.footer.xtype) {
39547         if (!this.wrapEl) {
39548             this.wrapEl = this.el.wrap();
39549         }
39550     
39551         this.footer.container = this.wrapEl.createChild();
39552          
39553         this.footer = Roo.factory(this.footer, Roo);
39554         
39555     }
39556     */
39557     
39558      if(typeof config == "string"){
39559         this.title = config;
39560     }else{
39561         Roo.apply(this, config);
39562     }
39563     
39564     if(this.resizeEl){
39565         this.resizeEl = Roo.get(this.resizeEl, true);
39566     }else{
39567         this.resizeEl = this.el;
39568     }
39569     // handle view.xtype
39570     
39571  
39572     
39573     
39574     this.addEvents({
39575         /**
39576          * @event activate
39577          * Fires when this panel is activated. 
39578          * @param {Roo.ContentPanel} this
39579          */
39580         "activate" : true,
39581         /**
39582          * @event deactivate
39583          * Fires when this panel is activated. 
39584          * @param {Roo.ContentPanel} this
39585          */
39586         "deactivate" : true,
39587
39588         /**
39589          * @event resize
39590          * Fires when this panel is resized if fitToFrame is true.
39591          * @param {Roo.ContentPanel} this
39592          * @param {Number} width The width after any component adjustments
39593          * @param {Number} height The height after any component adjustments
39594          */
39595         "resize" : true,
39596         
39597          /**
39598          * @event render
39599          * Fires when this tab is created
39600          * @param {Roo.ContentPanel} this
39601          */
39602         "render" : true
39603         
39604         
39605         
39606     });
39607     
39608
39609     
39610     
39611     if(this.autoScroll){
39612         this.resizeEl.setStyle("overflow", "auto");
39613     } else {
39614         // fix randome scrolling
39615         //this.el.on('scroll', function() {
39616         //    Roo.log('fix random scolling');
39617         //    this.scrollTo('top',0); 
39618         //});
39619     }
39620     content = content || this.content;
39621     if(content){
39622         this.setContent(content);
39623     }
39624     if(config && config.url){
39625         this.setUrl(this.url, this.params, this.loadOnce);
39626     }
39627     
39628     
39629     
39630     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39631     
39632     if (this.view && typeof(this.view.xtype) != 'undefined') {
39633         this.view.el = this.el.appendChild(document.createElement("div"));
39634         this.view = Roo.factory(this.view); 
39635         this.view.render  &&  this.view.render(false, '');  
39636     }
39637     
39638     
39639     this.fireEvent('render', this);
39640 };
39641
39642 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39643     
39644     cls : '',
39645     background : '',
39646     
39647     tabTip : '',
39648     
39649     setRegion : function(region){
39650         this.region = region;
39651         this.setActiveClass(region && !this.background);
39652     },
39653     
39654     
39655     setActiveClass: function(state)
39656     {
39657         if(state){
39658            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39659            this.el.setStyle('position','relative');
39660         }else{
39661            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39662            this.el.setStyle('position', 'absolute');
39663         } 
39664     },
39665     
39666     /**
39667      * Returns the toolbar for this Panel if one was configured. 
39668      * @return {Roo.Toolbar} 
39669      */
39670     getToolbar : function(){
39671         return this.toolbar;
39672     },
39673     
39674     setActiveState : function(active)
39675     {
39676         this.active = active;
39677         this.setActiveClass(active);
39678         if(!active){
39679             if(this.fireEvent("deactivate", this) === false){
39680                 return false;
39681             }
39682             return true;
39683         }
39684         this.fireEvent("activate", this);
39685         return true;
39686     },
39687     /**
39688      * Updates this panel's element
39689      * @param {String} content The new content
39690      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39691     */
39692     setContent : function(content, loadScripts){
39693         this.el.update(content, loadScripts);
39694     },
39695
39696     ignoreResize : function(w, h){
39697         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39698             return true;
39699         }else{
39700             this.lastSize = {width: w, height: h};
39701             return false;
39702         }
39703     },
39704     /**
39705      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39706      * @return {Roo.UpdateManager} The UpdateManager
39707      */
39708     getUpdateManager : function(){
39709         return this.el.getUpdateManager();
39710     },
39711      /**
39712      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39713      * @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:
39714 <pre><code>
39715 panel.load({
39716     url: "your-url.php",
39717     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39718     callback: yourFunction,
39719     scope: yourObject, //(optional scope)
39720     discardUrl: false,
39721     nocache: false,
39722     text: "Loading...",
39723     timeout: 30,
39724     scripts: false
39725 });
39726 </code></pre>
39727      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39728      * 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.
39729      * @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}
39730      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39731      * @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.
39732      * @return {Roo.ContentPanel} this
39733      */
39734     load : function(){
39735         var um = this.el.getUpdateManager();
39736         um.update.apply(um, arguments);
39737         return this;
39738     },
39739
39740
39741     /**
39742      * 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.
39743      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39744      * @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)
39745      * @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)
39746      * @return {Roo.UpdateManager} The UpdateManager
39747      */
39748     setUrl : function(url, params, loadOnce){
39749         if(this.refreshDelegate){
39750             this.removeListener("activate", this.refreshDelegate);
39751         }
39752         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39753         this.on("activate", this.refreshDelegate);
39754         return this.el.getUpdateManager();
39755     },
39756     
39757     _handleRefresh : function(url, params, loadOnce){
39758         if(!loadOnce || !this.loaded){
39759             var updater = this.el.getUpdateManager();
39760             updater.update(url, params, this._setLoaded.createDelegate(this));
39761         }
39762     },
39763     
39764     _setLoaded : function(){
39765         this.loaded = true;
39766     }, 
39767     
39768     /**
39769      * Returns this panel's id
39770      * @return {String} 
39771      */
39772     getId : function(){
39773         return this.el.id;
39774     },
39775     
39776     /** 
39777      * Returns this panel's element - used by regiosn to add.
39778      * @return {Roo.Element} 
39779      */
39780     getEl : function(){
39781         return this.wrapEl || this.el;
39782     },
39783     
39784    
39785     
39786     adjustForComponents : function(width, height)
39787     {
39788         //Roo.log('adjustForComponents ');
39789         if(this.resizeEl != this.el){
39790             width -= this.el.getFrameWidth('lr');
39791             height -= this.el.getFrameWidth('tb');
39792         }
39793         if(this.toolbar){
39794             var te = this.toolbar.getEl();
39795             te.setWidth(width);
39796             height -= te.getHeight();
39797         }
39798         if(this.footer){
39799             var te = this.footer.getEl();
39800             te.setWidth(width);
39801             height -= te.getHeight();
39802         }
39803         
39804         
39805         if(this.adjustments){
39806             width += this.adjustments[0];
39807             height += this.adjustments[1];
39808         }
39809         return {"width": width, "height": height};
39810     },
39811     
39812     setSize : function(width, height){
39813         if(this.fitToFrame && !this.ignoreResize(width, height)){
39814             if(this.fitContainer && this.resizeEl != this.el){
39815                 this.el.setSize(width, height);
39816             }
39817             var size = this.adjustForComponents(width, height);
39818             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39819             this.fireEvent('resize', this, size.width, size.height);
39820         }
39821     },
39822     
39823     /**
39824      * Returns this panel's title
39825      * @return {String} 
39826      */
39827     getTitle : function(){
39828         
39829         if (typeof(this.title) != 'object') {
39830             return this.title;
39831         }
39832         
39833         var t = '';
39834         for (var k in this.title) {
39835             if (!this.title.hasOwnProperty(k)) {
39836                 continue;
39837             }
39838             
39839             if (k.indexOf('-') >= 0) {
39840                 var s = k.split('-');
39841                 for (var i = 0; i<s.length; i++) {
39842                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39843                 }
39844             } else {
39845                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39846             }
39847         }
39848         return t;
39849     },
39850     
39851     /**
39852      * Set this panel's title
39853      * @param {String} title
39854      */
39855     setTitle : function(title){
39856         this.title = title;
39857         if(this.region){
39858             this.region.updatePanelTitle(this, title);
39859         }
39860     },
39861     
39862     /**
39863      * Returns true is this panel was configured to be closable
39864      * @return {Boolean} 
39865      */
39866     isClosable : function(){
39867         return this.closable;
39868     },
39869     
39870     beforeSlide : function(){
39871         this.el.clip();
39872         this.resizeEl.clip();
39873     },
39874     
39875     afterSlide : function(){
39876         this.el.unclip();
39877         this.resizeEl.unclip();
39878     },
39879     
39880     /**
39881      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39882      *   Will fail silently if the {@link #setUrl} method has not been called.
39883      *   This does not activate the panel, just updates its content.
39884      */
39885     refresh : function(){
39886         if(this.refreshDelegate){
39887            this.loaded = false;
39888            this.refreshDelegate();
39889         }
39890     },
39891     
39892     /**
39893      * Destroys this panel
39894      */
39895     destroy : function(){
39896         this.el.removeAllListeners();
39897         var tempEl = document.createElement("span");
39898         tempEl.appendChild(this.el.dom);
39899         tempEl.innerHTML = "";
39900         this.el.remove();
39901         this.el = null;
39902     },
39903     
39904     /**
39905      * form - if the content panel contains a form - this is a reference to it.
39906      * @type {Roo.form.Form}
39907      */
39908     form : false,
39909     /**
39910      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39911      *    This contains a reference to it.
39912      * @type {Roo.View}
39913      */
39914     view : false,
39915     
39916       /**
39917      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39918      * <pre><code>
39919
39920 layout.addxtype({
39921        xtype : 'Form',
39922        items: [ .... ]
39923    }
39924 );
39925
39926 </code></pre>
39927      * @param {Object} cfg Xtype definition of item to add.
39928      */
39929     
39930     
39931     getChildContainer: function () {
39932         return this.getEl();
39933     }
39934     
39935     
39936     /*
39937         var  ret = new Roo.factory(cfg);
39938         return ret;
39939         
39940         
39941         // add form..
39942         if (cfg.xtype.match(/^Form$/)) {
39943             
39944             var el;
39945             //if (this.footer) {
39946             //    el = this.footer.container.insertSibling(false, 'before');
39947             //} else {
39948                 el = this.el.createChild();
39949             //}
39950
39951             this.form = new  Roo.form.Form(cfg);
39952             
39953             
39954             if ( this.form.allItems.length) {
39955                 this.form.render(el.dom);
39956             }
39957             return this.form;
39958         }
39959         // should only have one of theses..
39960         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39961             // views.. should not be just added - used named prop 'view''
39962             
39963             cfg.el = this.el.appendChild(document.createElement("div"));
39964             // factory?
39965             
39966             var ret = new Roo.factory(cfg);
39967              
39968              ret.render && ret.render(false, ''); // render blank..
39969             this.view = ret;
39970             return ret;
39971         }
39972         return false;
39973     }
39974     \*/
39975 });
39976  
39977 /**
39978  * @class Roo.bootstrap.panel.Grid
39979  * @extends Roo.bootstrap.panel.Content
39980  * @constructor
39981  * Create a new GridPanel.
39982  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39983  * @param {Object} config A the config object
39984   
39985  */
39986
39987
39988
39989 Roo.bootstrap.panel.Grid = function(config)
39990 {
39991     
39992       
39993     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39994         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39995
39996     config.el = this.wrapper;
39997     //this.el = this.wrapper;
39998     
39999       if (config.container) {
40000         // ctor'ed from a Border/panel.grid
40001         
40002         
40003         this.wrapper.setStyle("overflow", "hidden");
40004         this.wrapper.addClass('roo-grid-container');
40005
40006     }
40007     
40008     
40009     if(config.toolbar){
40010         var tool_el = this.wrapper.createChild();    
40011         this.toolbar = Roo.factory(config.toolbar);
40012         var ti = [];
40013         if (config.toolbar.items) {
40014             ti = config.toolbar.items ;
40015             delete config.toolbar.items ;
40016         }
40017         
40018         var nitems = [];
40019         this.toolbar.render(tool_el);
40020         for(var i =0;i < ti.length;i++) {
40021           //  Roo.log(['add child', items[i]]);
40022             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40023         }
40024         this.toolbar.items = nitems;
40025         
40026         delete config.toolbar;
40027     }
40028     
40029     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40030     config.grid.scrollBody = true;;
40031     config.grid.monitorWindowResize = false; // turn off autosizing
40032     config.grid.autoHeight = false;
40033     config.grid.autoWidth = false;
40034     
40035     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40036     
40037     if (config.background) {
40038         // render grid on panel activation (if panel background)
40039         this.on('activate', function(gp) {
40040             if (!gp.grid.rendered) {
40041                 gp.grid.render(this.wrapper);
40042                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40043             }
40044         });
40045             
40046     } else {
40047         this.grid.render(this.wrapper);
40048         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40049
40050     }
40051     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40052     // ??? needed ??? config.el = this.wrapper;
40053     
40054     
40055     
40056   
40057     // xtype created footer. - not sure if will work as we normally have to render first..
40058     if (this.footer && !this.footer.el && this.footer.xtype) {
40059         
40060         var ctr = this.grid.getView().getFooterPanel(true);
40061         this.footer.dataSource = this.grid.dataSource;
40062         this.footer = Roo.factory(this.footer, Roo);
40063         this.footer.render(ctr);
40064         
40065     }
40066     
40067     
40068     
40069     
40070      
40071 };
40072
40073 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40074     getId : function(){
40075         return this.grid.id;
40076     },
40077     
40078     /**
40079      * Returns the grid for this panel
40080      * @return {Roo.bootstrap.Table} 
40081      */
40082     getGrid : function(){
40083         return this.grid;    
40084     },
40085     
40086     setSize : function(width, height){
40087         if(!this.ignoreResize(width, height)){
40088             var grid = this.grid;
40089             var size = this.adjustForComponents(width, height);
40090             // tfoot is not a footer?
40091           
40092             
40093             var gridel = grid.getGridEl();
40094             gridel.setSize(size.width, size.height);
40095             
40096             var tbd = grid.getGridEl().select('tbody', true).first();
40097             var thd = grid.getGridEl().select('thead',true).first();
40098             var tbf= grid.getGridEl().select('tfoot', true).first();
40099
40100             if (tbf) {
40101                 size.height -= thd.getHeight();
40102             }
40103             if (thd) {
40104                 size.height -= thd.getHeight();
40105             }
40106             
40107             tbd.setSize(size.width, size.height );
40108             // this is for the account management tab -seems to work there.
40109             var thd = grid.getGridEl().select('thead',true).first();
40110             //if (tbd) {
40111             //    tbd.setSize(size.width, size.height - thd.getHeight());
40112             //}
40113              
40114             grid.autoSize();
40115         }
40116     },
40117      
40118     
40119     
40120     beforeSlide : function(){
40121         this.grid.getView().scroller.clip();
40122     },
40123     
40124     afterSlide : function(){
40125         this.grid.getView().scroller.unclip();
40126     },
40127     
40128     destroy : function(){
40129         this.grid.destroy();
40130         delete this.grid;
40131         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40132     }
40133 });
40134
40135 /**
40136  * @class Roo.bootstrap.panel.Nest
40137  * @extends Roo.bootstrap.panel.Content
40138  * @constructor
40139  * Create a new Panel, that can contain a layout.Border.
40140  * 
40141  * 
40142  * @param {Roo.BorderLayout} layout The layout for this panel
40143  * @param {String/Object} config A string to set only the title or a config object
40144  */
40145 Roo.bootstrap.panel.Nest = function(config)
40146 {
40147     // construct with only one argument..
40148     /* FIXME - implement nicer consturctors
40149     if (layout.layout) {
40150         config = layout;
40151         layout = config.layout;
40152         delete config.layout;
40153     }
40154     if (layout.xtype && !layout.getEl) {
40155         // then layout needs constructing..
40156         layout = Roo.factory(layout, Roo);
40157     }
40158     */
40159     
40160     config.el =  config.layout.getEl();
40161     
40162     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40163     
40164     config.layout.monitorWindowResize = false; // turn off autosizing
40165     this.layout = config.layout;
40166     this.layout.getEl().addClass("roo-layout-nested-layout");
40167     this.layout.parent = this;
40168     
40169     
40170     
40171     
40172 };
40173
40174 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40175
40176     setSize : function(width, height){
40177         if(!this.ignoreResize(width, height)){
40178             var size = this.adjustForComponents(width, height);
40179             var el = this.layout.getEl();
40180             if (size.height < 1) {
40181                 el.setWidth(size.width);   
40182             } else {
40183                 el.setSize(size.width, size.height);
40184             }
40185             var touch = el.dom.offsetWidth;
40186             this.layout.layout();
40187             // ie requires a double layout on the first pass
40188             if(Roo.isIE && !this.initialized){
40189                 this.initialized = true;
40190                 this.layout.layout();
40191             }
40192         }
40193     },
40194     
40195     // activate all subpanels if not currently active..
40196     
40197     setActiveState : function(active){
40198         this.active = active;
40199         this.setActiveClass(active);
40200         
40201         if(!active){
40202             this.fireEvent("deactivate", this);
40203             return;
40204         }
40205         
40206         this.fireEvent("activate", this);
40207         // not sure if this should happen before or after..
40208         if (!this.layout) {
40209             return; // should not happen..
40210         }
40211         var reg = false;
40212         for (var r in this.layout.regions) {
40213             reg = this.layout.getRegion(r);
40214             if (reg.getActivePanel()) {
40215                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40216                 reg.setActivePanel(reg.getActivePanel());
40217                 continue;
40218             }
40219             if (!reg.panels.length) {
40220                 continue;
40221             }
40222             reg.showPanel(reg.getPanel(0));
40223         }
40224         
40225         
40226         
40227         
40228     },
40229     
40230     /**
40231      * Returns the nested BorderLayout for this panel
40232      * @return {Roo.BorderLayout} 
40233      */
40234     getLayout : function(){
40235         return this.layout;
40236     },
40237     
40238      /**
40239      * Adds a xtype elements to the layout of the nested panel
40240      * <pre><code>
40241
40242 panel.addxtype({
40243        xtype : 'ContentPanel',
40244        region: 'west',
40245        items: [ .... ]
40246    }
40247 );
40248
40249 panel.addxtype({
40250         xtype : 'NestedLayoutPanel',
40251         region: 'west',
40252         layout: {
40253            center: { },
40254            west: { }   
40255         },
40256         items : [ ... list of content panels or nested layout panels.. ]
40257    }
40258 );
40259 </code></pre>
40260      * @param {Object} cfg Xtype definition of item to add.
40261      */
40262     addxtype : function(cfg) {
40263         return this.layout.addxtype(cfg);
40264     
40265     }
40266 });/*
40267  * Based on:
40268  * Ext JS Library 1.1.1
40269  * Copyright(c) 2006-2007, Ext JS, LLC.
40270  *
40271  * Originally Released Under LGPL - original licence link has changed is not relivant.
40272  *
40273  * Fork - LGPL
40274  * <script type="text/javascript">
40275  */
40276 /**
40277  * @class Roo.TabPanel
40278  * @extends Roo.util.Observable
40279  * A lightweight tab container.
40280  * <br><br>
40281  * Usage:
40282  * <pre><code>
40283 // basic tabs 1, built from existing content
40284 var tabs = new Roo.TabPanel("tabs1");
40285 tabs.addTab("script", "View Script");
40286 tabs.addTab("markup", "View Markup");
40287 tabs.activate("script");
40288
40289 // more advanced tabs, built from javascript
40290 var jtabs = new Roo.TabPanel("jtabs");
40291 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40292
40293 // set up the UpdateManager
40294 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40295 var updater = tab2.getUpdateManager();
40296 updater.setDefaultUrl("ajax1.htm");
40297 tab2.on('activate', updater.refresh, updater, true);
40298
40299 // Use setUrl for Ajax loading
40300 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40301 tab3.setUrl("ajax2.htm", null, true);
40302
40303 // Disabled tab
40304 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40305 tab4.disable();
40306
40307 jtabs.activate("jtabs-1");
40308  * </code></pre>
40309  * @constructor
40310  * Create a new TabPanel.
40311  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40312  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40313  */
40314 Roo.bootstrap.panel.Tabs = function(config){
40315     /**
40316     * The container element for this TabPanel.
40317     * @type Roo.Element
40318     */
40319     this.el = Roo.get(config.el);
40320     delete config.el;
40321     if(config){
40322         if(typeof config == "boolean"){
40323             this.tabPosition = config ? "bottom" : "top";
40324         }else{
40325             Roo.apply(this, config);
40326         }
40327     }
40328     
40329     if(this.tabPosition == "bottom"){
40330         // if tabs are at the bottom = create the body first.
40331         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40332         this.el.addClass("roo-tabs-bottom");
40333     }
40334     // next create the tabs holders
40335     
40336     if (this.tabPosition == "west"){
40337         
40338         var reg = this.region; // fake it..
40339         while (reg) {
40340             if (!reg.mgr.parent) {
40341                 break;
40342             }
40343             reg = reg.mgr.parent.region;
40344         }
40345         Roo.log("got nest?");
40346         Roo.log(reg);
40347         if (reg.mgr.getRegion('west')) {
40348             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40349             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40350             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40351             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40352             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40353         
40354             
40355         }
40356         
40357         
40358     } else {
40359      
40360         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40361         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40362         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40363         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40364     }
40365     
40366     
40367     if(Roo.isIE){
40368         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40369     }
40370     
40371     // finally - if tabs are at the top, then create the body last..
40372     if(this.tabPosition != "bottom"){
40373         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40374          * @type Roo.Element
40375          */
40376         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40377         this.el.addClass("roo-tabs-top");
40378     }
40379     this.items = [];
40380
40381     this.bodyEl.setStyle("position", "relative");
40382
40383     this.active = null;
40384     this.activateDelegate = this.activate.createDelegate(this);
40385
40386     this.addEvents({
40387         /**
40388          * @event tabchange
40389          * Fires when the active tab changes
40390          * @param {Roo.TabPanel} this
40391          * @param {Roo.TabPanelItem} activePanel The new active tab
40392          */
40393         "tabchange": true,
40394         /**
40395          * @event beforetabchange
40396          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40397          * @param {Roo.TabPanel} this
40398          * @param {Object} e Set cancel to true on this object to cancel the tab change
40399          * @param {Roo.TabPanelItem} tab The tab being changed to
40400          */
40401         "beforetabchange" : true
40402     });
40403
40404     Roo.EventManager.onWindowResize(this.onResize, this);
40405     this.cpad = this.el.getPadding("lr");
40406     this.hiddenCount = 0;
40407
40408
40409     // toolbar on the tabbar support...
40410     if (this.toolbar) {
40411         alert("no toolbar support yet");
40412         this.toolbar  = false;
40413         /*
40414         var tcfg = this.toolbar;
40415         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40416         this.toolbar = new Roo.Toolbar(tcfg);
40417         if (Roo.isSafari) {
40418             var tbl = tcfg.container.child('table', true);
40419             tbl.setAttribute('width', '100%');
40420         }
40421         */
40422         
40423     }
40424    
40425
40426
40427     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40428 };
40429
40430 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40431     /*
40432      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40433      */
40434     tabPosition : "top",
40435     /*
40436      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40437      */
40438     currentTabWidth : 0,
40439     /*
40440      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40441      */
40442     minTabWidth : 40,
40443     /*
40444      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40445      */
40446     maxTabWidth : 250,
40447     /*
40448      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40449      */
40450     preferredTabWidth : 175,
40451     /*
40452      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40453      */
40454     resizeTabs : false,
40455     /*
40456      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40457      */
40458     monitorResize : true,
40459     /*
40460      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40461      */
40462     toolbar : false,  // set by caller..
40463     
40464     region : false, /// set by caller
40465     
40466     disableTooltips : true, // not used yet...
40467
40468     /**
40469      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40470      * @param {String} id The id of the div to use <b>or create</b>
40471      * @param {String} text The text for the tab
40472      * @param {String} content (optional) Content to put in the TabPanelItem body
40473      * @param {Boolean} closable (optional) True to create a close icon on the tab
40474      * @return {Roo.TabPanelItem} The created TabPanelItem
40475      */
40476     addTab : function(id, text, content, closable, tpl)
40477     {
40478         var item = new Roo.bootstrap.panel.TabItem({
40479             panel: this,
40480             id : id,
40481             text : text,
40482             closable : closable,
40483             tpl : tpl
40484         });
40485         this.addTabItem(item);
40486         if(content){
40487             item.setContent(content);
40488         }
40489         return item;
40490     },
40491
40492     /**
40493      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40494      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40495      * @return {Roo.TabPanelItem}
40496      */
40497     getTab : function(id){
40498         return this.items[id];
40499     },
40500
40501     /**
40502      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40503      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40504      */
40505     hideTab : function(id){
40506         var t = this.items[id];
40507         if(!t.isHidden()){
40508            t.setHidden(true);
40509            this.hiddenCount++;
40510            this.autoSizeTabs();
40511         }
40512     },
40513
40514     /**
40515      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40516      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40517      */
40518     unhideTab : function(id){
40519         var t = this.items[id];
40520         if(t.isHidden()){
40521            t.setHidden(false);
40522            this.hiddenCount--;
40523            this.autoSizeTabs();
40524         }
40525     },
40526
40527     /**
40528      * Adds an existing {@link Roo.TabPanelItem}.
40529      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40530      */
40531     addTabItem : function(item)
40532     {
40533         this.items[item.id] = item;
40534         this.items.push(item);
40535         this.autoSizeTabs();
40536       //  if(this.resizeTabs){
40537     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40538   //         this.autoSizeTabs();
40539 //        }else{
40540 //            item.autoSize();
40541        // }
40542     },
40543
40544     /**
40545      * Removes a {@link Roo.TabPanelItem}.
40546      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40547      */
40548     removeTab : function(id){
40549         var items = this.items;
40550         var tab = items[id];
40551         if(!tab) { return; }
40552         var index = items.indexOf(tab);
40553         if(this.active == tab && items.length > 1){
40554             var newTab = this.getNextAvailable(index);
40555             if(newTab) {
40556                 newTab.activate();
40557             }
40558         }
40559         this.stripEl.dom.removeChild(tab.pnode.dom);
40560         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40561             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40562         }
40563         items.splice(index, 1);
40564         delete this.items[tab.id];
40565         tab.fireEvent("close", tab);
40566         tab.purgeListeners();
40567         this.autoSizeTabs();
40568     },
40569
40570     getNextAvailable : function(start){
40571         var items = this.items;
40572         var index = start;
40573         // look for a next tab that will slide over to
40574         // replace the one being removed
40575         while(index < items.length){
40576             var item = items[++index];
40577             if(item && !item.isHidden()){
40578                 return item;
40579             }
40580         }
40581         // if one isn't found select the previous tab (on the left)
40582         index = start;
40583         while(index >= 0){
40584             var item = items[--index];
40585             if(item && !item.isHidden()){
40586                 return item;
40587             }
40588         }
40589         return null;
40590     },
40591
40592     /**
40593      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40594      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40595      */
40596     disableTab : function(id){
40597         var tab = this.items[id];
40598         if(tab && this.active != tab){
40599             tab.disable();
40600         }
40601     },
40602
40603     /**
40604      * Enables a {@link Roo.TabPanelItem} that is disabled.
40605      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40606      */
40607     enableTab : function(id){
40608         var tab = this.items[id];
40609         tab.enable();
40610     },
40611
40612     /**
40613      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40614      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40615      * @return {Roo.TabPanelItem} The TabPanelItem.
40616      */
40617     activate : function(id)
40618     {
40619         //Roo.log('activite:'  + id);
40620         
40621         var tab = this.items[id];
40622         if(!tab){
40623             return null;
40624         }
40625         if(tab == this.active || tab.disabled){
40626             return tab;
40627         }
40628         var e = {};
40629         this.fireEvent("beforetabchange", this, e, tab);
40630         if(e.cancel !== true && !tab.disabled){
40631             if(this.active){
40632                 this.active.hide();
40633             }
40634             this.active = this.items[id];
40635             this.active.show();
40636             this.fireEvent("tabchange", this, this.active);
40637         }
40638         return tab;
40639     },
40640
40641     /**
40642      * Gets the active {@link Roo.TabPanelItem}.
40643      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40644      */
40645     getActiveTab : function(){
40646         return this.active;
40647     },
40648
40649     /**
40650      * Updates the tab body element to fit the height of the container element
40651      * for overflow scrolling
40652      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40653      */
40654     syncHeight : function(targetHeight){
40655         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40656         var bm = this.bodyEl.getMargins();
40657         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40658         this.bodyEl.setHeight(newHeight);
40659         return newHeight;
40660     },
40661
40662     onResize : function(){
40663         if(this.monitorResize){
40664             this.autoSizeTabs();
40665         }
40666     },
40667
40668     /**
40669      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40670      */
40671     beginUpdate : function(){
40672         this.updating = true;
40673     },
40674
40675     /**
40676      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40677      */
40678     endUpdate : function(){
40679         this.updating = false;
40680         this.autoSizeTabs();
40681     },
40682
40683     /**
40684      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40685      */
40686     autoSizeTabs : function()
40687     {
40688         var count = this.items.length;
40689         var vcount = count - this.hiddenCount;
40690         
40691         if (vcount < 2) {
40692             this.stripEl.hide();
40693         } else {
40694             this.stripEl.show();
40695         }
40696         
40697         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40698             return;
40699         }
40700         
40701         
40702         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40703         var availWidth = Math.floor(w / vcount);
40704         var b = this.stripBody;
40705         if(b.getWidth() > w){
40706             var tabs = this.items;
40707             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40708             if(availWidth < this.minTabWidth){
40709                 /*if(!this.sleft){    // incomplete scrolling code
40710                     this.createScrollButtons();
40711                 }
40712                 this.showScroll();
40713                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40714             }
40715         }else{
40716             if(this.currentTabWidth < this.preferredTabWidth){
40717                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40718             }
40719         }
40720     },
40721
40722     /**
40723      * Returns the number of tabs in this TabPanel.
40724      * @return {Number}
40725      */
40726      getCount : function(){
40727          return this.items.length;
40728      },
40729
40730     /**
40731      * Resizes all the tabs to the passed width
40732      * @param {Number} The new width
40733      */
40734     setTabWidth : function(width){
40735         this.currentTabWidth = width;
40736         for(var i = 0, len = this.items.length; i < len; i++) {
40737                 if(!this.items[i].isHidden()) {
40738                 this.items[i].setWidth(width);
40739             }
40740         }
40741     },
40742
40743     /**
40744      * Destroys this TabPanel
40745      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40746      */
40747     destroy : function(removeEl){
40748         Roo.EventManager.removeResizeListener(this.onResize, this);
40749         for(var i = 0, len = this.items.length; i < len; i++){
40750             this.items[i].purgeListeners();
40751         }
40752         if(removeEl === true){
40753             this.el.update("");
40754             this.el.remove();
40755         }
40756     },
40757     
40758     createStrip : function(container)
40759     {
40760         var strip = document.createElement("nav");
40761         strip.className = Roo.bootstrap.version == 4 ?
40762             "navbar-light bg-light" : 
40763             "navbar navbar-default"; //"x-tabs-wrap";
40764         container.appendChild(strip);
40765         return strip;
40766     },
40767     
40768     createStripList : function(strip)
40769     {
40770         // div wrapper for retard IE
40771         // returns the "tr" element.
40772         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40773         //'<div class="x-tabs-strip-wrap">'+
40774           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40775           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40776         return strip.firstChild; //.firstChild.firstChild.firstChild;
40777     },
40778     createBody : function(container)
40779     {
40780         var body = document.createElement("div");
40781         Roo.id(body, "tab-body");
40782         //Roo.fly(body).addClass("x-tabs-body");
40783         Roo.fly(body).addClass("tab-content");
40784         container.appendChild(body);
40785         return body;
40786     },
40787     createItemBody :function(bodyEl, id){
40788         var body = Roo.getDom(id);
40789         if(!body){
40790             body = document.createElement("div");
40791             body.id = id;
40792         }
40793         //Roo.fly(body).addClass("x-tabs-item-body");
40794         Roo.fly(body).addClass("tab-pane");
40795          bodyEl.insertBefore(body, bodyEl.firstChild);
40796         return body;
40797     },
40798     /** @private */
40799     createStripElements :  function(stripEl, text, closable, tpl)
40800     {
40801         var td = document.createElement("li"); // was td..
40802         td.className = 'nav-item';
40803         
40804         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40805         
40806         
40807         stripEl.appendChild(td);
40808         /*if(closable){
40809             td.className = "x-tabs-closable";
40810             if(!this.closeTpl){
40811                 this.closeTpl = new Roo.Template(
40812                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40813                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40814                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40815                 );
40816             }
40817             var el = this.closeTpl.overwrite(td, {"text": text});
40818             var close = el.getElementsByTagName("div")[0];
40819             var inner = el.getElementsByTagName("em")[0];
40820             return {"el": el, "close": close, "inner": inner};
40821         } else {
40822         */
40823         // not sure what this is..
40824 //            if(!this.tabTpl){
40825                 //this.tabTpl = new Roo.Template(
40826                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40827                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40828                 //);
40829 //                this.tabTpl = new Roo.Template(
40830 //                   '<a href="#">' +
40831 //                   '<span unselectable="on"' +
40832 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40833 //                            ' >{text}</span></a>'
40834 //                );
40835 //                
40836 //            }
40837
40838
40839             var template = tpl || this.tabTpl || false;
40840             
40841             if(!template){
40842                 template =  new Roo.Template(
40843                         Roo.bootstrap.version == 4 ? 
40844                             (
40845                                 '<a class="nav-link" href="#" unselectable="on"' +
40846                                      (this.disableTooltips ? '' : ' title="{text}"') +
40847                                      ' >{text}</a>'
40848                             ) : (
40849                                 '<a class="nav-link" href="#">' +
40850                                 '<span unselectable="on"' +
40851                                          (this.disableTooltips ? '' : ' title="{text}"') +
40852                                     ' >{text}</span></a>'
40853                             )
40854                 );
40855             }
40856             
40857             switch (typeof(template)) {
40858                 case 'object' :
40859                     break;
40860                 case 'string' :
40861                     template = new Roo.Template(template);
40862                     break;
40863                 default :
40864                     break;
40865             }
40866             
40867             var el = template.overwrite(td, {"text": text});
40868             
40869             var inner = el.getElementsByTagName("span")[0];
40870             
40871             return {"el": el, "inner": inner};
40872             
40873     }
40874         
40875     
40876 });
40877
40878 /**
40879  * @class Roo.TabPanelItem
40880  * @extends Roo.util.Observable
40881  * Represents an individual item (tab plus body) in a TabPanel.
40882  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40883  * @param {String} id The id of this TabPanelItem
40884  * @param {String} text The text for the tab of this TabPanelItem
40885  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40886  */
40887 Roo.bootstrap.panel.TabItem = function(config){
40888     /**
40889      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40890      * @type Roo.TabPanel
40891      */
40892     this.tabPanel = config.panel;
40893     /**
40894      * The id for this TabPanelItem
40895      * @type String
40896      */
40897     this.id = config.id;
40898     /** @private */
40899     this.disabled = false;
40900     /** @private */
40901     this.text = config.text;
40902     /** @private */
40903     this.loaded = false;
40904     this.closable = config.closable;
40905
40906     /**
40907      * The body element for this TabPanelItem.
40908      * @type Roo.Element
40909      */
40910     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40911     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40912     this.bodyEl.setStyle("display", "block");
40913     this.bodyEl.setStyle("zoom", "1");
40914     //this.hideAction();
40915
40916     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40917     /** @private */
40918     this.el = Roo.get(els.el);
40919     this.inner = Roo.get(els.inner, true);
40920      this.textEl = Roo.bootstrap.version == 4 ?
40921         this.el : Roo.get(this.el.dom.firstChild, true);
40922
40923     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40924     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40925
40926     
40927 //    this.el.on("mousedown", this.onTabMouseDown, this);
40928     this.el.on("click", this.onTabClick, this);
40929     /** @private */
40930     if(config.closable){
40931         var c = Roo.get(els.close, true);
40932         c.dom.title = this.closeText;
40933         c.addClassOnOver("close-over");
40934         c.on("click", this.closeClick, this);
40935      }
40936
40937     this.addEvents({
40938          /**
40939          * @event activate
40940          * Fires when this tab becomes the active tab.
40941          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40942          * @param {Roo.TabPanelItem} this
40943          */
40944         "activate": true,
40945         /**
40946          * @event beforeclose
40947          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40948          * @param {Roo.TabPanelItem} this
40949          * @param {Object} e Set cancel to true on this object to cancel the close.
40950          */
40951         "beforeclose": true,
40952         /**
40953          * @event close
40954          * Fires when this tab is closed.
40955          * @param {Roo.TabPanelItem} this
40956          */
40957          "close": true,
40958         /**
40959          * @event deactivate
40960          * Fires when this tab is no longer the active tab.
40961          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40962          * @param {Roo.TabPanelItem} this
40963          */
40964          "deactivate" : true
40965     });
40966     this.hidden = false;
40967
40968     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40969 };
40970
40971 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40972            {
40973     purgeListeners : function(){
40974        Roo.util.Observable.prototype.purgeListeners.call(this);
40975        this.el.removeAllListeners();
40976     },
40977     /**
40978      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40979      */
40980     show : function(){
40981         this.status_node.addClass("active");
40982         this.showAction();
40983         if(Roo.isOpera){
40984             this.tabPanel.stripWrap.repaint();
40985         }
40986         this.fireEvent("activate", this.tabPanel, this);
40987     },
40988
40989     /**
40990      * Returns true if this tab is the active tab.
40991      * @return {Boolean}
40992      */
40993     isActive : function(){
40994         return this.tabPanel.getActiveTab() == this;
40995     },
40996
40997     /**
40998      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40999      */
41000     hide : function(){
41001         this.status_node.removeClass("active");
41002         this.hideAction();
41003         this.fireEvent("deactivate", this.tabPanel, this);
41004     },
41005
41006     hideAction : function(){
41007         this.bodyEl.hide();
41008         this.bodyEl.setStyle("position", "absolute");
41009         this.bodyEl.setLeft("-20000px");
41010         this.bodyEl.setTop("-20000px");
41011     },
41012
41013     showAction : function(){
41014         this.bodyEl.setStyle("position", "relative");
41015         this.bodyEl.setTop("");
41016         this.bodyEl.setLeft("");
41017         this.bodyEl.show();
41018     },
41019
41020     /**
41021      * Set the tooltip for the tab.
41022      * @param {String} tooltip The tab's tooltip
41023      */
41024     setTooltip : function(text){
41025         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41026             this.textEl.dom.qtip = text;
41027             this.textEl.dom.removeAttribute('title');
41028         }else{
41029             this.textEl.dom.title = text;
41030         }
41031     },
41032
41033     onTabClick : function(e){
41034         e.preventDefault();
41035         this.tabPanel.activate(this.id);
41036     },
41037
41038     onTabMouseDown : function(e){
41039         e.preventDefault();
41040         this.tabPanel.activate(this.id);
41041     },
41042 /*
41043     getWidth : function(){
41044         return this.inner.getWidth();
41045     },
41046
41047     setWidth : function(width){
41048         var iwidth = width - this.linode.getPadding("lr");
41049         this.inner.setWidth(iwidth);
41050         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41051         this.linode.setWidth(width);
41052     },
41053 */
41054     /**
41055      * Show or hide the tab
41056      * @param {Boolean} hidden True to hide or false to show.
41057      */
41058     setHidden : function(hidden){
41059         this.hidden = hidden;
41060         this.linode.setStyle("display", hidden ? "none" : "");
41061     },
41062
41063     /**
41064      * Returns true if this tab is "hidden"
41065      * @return {Boolean}
41066      */
41067     isHidden : function(){
41068         return this.hidden;
41069     },
41070
41071     /**
41072      * Returns the text for this tab
41073      * @return {String}
41074      */
41075     getText : function(){
41076         return this.text;
41077     },
41078     /*
41079     autoSize : function(){
41080         //this.el.beginMeasure();
41081         this.textEl.setWidth(1);
41082         /*
41083          *  #2804 [new] Tabs in Roojs
41084          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41085          */
41086         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41087         //this.el.endMeasure();
41088     //},
41089
41090     /**
41091      * Sets the text for the tab (Note: this also sets the tooltip text)
41092      * @param {String} text The tab's text and tooltip
41093      */
41094     setText : function(text){
41095         this.text = text;
41096         this.textEl.update(text);
41097         this.setTooltip(text);
41098         //if(!this.tabPanel.resizeTabs){
41099         //    this.autoSize();
41100         //}
41101     },
41102     /**
41103      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41104      */
41105     activate : function(){
41106         this.tabPanel.activate(this.id);
41107     },
41108
41109     /**
41110      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41111      */
41112     disable : function(){
41113         if(this.tabPanel.active != this){
41114             this.disabled = true;
41115             this.status_node.addClass("disabled");
41116         }
41117     },
41118
41119     /**
41120      * Enables this TabPanelItem if it was previously disabled.
41121      */
41122     enable : function(){
41123         this.disabled = false;
41124         this.status_node.removeClass("disabled");
41125     },
41126
41127     /**
41128      * Sets the content for this TabPanelItem.
41129      * @param {String} content The content
41130      * @param {Boolean} loadScripts true to look for and load scripts
41131      */
41132     setContent : function(content, loadScripts){
41133         this.bodyEl.update(content, loadScripts);
41134     },
41135
41136     /**
41137      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41138      * @return {Roo.UpdateManager} The UpdateManager
41139      */
41140     getUpdateManager : function(){
41141         return this.bodyEl.getUpdateManager();
41142     },
41143
41144     /**
41145      * Set a URL to be used to load the content for this TabPanelItem.
41146      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41147      * @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)
41148      * @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)
41149      * @return {Roo.UpdateManager} The UpdateManager
41150      */
41151     setUrl : function(url, params, loadOnce){
41152         if(this.refreshDelegate){
41153             this.un('activate', this.refreshDelegate);
41154         }
41155         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41156         this.on("activate", this.refreshDelegate);
41157         return this.bodyEl.getUpdateManager();
41158     },
41159
41160     /** @private */
41161     _handleRefresh : function(url, params, loadOnce){
41162         if(!loadOnce || !this.loaded){
41163             var updater = this.bodyEl.getUpdateManager();
41164             updater.update(url, params, this._setLoaded.createDelegate(this));
41165         }
41166     },
41167
41168     /**
41169      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41170      *   Will fail silently if the setUrl method has not been called.
41171      *   This does not activate the panel, just updates its content.
41172      */
41173     refresh : function(){
41174         if(this.refreshDelegate){
41175            this.loaded = false;
41176            this.refreshDelegate();
41177         }
41178     },
41179
41180     /** @private */
41181     _setLoaded : function(){
41182         this.loaded = true;
41183     },
41184
41185     /** @private */
41186     closeClick : function(e){
41187         var o = {};
41188         e.stopEvent();
41189         this.fireEvent("beforeclose", this, o);
41190         if(o.cancel !== true){
41191             this.tabPanel.removeTab(this.id);
41192         }
41193     },
41194     /**
41195      * The text displayed in the tooltip for the close icon.
41196      * @type String
41197      */
41198     closeText : "Close this tab"
41199 });
41200 /**
41201 *    This script refer to:
41202 *    Title: International Telephone Input
41203 *    Author: Jack O'Connor
41204 *    Code version:  v12.1.12
41205 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41206 **/
41207
41208 Roo.bootstrap.PhoneInputData = function() {
41209     var d = [
41210       [
41211         "Afghanistan (‫افغانستان‬‎)",
41212         "af",
41213         "93"
41214       ],
41215       [
41216         "Albania (Shqipëri)",
41217         "al",
41218         "355"
41219       ],
41220       [
41221         "Algeria (‫الجزائر‬‎)",
41222         "dz",
41223         "213"
41224       ],
41225       [
41226         "American Samoa",
41227         "as",
41228         "1684"
41229       ],
41230       [
41231         "Andorra",
41232         "ad",
41233         "376"
41234       ],
41235       [
41236         "Angola",
41237         "ao",
41238         "244"
41239       ],
41240       [
41241         "Anguilla",
41242         "ai",
41243         "1264"
41244       ],
41245       [
41246         "Antigua and Barbuda",
41247         "ag",
41248         "1268"
41249       ],
41250       [
41251         "Argentina",
41252         "ar",
41253         "54"
41254       ],
41255       [
41256         "Armenia (Հայաստան)",
41257         "am",
41258         "374"
41259       ],
41260       [
41261         "Aruba",
41262         "aw",
41263         "297"
41264       ],
41265       [
41266         "Australia",
41267         "au",
41268         "61",
41269         0
41270       ],
41271       [
41272         "Austria (Österreich)",
41273         "at",
41274         "43"
41275       ],
41276       [
41277         "Azerbaijan (Azərbaycan)",
41278         "az",
41279         "994"
41280       ],
41281       [
41282         "Bahamas",
41283         "bs",
41284         "1242"
41285       ],
41286       [
41287         "Bahrain (‫البحرين‬‎)",
41288         "bh",
41289         "973"
41290       ],
41291       [
41292         "Bangladesh (বাংলাদেশ)",
41293         "bd",
41294         "880"
41295       ],
41296       [
41297         "Barbados",
41298         "bb",
41299         "1246"
41300       ],
41301       [
41302         "Belarus (Беларусь)",
41303         "by",
41304         "375"
41305       ],
41306       [
41307         "Belgium (België)",
41308         "be",
41309         "32"
41310       ],
41311       [
41312         "Belize",
41313         "bz",
41314         "501"
41315       ],
41316       [
41317         "Benin (Bénin)",
41318         "bj",
41319         "229"
41320       ],
41321       [
41322         "Bermuda",
41323         "bm",
41324         "1441"
41325       ],
41326       [
41327         "Bhutan (འབྲུག)",
41328         "bt",
41329         "975"
41330       ],
41331       [
41332         "Bolivia",
41333         "bo",
41334         "591"
41335       ],
41336       [
41337         "Bosnia and Herzegovina (Босна и Херцеговина)",
41338         "ba",
41339         "387"
41340       ],
41341       [
41342         "Botswana",
41343         "bw",
41344         "267"
41345       ],
41346       [
41347         "Brazil (Brasil)",
41348         "br",
41349         "55"
41350       ],
41351       [
41352         "British Indian Ocean Territory",
41353         "io",
41354         "246"
41355       ],
41356       [
41357         "British Virgin Islands",
41358         "vg",
41359         "1284"
41360       ],
41361       [
41362         "Brunei",
41363         "bn",
41364         "673"
41365       ],
41366       [
41367         "Bulgaria (България)",
41368         "bg",
41369         "359"
41370       ],
41371       [
41372         "Burkina Faso",
41373         "bf",
41374         "226"
41375       ],
41376       [
41377         "Burundi (Uburundi)",
41378         "bi",
41379         "257"
41380       ],
41381       [
41382         "Cambodia (កម្ពុជា)",
41383         "kh",
41384         "855"
41385       ],
41386       [
41387         "Cameroon (Cameroun)",
41388         "cm",
41389         "237"
41390       ],
41391       [
41392         "Canada",
41393         "ca",
41394         "1",
41395         1,
41396         ["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"]
41397       ],
41398       [
41399         "Cape Verde (Kabu Verdi)",
41400         "cv",
41401         "238"
41402       ],
41403       [
41404         "Caribbean Netherlands",
41405         "bq",
41406         "599",
41407         1
41408       ],
41409       [
41410         "Cayman Islands",
41411         "ky",
41412         "1345"
41413       ],
41414       [
41415         "Central African Republic (République centrafricaine)",
41416         "cf",
41417         "236"
41418       ],
41419       [
41420         "Chad (Tchad)",
41421         "td",
41422         "235"
41423       ],
41424       [
41425         "Chile",
41426         "cl",
41427         "56"
41428       ],
41429       [
41430         "China (中国)",
41431         "cn",
41432         "86"
41433       ],
41434       [
41435         "Christmas Island",
41436         "cx",
41437         "61",
41438         2
41439       ],
41440       [
41441         "Cocos (Keeling) Islands",
41442         "cc",
41443         "61",
41444         1
41445       ],
41446       [
41447         "Colombia",
41448         "co",
41449         "57"
41450       ],
41451       [
41452         "Comoros (‫جزر القمر‬‎)",
41453         "km",
41454         "269"
41455       ],
41456       [
41457         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41458         "cd",
41459         "243"
41460       ],
41461       [
41462         "Congo (Republic) (Congo-Brazzaville)",
41463         "cg",
41464         "242"
41465       ],
41466       [
41467         "Cook Islands",
41468         "ck",
41469         "682"
41470       ],
41471       [
41472         "Costa Rica",
41473         "cr",
41474         "506"
41475       ],
41476       [
41477         "Côte d’Ivoire",
41478         "ci",
41479         "225"
41480       ],
41481       [
41482         "Croatia (Hrvatska)",
41483         "hr",
41484         "385"
41485       ],
41486       [
41487         "Cuba",
41488         "cu",
41489         "53"
41490       ],
41491       [
41492         "Curaçao",
41493         "cw",
41494         "599",
41495         0
41496       ],
41497       [
41498         "Cyprus (Κύπρος)",
41499         "cy",
41500         "357"
41501       ],
41502       [
41503         "Czech Republic (Česká republika)",
41504         "cz",
41505         "420"
41506       ],
41507       [
41508         "Denmark (Danmark)",
41509         "dk",
41510         "45"
41511       ],
41512       [
41513         "Djibouti",
41514         "dj",
41515         "253"
41516       ],
41517       [
41518         "Dominica",
41519         "dm",
41520         "1767"
41521       ],
41522       [
41523         "Dominican Republic (República Dominicana)",
41524         "do",
41525         "1",
41526         2,
41527         ["809", "829", "849"]
41528       ],
41529       [
41530         "Ecuador",
41531         "ec",
41532         "593"
41533       ],
41534       [
41535         "Egypt (‫مصر‬‎)",
41536         "eg",
41537         "20"
41538       ],
41539       [
41540         "El Salvador",
41541         "sv",
41542         "503"
41543       ],
41544       [
41545         "Equatorial Guinea (Guinea Ecuatorial)",
41546         "gq",
41547         "240"
41548       ],
41549       [
41550         "Eritrea",
41551         "er",
41552         "291"
41553       ],
41554       [
41555         "Estonia (Eesti)",
41556         "ee",
41557         "372"
41558       ],
41559       [
41560         "Ethiopia",
41561         "et",
41562         "251"
41563       ],
41564       [
41565         "Falkland Islands (Islas Malvinas)",
41566         "fk",
41567         "500"
41568       ],
41569       [
41570         "Faroe Islands (Føroyar)",
41571         "fo",
41572         "298"
41573       ],
41574       [
41575         "Fiji",
41576         "fj",
41577         "679"
41578       ],
41579       [
41580         "Finland (Suomi)",
41581         "fi",
41582         "358",
41583         0
41584       ],
41585       [
41586         "France",
41587         "fr",
41588         "33"
41589       ],
41590       [
41591         "French Guiana (Guyane française)",
41592         "gf",
41593         "594"
41594       ],
41595       [
41596         "French Polynesia (Polynésie française)",
41597         "pf",
41598         "689"
41599       ],
41600       [
41601         "Gabon",
41602         "ga",
41603         "241"
41604       ],
41605       [
41606         "Gambia",
41607         "gm",
41608         "220"
41609       ],
41610       [
41611         "Georgia (საქართველო)",
41612         "ge",
41613         "995"
41614       ],
41615       [
41616         "Germany (Deutschland)",
41617         "de",
41618         "49"
41619       ],
41620       [
41621         "Ghana (Gaana)",
41622         "gh",
41623         "233"
41624       ],
41625       [
41626         "Gibraltar",
41627         "gi",
41628         "350"
41629       ],
41630       [
41631         "Greece (Ελλάδα)",
41632         "gr",
41633         "30"
41634       ],
41635       [
41636         "Greenland (Kalaallit Nunaat)",
41637         "gl",
41638         "299"
41639       ],
41640       [
41641         "Grenada",
41642         "gd",
41643         "1473"
41644       ],
41645       [
41646         "Guadeloupe",
41647         "gp",
41648         "590",
41649         0
41650       ],
41651       [
41652         "Guam",
41653         "gu",
41654         "1671"
41655       ],
41656       [
41657         "Guatemala",
41658         "gt",
41659         "502"
41660       ],
41661       [
41662         "Guernsey",
41663         "gg",
41664         "44",
41665         1
41666       ],
41667       [
41668         "Guinea (Guinée)",
41669         "gn",
41670         "224"
41671       ],
41672       [
41673         "Guinea-Bissau (Guiné Bissau)",
41674         "gw",
41675         "245"
41676       ],
41677       [
41678         "Guyana",
41679         "gy",
41680         "592"
41681       ],
41682       [
41683         "Haiti",
41684         "ht",
41685         "509"
41686       ],
41687       [
41688         "Honduras",
41689         "hn",
41690         "504"
41691       ],
41692       [
41693         "Hong Kong (香港)",
41694         "hk",
41695         "852"
41696       ],
41697       [
41698         "Hungary (Magyarország)",
41699         "hu",
41700         "36"
41701       ],
41702       [
41703         "Iceland (Ísland)",
41704         "is",
41705         "354"
41706       ],
41707       [
41708         "India (भारत)",
41709         "in",
41710         "91"
41711       ],
41712       [
41713         "Indonesia",
41714         "id",
41715         "62"
41716       ],
41717       [
41718         "Iran (‫ایران‬‎)",
41719         "ir",
41720         "98"
41721       ],
41722       [
41723         "Iraq (‫العراق‬‎)",
41724         "iq",
41725         "964"
41726       ],
41727       [
41728         "Ireland",
41729         "ie",
41730         "353"
41731       ],
41732       [
41733         "Isle of Man",
41734         "im",
41735         "44",
41736         2
41737       ],
41738       [
41739         "Israel (‫ישראל‬‎)",
41740         "il",
41741         "972"
41742       ],
41743       [
41744         "Italy (Italia)",
41745         "it",
41746         "39",
41747         0
41748       ],
41749       [
41750         "Jamaica",
41751         "jm",
41752         "1876"
41753       ],
41754       [
41755         "Japan (日本)",
41756         "jp",
41757         "81"
41758       ],
41759       [
41760         "Jersey",
41761         "je",
41762         "44",
41763         3
41764       ],
41765       [
41766         "Jordan (‫الأردن‬‎)",
41767         "jo",
41768         "962"
41769       ],
41770       [
41771         "Kazakhstan (Казахстан)",
41772         "kz",
41773         "7",
41774         1
41775       ],
41776       [
41777         "Kenya",
41778         "ke",
41779         "254"
41780       ],
41781       [
41782         "Kiribati",
41783         "ki",
41784         "686"
41785       ],
41786       [
41787         "Kosovo",
41788         "xk",
41789         "383"
41790       ],
41791       [
41792         "Kuwait (‫الكويت‬‎)",
41793         "kw",
41794         "965"
41795       ],
41796       [
41797         "Kyrgyzstan (Кыргызстан)",
41798         "kg",
41799         "996"
41800       ],
41801       [
41802         "Laos (ລາວ)",
41803         "la",
41804         "856"
41805       ],
41806       [
41807         "Latvia (Latvija)",
41808         "lv",
41809         "371"
41810       ],
41811       [
41812         "Lebanon (‫لبنان‬‎)",
41813         "lb",
41814         "961"
41815       ],
41816       [
41817         "Lesotho",
41818         "ls",
41819         "266"
41820       ],
41821       [
41822         "Liberia",
41823         "lr",
41824         "231"
41825       ],
41826       [
41827         "Libya (‫ليبيا‬‎)",
41828         "ly",
41829         "218"
41830       ],
41831       [
41832         "Liechtenstein",
41833         "li",
41834         "423"
41835       ],
41836       [
41837         "Lithuania (Lietuva)",
41838         "lt",
41839         "370"
41840       ],
41841       [
41842         "Luxembourg",
41843         "lu",
41844         "352"
41845       ],
41846       [
41847         "Macau (澳門)",
41848         "mo",
41849         "853"
41850       ],
41851       [
41852         "Macedonia (FYROM) (Македонија)",
41853         "mk",
41854         "389"
41855       ],
41856       [
41857         "Madagascar (Madagasikara)",
41858         "mg",
41859         "261"
41860       ],
41861       [
41862         "Malawi",
41863         "mw",
41864         "265"
41865       ],
41866       [
41867         "Malaysia",
41868         "my",
41869         "60"
41870       ],
41871       [
41872         "Maldives",
41873         "mv",
41874         "960"
41875       ],
41876       [
41877         "Mali",
41878         "ml",
41879         "223"
41880       ],
41881       [
41882         "Malta",
41883         "mt",
41884         "356"
41885       ],
41886       [
41887         "Marshall Islands",
41888         "mh",
41889         "692"
41890       ],
41891       [
41892         "Martinique",
41893         "mq",
41894         "596"
41895       ],
41896       [
41897         "Mauritania (‫موريتانيا‬‎)",
41898         "mr",
41899         "222"
41900       ],
41901       [
41902         "Mauritius (Moris)",
41903         "mu",
41904         "230"
41905       ],
41906       [
41907         "Mayotte",
41908         "yt",
41909         "262",
41910         1
41911       ],
41912       [
41913         "Mexico (México)",
41914         "mx",
41915         "52"
41916       ],
41917       [
41918         "Micronesia",
41919         "fm",
41920         "691"
41921       ],
41922       [
41923         "Moldova (Republica Moldova)",
41924         "md",
41925         "373"
41926       ],
41927       [
41928         "Monaco",
41929         "mc",
41930         "377"
41931       ],
41932       [
41933         "Mongolia (Монгол)",
41934         "mn",
41935         "976"
41936       ],
41937       [
41938         "Montenegro (Crna Gora)",
41939         "me",
41940         "382"
41941       ],
41942       [
41943         "Montserrat",
41944         "ms",
41945         "1664"
41946       ],
41947       [
41948         "Morocco (‫المغرب‬‎)",
41949         "ma",
41950         "212",
41951         0
41952       ],
41953       [
41954         "Mozambique (Moçambique)",
41955         "mz",
41956         "258"
41957       ],
41958       [
41959         "Myanmar (Burma) (မြန်မာ)",
41960         "mm",
41961         "95"
41962       ],
41963       [
41964         "Namibia (Namibië)",
41965         "na",
41966         "264"
41967       ],
41968       [
41969         "Nauru",
41970         "nr",
41971         "674"
41972       ],
41973       [
41974         "Nepal (नेपाल)",
41975         "np",
41976         "977"
41977       ],
41978       [
41979         "Netherlands (Nederland)",
41980         "nl",
41981         "31"
41982       ],
41983       [
41984         "New Caledonia (Nouvelle-Calédonie)",
41985         "nc",
41986         "687"
41987       ],
41988       [
41989         "New Zealand",
41990         "nz",
41991         "64"
41992       ],
41993       [
41994         "Nicaragua",
41995         "ni",
41996         "505"
41997       ],
41998       [
41999         "Niger (Nijar)",
42000         "ne",
42001         "227"
42002       ],
42003       [
42004         "Nigeria",
42005         "ng",
42006         "234"
42007       ],
42008       [
42009         "Niue",
42010         "nu",
42011         "683"
42012       ],
42013       [
42014         "Norfolk Island",
42015         "nf",
42016         "672"
42017       ],
42018       [
42019         "North Korea (조선 민주주의 인민 공화국)",
42020         "kp",
42021         "850"
42022       ],
42023       [
42024         "Northern Mariana Islands",
42025         "mp",
42026         "1670"
42027       ],
42028       [
42029         "Norway (Norge)",
42030         "no",
42031         "47",
42032         0
42033       ],
42034       [
42035         "Oman (‫عُمان‬‎)",
42036         "om",
42037         "968"
42038       ],
42039       [
42040         "Pakistan (‫پاکستان‬‎)",
42041         "pk",
42042         "92"
42043       ],
42044       [
42045         "Palau",
42046         "pw",
42047         "680"
42048       ],
42049       [
42050         "Palestine (‫فلسطين‬‎)",
42051         "ps",
42052         "970"
42053       ],
42054       [
42055         "Panama (Panamá)",
42056         "pa",
42057         "507"
42058       ],
42059       [
42060         "Papua New Guinea",
42061         "pg",
42062         "675"
42063       ],
42064       [
42065         "Paraguay",
42066         "py",
42067         "595"
42068       ],
42069       [
42070         "Peru (Perú)",
42071         "pe",
42072         "51"
42073       ],
42074       [
42075         "Philippines",
42076         "ph",
42077         "63"
42078       ],
42079       [
42080         "Poland (Polska)",
42081         "pl",
42082         "48"
42083       ],
42084       [
42085         "Portugal",
42086         "pt",
42087         "351"
42088       ],
42089       [
42090         "Puerto Rico",
42091         "pr",
42092         "1",
42093         3,
42094         ["787", "939"]
42095       ],
42096       [
42097         "Qatar (‫قطر‬‎)",
42098         "qa",
42099         "974"
42100       ],
42101       [
42102         "Réunion (La Réunion)",
42103         "re",
42104         "262",
42105         0
42106       ],
42107       [
42108         "Romania (România)",
42109         "ro",
42110         "40"
42111       ],
42112       [
42113         "Russia (Россия)",
42114         "ru",
42115         "7",
42116         0
42117       ],
42118       [
42119         "Rwanda",
42120         "rw",
42121         "250"
42122       ],
42123       [
42124         "Saint Barthélemy",
42125         "bl",
42126         "590",
42127         1
42128       ],
42129       [
42130         "Saint Helena",
42131         "sh",
42132         "290"
42133       ],
42134       [
42135         "Saint Kitts and Nevis",
42136         "kn",
42137         "1869"
42138       ],
42139       [
42140         "Saint Lucia",
42141         "lc",
42142         "1758"
42143       ],
42144       [
42145         "Saint Martin (Saint-Martin (partie française))",
42146         "mf",
42147         "590",
42148         2
42149       ],
42150       [
42151         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42152         "pm",
42153         "508"
42154       ],
42155       [
42156         "Saint Vincent and the Grenadines",
42157         "vc",
42158         "1784"
42159       ],
42160       [
42161         "Samoa",
42162         "ws",
42163         "685"
42164       ],
42165       [
42166         "San Marino",
42167         "sm",
42168         "378"
42169       ],
42170       [
42171         "São Tomé and Príncipe (São Tomé e Príncipe)",
42172         "st",
42173         "239"
42174       ],
42175       [
42176         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42177         "sa",
42178         "966"
42179       ],
42180       [
42181         "Senegal (Sénégal)",
42182         "sn",
42183         "221"
42184       ],
42185       [
42186         "Serbia (Србија)",
42187         "rs",
42188         "381"
42189       ],
42190       [
42191         "Seychelles",
42192         "sc",
42193         "248"
42194       ],
42195       [
42196         "Sierra Leone",
42197         "sl",
42198         "232"
42199       ],
42200       [
42201         "Singapore",
42202         "sg",
42203         "65"
42204       ],
42205       [
42206         "Sint Maarten",
42207         "sx",
42208         "1721"
42209       ],
42210       [
42211         "Slovakia (Slovensko)",
42212         "sk",
42213         "421"
42214       ],
42215       [
42216         "Slovenia (Slovenija)",
42217         "si",
42218         "386"
42219       ],
42220       [
42221         "Solomon Islands",
42222         "sb",
42223         "677"
42224       ],
42225       [
42226         "Somalia (Soomaaliya)",
42227         "so",
42228         "252"
42229       ],
42230       [
42231         "South Africa",
42232         "za",
42233         "27"
42234       ],
42235       [
42236         "South Korea (대한민국)",
42237         "kr",
42238         "82"
42239       ],
42240       [
42241         "South Sudan (‫جنوب السودان‬‎)",
42242         "ss",
42243         "211"
42244       ],
42245       [
42246         "Spain (España)",
42247         "es",
42248         "34"
42249       ],
42250       [
42251         "Sri Lanka (ශ්‍රී ලංකාව)",
42252         "lk",
42253         "94"
42254       ],
42255       [
42256         "Sudan (‫السودان‬‎)",
42257         "sd",
42258         "249"
42259       ],
42260       [
42261         "Suriname",
42262         "sr",
42263         "597"
42264       ],
42265       [
42266         "Svalbard and Jan Mayen",
42267         "sj",
42268         "47",
42269         1
42270       ],
42271       [
42272         "Swaziland",
42273         "sz",
42274         "268"
42275       ],
42276       [
42277         "Sweden (Sverige)",
42278         "se",
42279         "46"
42280       ],
42281       [
42282         "Switzerland (Schweiz)",
42283         "ch",
42284         "41"
42285       ],
42286       [
42287         "Syria (‫سوريا‬‎)",
42288         "sy",
42289         "963"
42290       ],
42291       [
42292         "Taiwan (台灣)",
42293         "tw",
42294         "886"
42295       ],
42296       [
42297         "Tajikistan",
42298         "tj",
42299         "992"
42300       ],
42301       [
42302         "Tanzania",
42303         "tz",
42304         "255"
42305       ],
42306       [
42307         "Thailand (ไทย)",
42308         "th",
42309         "66"
42310       ],
42311       [
42312         "Timor-Leste",
42313         "tl",
42314         "670"
42315       ],
42316       [
42317         "Togo",
42318         "tg",
42319         "228"
42320       ],
42321       [
42322         "Tokelau",
42323         "tk",
42324         "690"
42325       ],
42326       [
42327         "Tonga",
42328         "to",
42329         "676"
42330       ],
42331       [
42332         "Trinidad and Tobago",
42333         "tt",
42334         "1868"
42335       ],
42336       [
42337         "Tunisia (‫تونس‬‎)",
42338         "tn",
42339         "216"
42340       ],
42341       [
42342         "Turkey (Türkiye)",
42343         "tr",
42344         "90"
42345       ],
42346       [
42347         "Turkmenistan",
42348         "tm",
42349         "993"
42350       ],
42351       [
42352         "Turks and Caicos Islands",
42353         "tc",
42354         "1649"
42355       ],
42356       [
42357         "Tuvalu",
42358         "tv",
42359         "688"
42360       ],
42361       [
42362         "U.S. Virgin Islands",
42363         "vi",
42364         "1340"
42365       ],
42366       [
42367         "Uganda",
42368         "ug",
42369         "256"
42370       ],
42371       [
42372         "Ukraine (Україна)",
42373         "ua",
42374         "380"
42375       ],
42376       [
42377         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42378         "ae",
42379         "971"
42380       ],
42381       [
42382         "United Kingdom",
42383         "gb",
42384         "44",
42385         0
42386       ],
42387       [
42388         "United States",
42389         "us",
42390         "1",
42391         0
42392       ],
42393       [
42394         "Uruguay",
42395         "uy",
42396         "598"
42397       ],
42398       [
42399         "Uzbekistan (Oʻzbekiston)",
42400         "uz",
42401         "998"
42402       ],
42403       [
42404         "Vanuatu",
42405         "vu",
42406         "678"
42407       ],
42408       [
42409         "Vatican City (Città del Vaticano)",
42410         "va",
42411         "39",
42412         1
42413       ],
42414       [
42415         "Venezuela",
42416         "ve",
42417         "58"
42418       ],
42419       [
42420         "Vietnam (Việt Nam)",
42421         "vn",
42422         "84"
42423       ],
42424       [
42425         "Wallis and Futuna (Wallis-et-Futuna)",
42426         "wf",
42427         "681"
42428       ],
42429       [
42430         "Western Sahara (‫الصحراء الغربية‬‎)",
42431         "eh",
42432         "212",
42433         1
42434       ],
42435       [
42436         "Yemen (‫اليمن‬‎)",
42437         "ye",
42438         "967"
42439       ],
42440       [
42441         "Zambia",
42442         "zm",
42443         "260"
42444       ],
42445       [
42446         "Zimbabwe",
42447         "zw",
42448         "263"
42449       ],
42450       [
42451         "Åland Islands",
42452         "ax",
42453         "358",
42454         1
42455       ]
42456   ];
42457   
42458   return d;
42459 }/**
42460 *    This script refer to:
42461 *    Title: International Telephone Input
42462 *    Author: Jack O'Connor
42463 *    Code version:  v12.1.12
42464 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42465 **/
42466
42467 /**
42468  * @class Roo.bootstrap.PhoneInput
42469  * @extends Roo.bootstrap.TriggerField
42470  * An input with International dial-code selection
42471  
42472  * @cfg {String} defaultDialCode default '+852'
42473  * @cfg {Array} preferedCountries default []
42474   
42475  * @constructor
42476  * Create a new PhoneInput.
42477  * @param {Object} config Configuration options
42478  */
42479
42480 Roo.bootstrap.PhoneInput = function(config) {
42481     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42482 };
42483
42484 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42485         
42486         listWidth: undefined,
42487         
42488         selectedClass: 'active',
42489         
42490         invalidClass : "has-warning",
42491         
42492         validClass: 'has-success',
42493         
42494         allowed: '0123456789',
42495         
42496         max_length: 15,
42497         
42498         /**
42499          * @cfg {String} defaultDialCode The default dial code when initializing the input
42500          */
42501         defaultDialCode: '+852',
42502         
42503         /**
42504          * @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
42505          */
42506         preferedCountries: false,
42507         
42508         getAutoCreate : function()
42509         {
42510             var data = Roo.bootstrap.PhoneInputData();
42511             var align = this.labelAlign || this.parentLabelAlign();
42512             var id = Roo.id();
42513             
42514             this.allCountries = [];
42515             this.dialCodeMapping = [];
42516             
42517             for (var i = 0; i < data.length; i++) {
42518               var c = data[i];
42519               this.allCountries[i] = {
42520                 name: c[0],
42521                 iso2: c[1],
42522                 dialCode: c[2],
42523                 priority: c[3] || 0,
42524                 areaCodes: c[4] || null
42525               };
42526               this.dialCodeMapping[c[2]] = {
42527                   name: c[0],
42528                   iso2: c[1],
42529                   priority: c[3] || 0,
42530                   areaCodes: c[4] || null
42531               };
42532             }
42533             
42534             var cfg = {
42535                 cls: 'form-group',
42536                 cn: []
42537             };
42538             
42539             var input =  {
42540                 tag: 'input',
42541                 id : id,
42542                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42543                 maxlength: this.max_length,
42544                 cls : 'form-control tel-input',
42545                 autocomplete: 'new-password'
42546             };
42547             
42548             var hiddenInput = {
42549                 tag: 'input',
42550                 type: 'hidden',
42551                 cls: 'hidden-tel-input'
42552             };
42553             
42554             if (this.name) {
42555                 hiddenInput.name = this.name;
42556             }
42557             
42558             if (this.disabled) {
42559                 input.disabled = true;
42560             }
42561             
42562             var flag_container = {
42563                 tag: 'div',
42564                 cls: 'flag-box',
42565                 cn: [
42566                     {
42567                         tag: 'div',
42568                         cls: 'flag'
42569                     },
42570                     {
42571                         tag: 'div',
42572                         cls: 'caret'
42573                     }
42574                 ]
42575             };
42576             
42577             var box = {
42578                 tag: 'div',
42579                 cls: this.hasFeedback ? 'has-feedback' : '',
42580                 cn: [
42581                     hiddenInput,
42582                     input,
42583                     {
42584                         tag: 'input',
42585                         cls: 'dial-code-holder',
42586                         disabled: true
42587                     }
42588                 ]
42589             };
42590             
42591             var container = {
42592                 cls: 'roo-select2-container input-group',
42593                 cn: [
42594                     flag_container,
42595                     box
42596                 ]
42597             };
42598             
42599             if (this.fieldLabel.length) {
42600                 var indicator = {
42601                     tag: 'i',
42602                     tooltip: 'This field is required'
42603                 };
42604                 
42605                 var label = {
42606                     tag: 'label',
42607                     'for':  id,
42608                     cls: 'control-label',
42609                     cn: []
42610                 };
42611                 
42612                 var label_text = {
42613                     tag: 'span',
42614                     html: this.fieldLabel
42615                 };
42616                 
42617                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42618                 label.cn = [
42619                     indicator,
42620                     label_text
42621                 ];
42622                 
42623                 if(this.indicatorpos == 'right') {
42624                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42625                     label.cn = [
42626                         label_text,
42627                         indicator
42628                     ];
42629                 }
42630                 
42631                 if(align == 'left') {
42632                     container = {
42633                         tag: 'div',
42634                         cn: [
42635                             container
42636                         ]
42637                     };
42638                     
42639                     if(this.labelWidth > 12){
42640                         label.style = "width: " + this.labelWidth + 'px';
42641                     }
42642                     if(this.labelWidth < 13 && this.labelmd == 0){
42643                         this.labelmd = this.labelWidth;
42644                     }
42645                     if(this.labellg > 0){
42646                         label.cls += ' col-lg-' + this.labellg;
42647                         input.cls += ' col-lg-' + (12 - this.labellg);
42648                     }
42649                     if(this.labelmd > 0){
42650                         label.cls += ' col-md-' + this.labelmd;
42651                         container.cls += ' col-md-' + (12 - this.labelmd);
42652                     }
42653                     if(this.labelsm > 0){
42654                         label.cls += ' col-sm-' + this.labelsm;
42655                         container.cls += ' col-sm-' + (12 - this.labelsm);
42656                     }
42657                     if(this.labelxs > 0){
42658                         label.cls += ' col-xs-' + this.labelxs;
42659                         container.cls += ' col-xs-' + (12 - this.labelxs);
42660                     }
42661                 }
42662             }
42663             
42664             cfg.cn = [
42665                 label,
42666                 container
42667             ];
42668             
42669             var settings = this;
42670             
42671             ['xs','sm','md','lg'].map(function(size){
42672                 if (settings[size]) {
42673                     cfg.cls += ' col-' + size + '-' + settings[size];
42674                 }
42675             });
42676             
42677             this.store = new Roo.data.Store({
42678                 proxy : new Roo.data.MemoryProxy({}),
42679                 reader : new Roo.data.JsonReader({
42680                     fields : [
42681                         {
42682                             'name' : 'name',
42683                             'type' : 'string'
42684                         },
42685                         {
42686                             'name' : 'iso2',
42687                             'type' : 'string'
42688                         },
42689                         {
42690                             'name' : 'dialCode',
42691                             'type' : 'string'
42692                         },
42693                         {
42694                             'name' : 'priority',
42695                             'type' : 'string'
42696                         },
42697                         {
42698                             'name' : 'areaCodes',
42699                             'type' : 'string'
42700                         }
42701                     ]
42702                 })
42703             });
42704             
42705             if(!this.preferedCountries) {
42706                 this.preferedCountries = [
42707                     'hk',
42708                     'gb',
42709                     'us'
42710                 ];
42711             }
42712             
42713             var p = this.preferedCountries.reverse();
42714             
42715             if(p) {
42716                 for (var i = 0; i < p.length; i++) {
42717                     for (var j = 0; j < this.allCountries.length; j++) {
42718                         if(this.allCountries[j].iso2 == p[i]) {
42719                             var t = this.allCountries[j];
42720                             this.allCountries.splice(j,1);
42721                             this.allCountries.unshift(t);
42722                         }
42723                     } 
42724                 }
42725             }
42726             
42727             this.store.proxy.data = {
42728                 success: true,
42729                 data: this.allCountries
42730             };
42731             
42732             return cfg;
42733         },
42734         
42735         initEvents : function()
42736         {
42737             this.createList();
42738             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42739             
42740             this.indicator = this.indicatorEl();
42741             this.flag = this.flagEl();
42742             this.dialCodeHolder = this.dialCodeHolderEl();
42743             
42744             this.trigger = this.el.select('div.flag-box',true).first();
42745             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42746             
42747             var _this = this;
42748             
42749             (function(){
42750                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42751                 _this.list.setWidth(lw);
42752             }).defer(100);
42753             
42754             this.list.on('mouseover', this.onViewOver, this);
42755             this.list.on('mousemove', this.onViewMove, this);
42756             this.inputEl().on("keyup", this.onKeyUp, this);
42757             this.inputEl().on("keypress", this.onKeyPress, this);
42758             
42759             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42760
42761             this.view = new Roo.View(this.list, this.tpl, {
42762                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42763             });
42764             
42765             this.view.on('click', this.onViewClick, this);
42766             this.setValue(this.defaultDialCode);
42767         },
42768         
42769         onTriggerClick : function(e)
42770         {
42771             Roo.log('trigger click');
42772             if(this.disabled){
42773                 return;
42774             }
42775             
42776             if(this.isExpanded()){
42777                 this.collapse();
42778                 this.hasFocus = false;
42779             }else {
42780                 this.store.load({});
42781                 this.hasFocus = true;
42782                 this.expand();
42783             }
42784         },
42785         
42786         isExpanded : function()
42787         {
42788             return this.list.isVisible();
42789         },
42790         
42791         collapse : function()
42792         {
42793             if(!this.isExpanded()){
42794                 return;
42795             }
42796             this.list.hide();
42797             Roo.get(document).un('mousedown', this.collapseIf, this);
42798             Roo.get(document).un('mousewheel', this.collapseIf, this);
42799             this.fireEvent('collapse', this);
42800             this.validate();
42801         },
42802         
42803         expand : function()
42804         {
42805             Roo.log('expand');
42806
42807             if(this.isExpanded() || !this.hasFocus){
42808                 return;
42809             }
42810             
42811             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42812             this.list.setWidth(lw);
42813             
42814             this.list.show();
42815             this.restrictHeight();
42816             
42817             Roo.get(document).on('mousedown', this.collapseIf, this);
42818             Roo.get(document).on('mousewheel', this.collapseIf, this);
42819             
42820             this.fireEvent('expand', this);
42821         },
42822         
42823         restrictHeight : function()
42824         {
42825             this.list.alignTo(this.inputEl(), this.listAlign);
42826             this.list.alignTo(this.inputEl(), this.listAlign);
42827         },
42828         
42829         onViewOver : function(e, t)
42830         {
42831             if(this.inKeyMode){
42832                 return;
42833             }
42834             var item = this.view.findItemFromChild(t);
42835             
42836             if(item){
42837                 var index = this.view.indexOf(item);
42838                 this.select(index, false);
42839             }
42840         },
42841
42842         // private
42843         onViewClick : function(view, doFocus, el, e)
42844         {
42845             var index = this.view.getSelectedIndexes()[0];
42846             
42847             var r = this.store.getAt(index);
42848             
42849             if(r){
42850                 this.onSelect(r, index);
42851             }
42852             if(doFocus !== false && !this.blockFocus){
42853                 this.inputEl().focus();
42854             }
42855         },
42856         
42857         onViewMove : function(e, t)
42858         {
42859             this.inKeyMode = false;
42860         },
42861         
42862         select : function(index, scrollIntoView)
42863         {
42864             this.selectedIndex = index;
42865             this.view.select(index);
42866             if(scrollIntoView !== false){
42867                 var el = this.view.getNode(index);
42868                 if(el){
42869                     this.list.scrollChildIntoView(el, false);
42870                 }
42871             }
42872         },
42873         
42874         createList : function()
42875         {
42876             this.list = Roo.get(document.body).createChild({
42877                 tag: 'ul',
42878                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42879                 style: 'display:none'
42880             });
42881             
42882             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42883         },
42884         
42885         collapseIf : function(e)
42886         {
42887             var in_combo  = e.within(this.el);
42888             var in_list =  e.within(this.list);
42889             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42890             
42891             if (in_combo || in_list || is_list) {
42892                 return;
42893             }
42894             this.collapse();
42895         },
42896         
42897         onSelect : function(record, index)
42898         {
42899             if(this.fireEvent('beforeselect', this, record, index) !== false){
42900                 
42901                 this.setFlagClass(record.data.iso2);
42902                 this.setDialCode(record.data.dialCode);
42903                 this.hasFocus = false;
42904                 this.collapse();
42905                 this.fireEvent('select', this, record, index);
42906             }
42907         },
42908         
42909         flagEl : function()
42910         {
42911             var flag = this.el.select('div.flag',true).first();
42912             if(!flag){
42913                 return false;
42914             }
42915             return flag;
42916         },
42917         
42918         dialCodeHolderEl : function()
42919         {
42920             var d = this.el.select('input.dial-code-holder',true).first();
42921             if(!d){
42922                 return false;
42923             }
42924             return d;
42925         },
42926         
42927         setDialCode : function(v)
42928         {
42929             this.dialCodeHolder.dom.value = '+'+v;
42930         },
42931         
42932         setFlagClass : function(n)
42933         {
42934             this.flag.dom.className = 'flag '+n;
42935         },
42936         
42937         getValue : function()
42938         {
42939             var v = this.inputEl().getValue();
42940             if(this.dialCodeHolder) {
42941                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42942             }
42943             return v;
42944         },
42945         
42946         setValue : function(v)
42947         {
42948             var d = this.getDialCode(v);
42949             
42950             //invalid dial code
42951             if(v.length == 0 || !d || d.length == 0) {
42952                 if(this.rendered){
42953                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42954                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42955                 }
42956                 return;
42957             }
42958             
42959             //valid dial code
42960             this.setFlagClass(this.dialCodeMapping[d].iso2);
42961             this.setDialCode(d);
42962             this.inputEl().dom.value = v.replace('+'+d,'');
42963             this.hiddenEl().dom.value = this.getValue();
42964             
42965             this.validate();
42966         },
42967         
42968         getDialCode : function(v)
42969         {
42970             v = v ||  '';
42971             
42972             if (v.length == 0) {
42973                 return this.dialCodeHolder.dom.value;
42974             }
42975             
42976             var dialCode = "";
42977             if (v.charAt(0) != "+") {
42978                 return false;
42979             }
42980             var numericChars = "";
42981             for (var i = 1; i < v.length; i++) {
42982               var c = v.charAt(i);
42983               if (!isNaN(c)) {
42984                 numericChars += c;
42985                 if (this.dialCodeMapping[numericChars]) {
42986                   dialCode = v.substr(1, i);
42987                 }
42988                 if (numericChars.length == 4) {
42989                   break;
42990                 }
42991               }
42992             }
42993             return dialCode;
42994         },
42995         
42996         reset : function()
42997         {
42998             this.setValue(this.defaultDialCode);
42999             this.validate();
43000         },
43001         
43002         hiddenEl : function()
43003         {
43004             return this.el.select('input.hidden-tel-input',true).first();
43005         },
43006         
43007         // after setting val
43008         onKeyUp : function(e){
43009             this.setValue(this.getValue());
43010         },
43011         
43012         onKeyPress : function(e){
43013             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43014                 e.stopEvent();
43015             }
43016         }
43017         
43018 });
43019 /**
43020  * @class Roo.bootstrap.MoneyField
43021  * @extends Roo.bootstrap.ComboBox
43022  * Bootstrap MoneyField class
43023  * 
43024  * @constructor
43025  * Create a new MoneyField.
43026  * @param {Object} config Configuration options
43027  */
43028
43029 Roo.bootstrap.MoneyField = function(config) {
43030     
43031     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43032     
43033 };
43034
43035 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43036     
43037     /**
43038      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43039      */
43040     allowDecimals : true,
43041     /**
43042      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43043      */
43044     decimalSeparator : ".",
43045     /**
43046      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43047      */
43048     decimalPrecision : 0,
43049     /**
43050      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43051      */
43052     allowNegative : true,
43053     /**
43054      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43055      */
43056     allowZero: true,
43057     /**
43058      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43059      */
43060     minValue : Number.NEGATIVE_INFINITY,
43061     /**
43062      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43063      */
43064     maxValue : Number.MAX_VALUE,
43065     /**
43066      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43067      */
43068     minText : "The minimum value for this field is {0}",
43069     /**
43070      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43071      */
43072     maxText : "The maximum value for this field is {0}",
43073     /**
43074      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43075      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43076      */
43077     nanText : "{0} is not a valid number",
43078     /**
43079      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43080      */
43081     castInt : true,
43082     /**
43083      * @cfg {String} defaults currency of the MoneyField
43084      * value should be in lkey
43085      */
43086     defaultCurrency : false,
43087     /**
43088      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43089      */
43090     thousandsDelimiter : false,
43091     /**
43092      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43093      */
43094     max_length: false,
43095     
43096     inputlg : 9,
43097     inputmd : 9,
43098     inputsm : 9,
43099     inputxs : 6,
43100     
43101     store : false,
43102     
43103     getAutoCreate : function()
43104     {
43105         var align = this.labelAlign || this.parentLabelAlign();
43106         
43107         var id = Roo.id();
43108
43109         var cfg = {
43110             cls: 'form-group',
43111             cn: []
43112         };
43113
43114         var input =  {
43115             tag: 'input',
43116             id : id,
43117             cls : 'form-control roo-money-amount-input',
43118             autocomplete: 'new-password'
43119         };
43120         
43121         var hiddenInput = {
43122             tag: 'input',
43123             type: 'hidden',
43124             id: Roo.id(),
43125             cls: 'hidden-number-input'
43126         };
43127         
43128         if(this.max_length) {
43129             input.maxlength = this.max_length; 
43130         }
43131         
43132         if (this.name) {
43133             hiddenInput.name = this.name;
43134         }
43135
43136         if (this.disabled) {
43137             input.disabled = true;
43138         }
43139
43140         var clg = 12 - this.inputlg;
43141         var cmd = 12 - this.inputmd;
43142         var csm = 12 - this.inputsm;
43143         var cxs = 12 - this.inputxs;
43144         
43145         var container = {
43146             tag : 'div',
43147             cls : 'row roo-money-field',
43148             cn : [
43149                 {
43150                     tag : 'div',
43151                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43152                     cn : [
43153                         {
43154                             tag : 'div',
43155                             cls: 'roo-select2-container input-group',
43156                             cn: [
43157                                 {
43158                                     tag : 'input',
43159                                     cls : 'form-control roo-money-currency-input',
43160                                     autocomplete: 'new-password',
43161                                     readOnly : 1,
43162                                     name : this.currencyName
43163                                 },
43164                                 {
43165                                     tag :'span',
43166                                     cls : 'input-group-addon',
43167                                     cn : [
43168                                         {
43169                                             tag: 'span',
43170                                             cls: 'caret'
43171                                         }
43172                                     ]
43173                                 }
43174                             ]
43175                         }
43176                     ]
43177                 },
43178                 {
43179                     tag : 'div',
43180                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43181                     cn : [
43182                         {
43183                             tag: 'div',
43184                             cls: this.hasFeedback ? 'has-feedback' : '',
43185                             cn: [
43186                                 input
43187                             ]
43188                         }
43189                     ]
43190                 }
43191             ]
43192             
43193         };
43194         
43195         if (this.fieldLabel.length) {
43196             var indicator = {
43197                 tag: 'i',
43198                 tooltip: 'This field is required'
43199             };
43200
43201             var label = {
43202                 tag: 'label',
43203                 'for':  id,
43204                 cls: 'control-label',
43205                 cn: []
43206             };
43207
43208             var label_text = {
43209                 tag: 'span',
43210                 html: this.fieldLabel
43211             };
43212
43213             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43214             label.cn = [
43215                 indicator,
43216                 label_text
43217             ];
43218
43219             if(this.indicatorpos == 'right') {
43220                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43221                 label.cn = [
43222                     label_text,
43223                     indicator
43224                 ];
43225             }
43226
43227             if(align == 'left') {
43228                 container = {
43229                     tag: 'div',
43230                     cn: [
43231                         container
43232                     ]
43233                 };
43234
43235                 if(this.labelWidth > 12){
43236                     label.style = "width: " + this.labelWidth + 'px';
43237                 }
43238                 if(this.labelWidth < 13 && this.labelmd == 0){
43239                     this.labelmd = this.labelWidth;
43240                 }
43241                 if(this.labellg > 0){
43242                     label.cls += ' col-lg-' + this.labellg;
43243                     input.cls += ' col-lg-' + (12 - this.labellg);
43244                 }
43245                 if(this.labelmd > 0){
43246                     label.cls += ' col-md-' + this.labelmd;
43247                     container.cls += ' col-md-' + (12 - this.labelmd);
43248                 }
43249                 if(this.labelsm > 0){
43250                     label.cls += ' col-sm-' + this.labelsm;
43251                     container.cls += ' col-sm-' + (12 - this.labelsm);
43252                 }
43253                 if(this.labelxs > 0){
43254                     label.cls += ' col-xs-' + this.labelxs;
43255                     container.cls += ' col-xs-' + (12 - this.labelxs);
43256                 }
43257             }
43258         }
43259
43260         cfg.cn = [
43261             label,
43262             container,
43263             hiddenInput
43264         ];
43265         
43266         var settings = this;
43267
43268         ['xs','sm','md','lg'].map(function(size){
43269             if (settings[size]) {
43270                 cfg.cls += ' col-' + size + '-' + settings[size];
43271             }
43272         });
43273         
43274         return cfg;
43275     },
43276     
43277     initEvents : function()
43278     {
43279         this.indicator = this.indicatorEl();
43280         
43281         this.initCurrencyEvent();
43282         
43283         this.initNumberEvent();
43284     },
43285     
43286     initCurrencyEvent : function()
43287     {
43288         if (!this.store) {
43289             throw "can not find store for combo";
43290         }
43291         
43292         this.store = Roo.factory(this.store, Roo.data);
43293         this.store.parent = this;
43294         
43295         this.createList();
43296         
43297         this.triggerEl = this.el.select('.input-group-addon', true).first();
43298         
43299         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43300         
43301         var _this = this;
43302         
43303         (function(){
43304             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43305             _this.list.setWidth(lw);
43306         }).defer(100);
43307         
43308         this.list.on('mouseover', this.onViewOver, this);
43309         this.list.on('mousemove', this.onViewMove, this);
43310         this.list.on('scroll', this.onViewScroll, this);
43311         
43312         if(!this.tpl){
43313             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43314         }
43315         
43316         this.view = new Roo.View(this.list, this.tpl, {
43317             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43318         });
43319         
43320         this.view.on('click', this.onViewClick, this);
43321         
43322         this.store.on('beforeload', this.onBeforeLoad, this);
43323         this.store.on('load', this.onLoad, this);
43324         this.store.on('loadexception', this.onLoadException, this);
43325         
43326         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43327             "up" : function(e){
43328                 this.inKeyMode = true;
43329                 this.selectPrev();
43330             },
43331
43332             "down" : function(e){
43333                 if(!this.isExpanded()){
43334                     this.onTriggerClick();
43335                 }else{
43336                     this.inKeyMode = true;
43337                     this.selectNext();
43338                 }
43339             },
43340
43341             "enter" : function(e){
43342                 this.collapse();
43343                 
43344                 if(this.fireEvent("specialkey", this, e)){
43345                     this.onViewClick(false);
43346                 }
43347                 
43348                 return true;
43349             },
43350
43351             "esc" : function(e){
43352                 this.collapse();
43353             },
43354
43355             "tab" : function(e){
43356                 this.collapse();
43357                 
43358                 if(this.fireEvent("specialkey", this, e)){
43359                     this.onViewClick(false);
43360                 }
43361                 
43362                 return true;
43363             },
43364
43365             scope : this,
43366
43367             doRelay : function(foo, bar, hname){
43368                 if(hname == 'down' || this.scope.isExpanded()){
43369                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43370                 }
43371                 return true;
43372             },
43373
43374             forceKeyDown: true
43375         });
43376         
43377         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43378         
43379     },
43380     
43381     initNumberEvent : function(e)
43382     {
43383         this.inputEl().on("keydown" , this.fireKey,  this);
43384         this.inputEl().on("focus", this.onFocus,  this);
43385         this.inputEl().on("blur", this.onBlur,  this);
43386         
43387         this.inputEl().relayEvent('keyup', this);
43388         
43389         if(this.indicator){
43390             this.indicator.addClass('invisible');
43391         }
43392  
43393         this.originalValue = this.getValue();
43394         
43395         if(this.validationEvent == 'keyup'){
43396             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43397             this.inputEl().on('keyup', this.filterValidation, this);
43398         }
43399         else if(this.validationEvent !== false){
43400             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43401         }
43402         
43403         if(this.selectOnFocus){
43404             this.on("focus", this.preFocus, this);
43405             
43406         }
43407         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43408             this.inputEl().on("keypress", this.filterKeys, this);
43409         } else {
43410             this.inputEl().relayEvent('keypress', this);
43411         }
43412         
43413         var allowed = "0123456789";
43414         
43415         if(this.allowDecimals){
43416             allowed += this.decimalSeparator;
43417         }
43418         
43419         if(this.allowNegative){
43420             allowed += "-";
43421         }
43422         
43423         if(this.thousandsDelimiter) {
43424             allowed += ",";
43425         }
43426         
43427         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43428         
43429         var keyPress = function(e){
43430             
43431             var k = e.getKey();
43432             
43433             var c = e.getCharCode();
43434             
43435             if(
43436                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43437                     allowed.indexOf(String.fromCharCode(c)) === -1
43438             ){
43439                 e.stopEvent();
43440                 return;
43441             }
43442             
43443             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43444                 return;
43445             }
43446             
43447             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43448                 e.stopEvent();
43449             }
43450         };
43451         
43452         this.inputEl().on("keypress", keyPress, this);
43453         
43454     },
43455     
43456     onTriggerClick : function(e)
43457     {   
43458         if(this.disabled){
43459             return;
43460         }
43461         
43462         this.page = 0;
43463         this.loadNext = false;
43464         
43465         if(this.isExpanded()){
43466             this.collapse();
43467             return;
43468         }
43469         
43470         this.hasFocus = true;
43471         
43472         if(this.triggerAction == 'all') {
43473             this.doQuery(this.allQuery, true);
43474             return;
43475         }
43476         
43477         this.doQuery(this.getRawValue());
43478     },
43479     
43480     getCurrency : function()
43481     {   
43482         var v = this.currencyEl().getValue();
43483         
43484         return v;
43485     },
43486     
43487     restrictHeight : function()
43488     {
43489         this.list.alignTo(this.currencyEl(), this.listAlign);
43490         this.list.alignTo(this.currencyEl(), this.listAlign);
43491     },
43492     
43493     onViewClick : function(view, doFocus, el, e)
43494     {
43495         var index = this.view.getSelectedIndexes()[0];
43496         
43497         var r = this.store.getAt(index);
43498         
43499         if(r){
43500             this.onSelect(r, index);
43501         }
43502     },
43503     
43504     onSelect : function(record, index){
43505         
43506         if(this.fireEvent('beforeselect', this, record, index) !== false){
43507         
43508             this.setFromCurrencyData(index > -1 ? record.data : false);
43509             
43510             this.collapse();
43511             
43512             this.fireEvent('select', this, record, index);
43513         }
43514     },
43515     
43516     setFromCurrencyData : function(o)
43517     {
43518         var currency = '';
43519         
43520         this.lastCurrency = o;
43521         
43522         if (this.currencyField) {
43523             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43524         } else {
43525             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43526         }
43527         
43528         this.lastSelectionText = currency;
43529         
43530         //setting default currency
43531         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43532             this.setCurrency(this.defaultCurrency);
43533             return;
43534         }
43535         
43536         this.setCurrency(currency);
43537     },
43538     
43539     setFromData : function(o)
43540     {
43541         var c = {};
43542         
43543         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43544         
43545         this.setFromCurrencyData(c);
43546         
43547         var value = '';
43548         
43549         if (this.name) {
43550             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43551         } else {
43552             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43553         }
43554         
43555         this.setValue(value);
43556         
43557     },
43558     
43559     setCurrency : function(v)
43560     {   
43561         this.currencyValue = v;
43562         
43563         if(this.rendered){
43564             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43565             this.validate();
43566         }
43567     },
43568     
43569     setValue : function(v)
43570     {
43571         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43572         
43573         this.value = v;
43574         
43575         if(this.rendered){
43576             
43577             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43578             
43579             this.inputEl().dom.value = (v == '') ? '' :
43580                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43581             
43582             if(!this.allowZero && v === '0') {
43583                 this.hiddenEl().dom.value = '';
43584                 this.inputEl().dom.value = '';
43585             }
43586             
43587             this.validate();
43588         }
43589     },
43590     
43591     getRawValue : function()
43592     {
43593         var v = this.inputEl().getValue();
43594         
43595         return v;
43596     },
43597     
43598     getValue : function()
43599     {
43600         return this.fixPrecision(this.parseValue(this.getRawValue()));
43601     },
43602     
43603     parseValue : function(value)
43604     {
43605         if(this.thousandsDelimiter) {
43606             value += "";
43607             r = new RegExp(",", "g");
43608             value = value.replace(r, "");
43609         }
43610         
43611         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43612         return isNaN(value) ? '' : value;
43613         
43614     },
43615     
43616     fixPrecision : function(value)
43617     {
43618         if(this.thousandsDelimiter) {
43619             value += "";
43620             r = new RegExp(",", "g");
43621             value = value.replace(r, "");
43622         }
43623         
43624         var nan = isNaN(value);
43625         
43626         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43627             return nan ? '' : value;
43628         }
43629         return parseFloat(value).toFixed(this.decimalPrecision);
43630     },
43631     
43632     decimalPrecisionFcn : function(v)
43633     {
43634         return Math.floor(v);
43635     },
43636     
43637     validateValue : function(value)
43638     {
43639         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43640             return false;
43641         }
43642         
43643         var num = this.parseValue(value);
43644         
43645         if(isNaN(num)){
43646             this.markInvalid(String.format(this.nanText, value));
43647             return false;
43648         }
43649         
43650         if(num < this.minValue){
43651             this.markInvalid(String.format(this.minText, this.minValue));
43652             return false;
43653         }
43654         
43655         if(num > this.maxValue){
43656             this.markInvalid(String.format(this.maxText, this.maxValue));
43657             return false;
43658         }
43659         
43660         return true;
43661     },
43662     
43663     validate : function()
43664     {
43665         if(this.disabled || this.allowBlank){
43666             this.markValid();
43667             return true;
43668         }
43669         
43670         var currency = this.getCurrency();
43671         
43672         if(this.validateValue(this.getRawValue()) && currency.length){
43673             this.markValid();
43674             return true;
43675         }
43676         
43677         this.markInvalid();
43678         return false;
43679     },
43680     
43681     getName: function()
43682     {
43683         return this.name;
43684     },
43685     
43686     beforeBlur : function()
43687     {
43688         if(!this.castInt){
43689             return;
43690         }
43691         
43692         var v = this.parseValue(this.getRawValue());
43693         
43694         if(v || v == 0){
43695             this.setValue(v);
43696         }
43697     },
43698     
43699     onBlur : function()
43700     {
43701         this.beforeBlur();
43702         
43703         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43704             //this.el.removeClass(this.focusClass);
43705         }
43706         
43707         this.hasFocus = false;
43708         
43709         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43710             this.validate();
43711         }
43712         
43713         var v = this.getValue();
43714         
43715         if(String(v) !== String(this.startValue)){
43716             this.fireEvent('change', this, v, this.startValue);
43717         }
43718         
43719         this.fireEvent("blur", this);
43720     },
43721     
43722     inputEl : function()
43723     {
43724         return this.el.select('.roo-money-amount-input', true).first();
43725     },
43726     
43727     currencyEl : function()
43728     {
43729         return this.el.select('.roo-money-currency-input', true).first();
43730     },
43731     
43732     hiddenEl : function()
43733     {
43734         return this.el.select('input.hidden-number-input',true).first();
43735     }
43736     
43737 });/**
43738  * @class Roo.bootstrap.BezierSignature
43739  * @extends Roo.bootstrap.Component
43740  * Bootstrap BezierSignature class
43741  * This script refer to:
43742  *    Title: Signature Pad
43743  *    Author: szimek
43744  *    Availability: https://github.com/szimek/signature_pad
43745  *
43746  * @constructor
43747  * Create a new BezierSignature
43748  * @param {Object} config The config object
43749  */
43750
43751 Roo.bootstrap.BezierSignature = function(config){
43752     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43753     this.addEvents({
43754         "resize" : true
43755     });
43756 };
43757
43758 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43759 {
43760      
43761     curve_data: [],
43762     
43763     is_empty: true,
43764     
43765     mouse_btn_down: true,
43766     
43767     /**
43768      * @cfg {int} canvas height
43769      */
43770     canvas_height: '200px',
43771     
43772     /**
43773      * @cfg {float|function} Radius of a single dot.
43774      */ 
43775     dot_size: false,
43776     
43777     /**
43778      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43779      */
43780     min_width: 0.5,
43781     
43782     /**
43783      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43784      */
43785     max_width: 2.5,
43786     
43787     /**
43788      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43789      */
43790     throttle: 16,
43791     
43792     /**
43793      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43794      */
43795     min_distance: 5,
43796     
43797     /**
43798      * @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.
43799      */
43800     bg_color: 'rgba(0, 0, 0, 0)',
43801     
43802     /**
43803      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43804      */
43805     dot_color: 'black',
43806     
43807     /**
43808      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43809      */ 
43810     velocity_filter_weight: 0.7,
43811     
43812     /**
43813      * @cfg {function} Callback when stroke begin. 
43814      */
43815     onBegin: false,
43816     
43817     /**
43818      * @cfg {function} Callback when stroke end.
43819      */
43820     onEnd: false,
43821     
43822     getAutoCreate : function()
43823     {
43824         var cls = 'roo-signature column';
43825         
43826         if(this.cls){
43827             cls += ' ' + this.cls;
43828         }
43829         
43830         var col_sizes = [
43831             'lg',
43832             'md',
43833             'sm',
43834             'xs'
43835         ];
43836         
43837         for(var i = 0; i < col_sizes.length; i++) {
43838             if(this[col_sizes[i]]) {
43839                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43840             }
43841         }
43842         
43843         var cfg = {
43844             tag: 'div',
43845             cls: cls,
43846             cn: [
43847                 {
43848                     tag: 'div',
43849                     cls: 'roo-signature-body',
43850                     cn: [
43851                         {
43852                             tag: 'canvas',
43853                             cls: 'roo-signature-body-canvas',
43854                             height: this.canvas_height,
43855                             width: this.canvas_width
43856                         }
43857                     ]
43858                 },
43859                 {
43860                     tag: 'input',
43861                     type: 'file',
43862                     style: 'display: none'
43863                 }
43864             ]
43865         };
43866         
43867         return cfg;
43868     },
43869     
43870     initEvents: function() 
43871     {
43872         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43873         
43874         var canvas = this.canvasEl();
43875         
43876         // mouse && touch event swapping...
43877         canvas.dom.style.touchAction = 'none';
43878         canvas.dom.style.msTouchAction = 'none';
43879         
43880         this.mouse_btn_down = false;
43881         canvas.on('mousedown', this._handleMouseDown, this);
43882         canvas.on('mousemove', this._handleMouseMove, this);
43883         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43884         
43885         if (window.PointerEvent) {
43886             canvas.on('pointerdown', this._handleMouseDown, this);
43887             canvas.on('pointermove', this._handleMouseMove, this);
43888             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43889         }
43890         
43891         if ('ontouchstart' in window) {
43892             canvas.on('touchstart', this._handleTouchStart, this);
43893             canvas.on('touchmove', this._handleTouchMove, this);
43894             canvas.on('touchend', this._handleTouchEnd, this);
43895         }
43896         
43897         Roo.EventManager.onWindowResize(this.resize, this, true);
43898         
43899         // file input event
43900         this.fileEl().on('change', this.uploadImage, this);
43901         
43902         this.clear();
43903         
43904         this.resize();
43905     },
43906     
43907     resize: function(){
43908         
43909         var canvas = this.canvasEl().dom;
43910         var ctx = this.canvasElCtx();
43911         var img_data = false;
43912         
43913         if(canvas.width > 0) {
43914             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43915         }
43916         // setting canvas width will clean img data
43917         canvas.width = 0;
43918         
43919         var style = window.getComputedStyle ? 
43920             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43921             
43922         var padding_left = parseInt(style.paddingLeft) || 0;
43923         var padding_right = parseInt(style.paddingRight) || 0;
43924         
43925         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43926         
43927         if(img_data) {
43928             ctx.putImageData(img_data, 0, 0);
43929         }
43930     },
43931     
43932     _handleMouseDown: function(e)
43933     {
43934         if (e.browserEvent.which === 1) {
43935             this.mouse_btn_down = true;
43936             this.strokeBegin(e);
43937         }
43938     },
43939     
43940     _handleMouseMove: function (e)
43941     {
43942         if (this.mouse_btn_down) {
43943             this.strokeMoveUpdate(e);
43944         }
43945     },
43946     
43947     _handleMouseUp: function (e)
43948     {
43949         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43950             this.mouse_btn_down = false;
43951             this.strokeEnd(e);
43952         }
43953     },
43954     
43955     _handleTouchStart: function (e) {
43956         
43957         e.preventDefault();
43958         if (e.browserEvent.targetTouches.length === 1) {
43959             // var touch = e.browserEvent.changedTouches[0];
43960             // this.strokeBegin(touch);
43961             
43962              this.strokeBegin(e); // assume e catching the correct xy...
43963         }
43964     },
43965     
43966     _handleTouchMove: function (e) {
43967         e.preventDefault();
43968         // var touch = event.targetTouches[0];
43969         // _this._strokeMoveUpdate(touch);
43970         this.strokeMoveUpdate(e);
43971     },
43972     
43973     _handleTouchEnd: function (e) {
43974         var wasCanvasTouched = e.target === this.canvasEl().dom;
43975         if (wasCanvasTouched) {
43976             e.preventDefault();
43977             // var touch = event.changedTouches[0];
43978             // _this._strokeEnd(touch);
43979             this.strokeEnd(e);
43980         }
43981     },
43982     
43983     reset: function () {
43984         this._lastPoints = [];
43985         this._lastVelocity = 0;
43986         this._lastWidth = (this.min_width + this.max_width) / 2;
43987         this.canvasElCtx().fillStyle = this.dot_color;
43988     },
43989     
43990     strokeMoveUpdate: function(e)
43991     {
43992         this.strokeUpdate(e);
43993         
43994         if (this.throttle) {
43995             this.throttleStroke(this.strokeUpdate, this.throttle);
43996         }
43997         else {
43998             this.strokeUpdate(e);
43999         }
44000     },
44001     
44002     strokeBegin: function(e)
44003     {
44004         var newPointGroup = {
44005             color: this.dot_color,
44006             points: []
44007         };
44008         
44009         if (typeof this.onBegin === 'function') {
44010             this.onBegin(e);
44011         }
44012         
44013         this.curve_data.push(newPointGroup);
44014         this.reset();
44015         this.strokeUpdate(e);
44016     },
44017     
44018     strokeUpdate: function(e)
44019     {
44020         var rect = this.canvasEl().dom.getBoundingClientRect();
44021         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44022         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44023         var lastPoints = lastPointGroup.points;
44024         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44025         var isLastPointTooClose = lastPoint
44026             ? point.distanceTo(lastPoint) <= this.min_distance
44027             : false;
44028         var color = lastPointGroup.color;
44029         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44030             var curve = this.addPoint(point);
44031             if (!lastPoint) {
44032                 this.drawDot({color: color, point: point});
44033             }
44034             else if (curve) {
44035                 this.drawCurve({color: color, curve: curve});
44036             }
44037             lastPoints.push({
44038                 time: point.time,
44039                 x: point.x,
44040                 y: point.y
44041             });
44042         }
44043     },
44044     
44045     strokeEnd: function(e)
44046     {
44047         this.strokeUpdate(e);
44048         if (typeof this.onEnd === 'function') {
44049             this.onEnd(e);
44050         }
44051     },
44052     
44053     addPoint:  function (point) {
44054         var _lastPoints = this._lastPoints;
44055         _lastPoints.push(point);
44056         if (_lastPoints.length > 2) {
44057             if (_lastPoints.length === 3) {
44058                 _lastPoints.unshift(_lastPoints[0]);
44059             }
44060             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44061             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44062             _lastPoints.shift();
44063             return curve;
44064         }
44065         return null;
44066     },
44067     
44068     calculateCurveWidths: function (startPoint, endPoint) {
44069         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44070             (1 - this.velocity_filter_weight) * this._lastVelocity;
44071
44072         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44073         var widths = {
44074             end: newWidth,
44075             start: this._lastWidth
44076         };
44077         
44078         this._lastVelocity = velocity;
44079         this._lastWidth = newWidth;
44080         return widths;
44081     },
44082     
44083     drawDot: function (_a) {
44084         var color = _a.color, point = _a.point;
44085         var ctx = this.canvasElCtx();
44086         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44087         ctx.beginPath();
44088         this.drawCurveSegment(point.x, point.y, width);
44089         ctx.closePath();
44090         ctx.fillStyle = color;
44091         ctx.fill();
44092     },
44093     
44094     drawCurve: function (_a) {
44095         var color = _a.color, curve = _a.curve;
44096         var ctx = this.canvasElCtx();
44097         var widthDelta = curve.endWidth - curve.startWidth;
44098         var drawSteps = Math.floor(curve.length()) * 2;
44099         ctx.beginPath();
44100         ctx.fillStyle = color;
44101         for (var i = 0; i < drawSteps; i += 1) {
44102         var t = i / drawSteps;
44103         var tt = t * t;
44104         var ttt = tt * t;
44105         var u = 1 - t;
44106         var uu = u * u;
44107         var uuu = uu * u;
44108         var x = uuu * curve.startPoint.x;
44109         x += 3 * uu * t * curve.control1.x;
44110         x += 3 * u * tt * curve.control2.x;
44111         x += ttt * curve.endPoint.x;
44112         var y = uuu * curve.startPoint.y;
44113         y += 3 * uu * t * curve.control1.y;
44114         y += 3 * u * tt * curve.control2.y;
44115         y += ttt * curve.endPoint.y;
44116         var width = curve.startWidth + ttt * widthDelta;
44117         this.drawCurveSegment(x, y, width);
44118         }
44119         ctx.closePath();
44120         ctx.fill();
44121     },
44122     
44123     drawCurveSegment: function (x, y, width) {
44124         var ctx = this.canvasElCtx();
44125         ctx.moveTo(x, y);
44126         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44127         this.is_empty = false;
44128     },
44129     
44130     clear: function()
44131     {
44132         var ctx = this.canvasElCtx();
44133         var canvas = this.canvasEl().dom;
44134         ctx.fillStyle = this.bg_color;
44135         ctx.clearRect(0, 0, canvas.width, canvas.height);
44136         ctx.fillRect(0, 0, canvas.width, canvas.height);
44137         this.curve_data = [];
44138         this.reset();
44139         this.is_empty = true;
44140     },
44141     
44142     fileEl: function()
44143     {
44144         return  this.el.select('input',true).first();
44145     },
44146     
44147     canvasEl: function()
44148     {
44149         return this.el.select('canvas',true).first();
44150     },
44151     
44152     canvasElCtx: function()
44153     {
44154         return this.el.select('canvas',true).first().dom.getContext('2d');
44155     },
44156     
44157     getImage: function(type)
44158     {
44159         if(this.is_empty) {
44160             return false;
44161         }
44162         
44163         // encryption ?
44164         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44165     },
44166     
44167     drawFromImage: function(img_src)
44168     {
44169         var img = new Image();
44170         
44171         img.onload = function(){
44172             this.canvasElCtx().drawImage(img, 0, 0);
44173         }.bind(this);
44174         
44175         img.src = img_src;
44176         
44177         this.is_empty = false;
44178     },
44179     
44180     selectImage: function()
44181     {
44182         this.fileEl().dom.click();
44183     },
44184     
44185     uploadImage: function(e)
44186     {
44187         var reader = new FileReader();
44188         
44189         reader.onload = function(e){
44190             var img = new Image();
44191             img.onload = function(){
44192                 this.reset();
44193                 this.canvasElCtx().drawImage(img, 0, 0);
44194             }.bind(this);
44195             img.src = e.target.result;
44196         }.bind(this);
44197         
44198         reader.readAsDataURL(e.target.files[0]);
44199     },
44200     
44201     // Bezier Point Constructor
44202     Point: (function () {
44203         function Point(x, y, time) {
44204             this.x = x;
44205             this.y = y;
44206             this.time = time || Date.now();
44207         }
44208         Point.prototype.distanceTo = function (start) {
44209             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44210         };
44211         Point.prototype.equals = function (other) {
44212             return this.x === other.x && this.y === other.y && this.time === other.time;
44213         };
44214         Point.prototype.velocityFrom = function (start) {
44215             return this.time !== start.time
44216             ? this.distanceTo(start) / (this.time - start.time)
44217             : 0;
44218         };
44219         return Point;
44220     }()),
44221     
44222     
44223     // Bezier Constructor
44224     Bezier: (function () {
44225         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44226             this.startPoint = startPoint;
44227             this.control2 = control2;
44228             this.control1 = control1;
44229             this.endPoint = endPoint;
44230             this.startWidth = startWidth;
44231             this.endWidth = endWidth;
44232         }
44233         Bezier.fromPoints = function (points, widths, scope) {
44234             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44235             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44236             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44237         };
44238         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44239             var dx1 = s1.x - s2.x;
44240             var dy1 = s1.y - s2.y;
44241             var dx2 = s2.x - s3.x;
44242             var dy2 = s2.y - s3.y;
44243             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44244             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44245             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44246             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44247             var dxm = m1.x - m2.x;
44248             var dym = m1.y - m2.y;
44249             var k = l2 / (l1 + l2);
44250             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44251             var tx = s2.x - cm.x;
44252             var ty = s2.y - cm.y;
44253             return {
44254                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44255                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44256             };
44257         };
44258         Bezier.prototype.length = function () {
44259             var steps = 10;
44260             var length = 0;
44261             var px;
44262             var py;
44263             for (var i = 0; i <= steps; i += 1) {
44264                 var t = i / steps;
44265                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44266                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44267                 if (i > 0) {
44268                     var xdiff = cx - px;
44269                     var ydiff = cy - py;
44270                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44271                 }
44272                 px = cx;
44273                 py = cy;
44274             }
44275             return length;
44276         };
44277         Bezier.prototype.point = function (t, start, c1, c2, end) {
44278             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44279             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44280             + (3.0 * c2 * (1.0 - t) * t * t)
44281             + (end * t * t * t);
44282         };
44283         return Bezier;
44284     }()),
44285     
44286     throttleStroke: function(fn, wait) {
44287       if (wait === void 0) { wait = 250; }
44288       var previous = 0;
44289       var timeout = null;
44290       var result;
44291       var storedContext;
44292       var storedArgs;
44293       var later = function () {
44294           previous = Date.now();
44295           timeout = null;
44296           result = fn.apply(storedContext, storedArgs);
44297           if (!timeout) {
44298               storedContext = null;
44299               storedArgs = [];
44300           }
44301       };
44302       return function wrapper() {
44303           var args = [];
44304           for (var _i = 0; _i < arguments.length; _i++) {
44305               args[_i] = arguments[_i];
44306           }
44307           var now = Date.now();
44308           var remaining = wait - (now - previous);
44309           storedContext = this;
44310           storedArgs = args;
44311           if (remaining <= 0 || remaining > wait) {
44312               if (timeout) {
44313                   clearTimeout(timeout);
44314                   timeout = null;
44315               }
44316               previous = now;
44317               result = fn.apply(storedContext, storedArgs);
44318               if (!timeout) {
44319                   storedContext = null;
44320                   storedArgs = [];
44321               }
44322           }
44323           else if (!timeout) {
44324               timeout = window.setTimeout(later, remaining);
44325           }
44326           return result;
44327       };
44328   }
44329   
44330 });
44331
44332  
44333
44334