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         
19728         
19729         if(!this.el){
19730             var cfg = Roo.apply({},  this.getAutoCreate());
19731             cfg.id = Roo.id();
19732             
19733             if (this.cls) {
19734                 cfg.cls += ' ' + this.cls;
19735             }
19736             if (this.style) {
19737                 cfg.style = this.style;
19738             }
19739             //Roo.log("adding to ");
19740             this.el = Roo.get(document.body).createChild(cfg, position);
19741 //            Roo.log(this.el);
19742         }
19743         
19744         this.contentEl = this.el.select('.popover-content',true).first();
19745         this.headerEl =  this.el.select('.popover-title',true).first();
19746         
19747         var nitems = [];
19748         if(typeof(this.items) != 'undefined'){
19749             var items = this.items;
19750             delete this.items;
19751
19752             for(var i =0;i < items.length;i++) {
19753                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19754             }
19755         }
19756
19757         this.items = nitems;
19758         
19759         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19760         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19761         
19762         
19763         
19764         this.initEvents();
19765     },
19766     
19767     resizeMask : function()
19768     {
19769         this.maskEl.setSize(
19770             Roo.lib.Dom.getViewWidth(true),
19771             Roo.lib.Dom.getViewHeight(true)
19772         );
19773     },
19774     
19775     initEvents : function()
19776     {
19777         
19778         if (!this.modal) { 
19779             Roo.bootstrap.Popover.register(this);
19780         }
19781          
19782         
19783         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19784         this.el.enableDisplayMode('block');
19785         this.el.hide();
19786         if (this.over === false && !this.parent()) {
19787             return; 
19788         }
19789         if (this.triggers === false) {
19790             return;
19791         }
19792          
19793         // support parent
19794         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19795         var triggers = this.trigger ? this.trigger.split(' ') : [];
19796         Roo.each(triggers, function(trigger) {
19797         
19798             if (trigger == 'click') {
19799                 on_el.on('click', this.toggle, this);
19800             } else if (trigger != 'manual') {
19801                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19802                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19803       
19804                 on_el.on(eventIn  ,this.enter, this);
19805                 on_el.on(eventOut, this.leave, this);
19806             }
19807         }, this);
19808         
19809     },
19810     
19811     
19812     // private
19813     timeout : null,
19814     hoverState : null,
19815     
19816     toggle : function () {
19817         this.hoverState == 'in' ? this.leave() : this.enter();
19818     },
19819     
19820     enter : function () {
19821         
19822         clearTimeout(this.timeout);
19823     
19824         this.hoverState = 'in';
19825     
19826         if (!this.delay || !this.delay.show) {
19827             this.show();
19828             return;
19829         }
19830         var _t = this;
19831         this.timeout = setTimeout(function () {
19832             if (_t.hoverState == 'in') {
19833                 _t.show();
19834             }
19835         }, this.delay.show)
19836     },
19837     
19838     leave : function() {
19839         clearTimeout(this.timeout);
19840     
19841         this.hoverState = 'out';
19842     
19843         if (!this.delay || !this.delay.hide) {
19844             this.hide();
19845             return;
19846         }
19847         var _t = this;
19848         this.timeout = setTimeout(function () {
19849             if (_t.hoverState == 'out') {
19850                 _t.hide();
19851             }
19852         }, this.delay.hide)
19853     },
19854     /**
19855      * Show the popover
19856      * @param {Roo.Element|string|false} - element to align and point to.
19857      */
19858     show : function (on_el)
19859     {
19860         
19861         on_el = on_el || false; // default to false
19862         if (!on_el) {
19863             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19864                 on_el = this.parent().el;
19865             } else if (this.over) {
19866                 Roo.get(this.over);
19867             }
19868             
19869         }
19870         
19871         if (!this.el) {
19872             this.render(document.body);
19873         }
19874         
19875         
19876         this.el.removeClass([
19877             'fade','top','bottom', 'left', 'right','in',
19878             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19879         ]);
19880         
19881         if (this.title === false) {
19882             this.headerEl.hide();
19883         }
19884         
19885         
19886         var placement = typeof this.placement == 'function' ?
19887             this.placement.call(this, this.el, on_el) :
19888             this.placement;
19889             
19890         /*
19891         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19892         
19893         // I think  'auto right' - but 
19894         
19895         var autoPlace = autoToken.test(placement);
19896         if (autoPlace) {
19897             placement = placement.replace(autoToken, '') || 'top';
19898         }
19899         */
19900         
19901         
19902         this.el.show();
19903         this.el.dom.style.display='block';
19904         
19905         //this.el.appendTo(on_el);
19906         
19907         var p = this.getPosition();
19908         var box = this.el.getBox();
19909         
19910         
19911         var align = Roo.bootstrap.Popover.alignment[placement];
19912         this.el.addClass(align[2]);
19913
19914 //        Roo.log(align);
19915
19916         if (on_el) {
19917             this.el.alignTo(on_el, align[0],align[1]);
19918         } else {
19919             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19920             var es = this.el.getSize();
19921             var x = Roo.lib.Dom.getViewWidth()/2;
19922             var y = Roo.lib.Dom.getViewHeight()/2;
19923             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19924             
19925         }
19926
19927         
19928         //var arrow = this.el.select('.arrow',true).first();
19929         //arrow.set(align[2], 
19930         
19931         this.el.addClass('in');
19932         
19933         
19934         if (this.el.hasClass('fade')) {
19935             // fade it?
19936         }
19937         
19938         this.hoverState = 'in';
19939         
19940         if (this.modal) {
19941             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19942             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19943             this.maskEl.dom.style.display = 'block';
19944             this.maskEl.addClass('show');
19945         }
19946         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19947
19948         
19949         
19950         this.fireEvent('show', this);
19951         
19952     },
19953     hide : function()
19954     {
19955         this.el.setXY([0,0]);
19956         this.el.removeClass('in');
19957         this.el.hide();
19958         this.hoverState = null;
19959         this.maskEl.hide(); // always..
19960         this.fireEvent('hide', this);
19961     }
19962     
19963 });
19964
19965
19966 Roo.apply(Roo.bootstrap.Popover, {
19967
19968     alignment : {
19969         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19970         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19971         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19972         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19973     },
19974     
19975     zIndex : 20001,
19976
19977     clickHander : false,
19978     
19979
19980     onMouseDown : function(e)
19981     {
19982         if (!e.getTarget(".roo-popover")) {
19983             this.hideAll();
19984         }
19985          
19986     },
19987     
19988     popups : [],
19989     
19990     register : function(popup)
19991     {
19992         if (!Roo.bootstrap.Popover.clickHandler) {
19993             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19994         }
19995         // hide other popups.
19996         this.hideAll();
19997         this.popups.push(popup);
19998     },
19999     hideAll : function()
20000     {
20001         this.popups.forEach(function(p) {
20002             p.hide();
20003         });
20004     }
20005
20006 });/*
20007  * - LGPL
20008  *
20009  * Card header - holder for the card header elements.
20010  * 
20011  */
20012
20013 /**
20014  * @class Roo.bootstrap.PopoverNav
20015  * @extends Roo.bootstrap.NavGroup
20016  * Bootstrap Popover header navigation class
20017  * @constructor
20018  * Create a new Popover Header Navigation 
20019  * @param {Object} config The config object
20020  */
20021
20022 Roo.bootstrap.PopoverNav = function(config){
20023     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20024 };
20025
20026 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavGroup,  {
20027     
20028     
20029     container_method : 'getPopoverHeader' 
20030     
20031      
20032     
20033     
20034    
20035 });
20036
20037  
20038
20039  /*
20040  * - LGPL
20041  *
20042  * Progress
20043  * 
20044  */
20045
20046 /**
20047  * @class Roo.bootstrap.Progress
20048  * @extends Roo.bootstrap.Component
20049  * Bootstrap Progress class
20050  * @cfg {Boolean} striped striped of the progress bar
20051  * @cfg {Boolean} active animated of the progress bar
20052  * 
20053  * 
20054  * @constructor
20055  * Create a new Progress
20056  * @param {Object} config The config object
20057  */
20058
20059 Roo.bootstrap.Progress = function(config){
20060     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20061 };
20062
20063 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20064     
20065     striped : false,
20066     active: false,
20067     
20068     getAutoCreate : function(){
20069         var cfg = {
20070             tag: 'div',
20071             cls: 'progress'
20072         };
20073         
20074         
20075         if(this.striped){
20076             cfg.cls += ' progress-striped';
20077         }
20078       
20079         if(this.active){
20080             cfg.cls += ' active';
20081         }
20082         
20083         
20084         return cfg;
20085     }
20086    
20087 });
20088
20089  
20090
20091  /*
20092  * - LGPL
20093  *
20094  * ProgressBar
20095  * 
20096  */
20097
20098 /**
20099  * @class Roo.bootstrap.ProgressBar
20100  * @extends Roo.bootstrap.Component
20101  * Bootstrap ProgressBar class
20102  * @cfg {Number} aria_valuenow aria-value now
20103  * @cfg {Number} aria_valuemin aria-value min
20104  * @cfg {Number} aria_valuemax aria-value max
20105  * @cfg {String} label label for the progress bar
20106  * @cfg {String} panel (success | info | warning | danger )
20107  * @cfg {String} role role of the progress bar
20108  * @cfg {String} sr_only text
20109  * 
20110  * 
20111  * @constructor
20112  * Create a new ProgressBar
20113  * @param {Object} config The config object
20114  */
20115
20116 Roo.bootstrap.ProgressBar = function(config){
20117     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20118 };
20119
20120 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20121     
20122     aria_valuenow : 0,
20123     aria_valuemin : 0,
20124     aria_valuemax : 100,
20125     label : false,
20126     panel : false,
20127     role : false,
20128     sr_only: false,
20129     
20130     getAutoCreate : function()
20131     {
20132         
20133         var cfg = {
20134             tag: 'div',
20135             cls: 'progress-bar',
20136             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20137         };
20138         
20139         if(this.sr_only){
20140             cfg.cn = {
20141                 tag: 'span',
20142                 cls: 'sr-only',
20143                 html: this.sr_only
20144             }
20145         }
20146         
20147         if(this.role){
20148             cfg.role = this.role;
20149         }
20150         
20151         if(this.aria_valuenow){
20152             cfg['aria-valuenow'] = this.aria_valuenow;
20153         }
20154         
20155         if(this.aria_valuemin){
20156             cfg['aria-valuemin'] = this.aria_valuemin;
20157         }
20158         
20159         if(this.aria_valuemax){
20160             cfg['aria-valuemax'] = this.aria_valuemax;
20161         }
20162         
20163         if(this.label && !this.sr_only){
20164             cfg.html = this.label;
20165         }
20166         
20167         if(this.panel){
20168             cfg.cls += ' progress-bar-' + this.panel;
20169         }
20170         
20171         return cfg;
20172     },
20173     
20174     update : function(aria_valuenow)
20175     {
20176         this.aria_valuenow = aria_valuenow;
20177         
20178         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20179     }
20180    
20181 });
20182
20183  
20184
20185  /*
20186  * - LGPL
20187  *
20188  * column
20189  * 
20190  */
20191
20192 /**
20193  * @class Roo.bootstrap.TabGroup
20194  * @extends Roo.bootstrap.Column
20195  * Bootstrap Column class
20196  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20197  * @cfg {Boolean} carousel true to make the group behave like a carousel
20198  * @cfg {Boolean} bullets show bullets for the panels
20199  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20200  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20201  * @cfg {Boolean} showarrow (true|false) show arrow default true
20202  * 
20203  * @constructor
20204  * Create a new TabGroup
20205  * @param {Object} config The config object
20206  */
20207
20208 Roo.bootstrap.TabGroup = function(config){
20209     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20210     if (!this.navId) {
20211         this.navId = Roo.id();
20212     }
20213     this.tabs = [];
20214     Roo.bootstrap.TabGroup.register(this);
20215     
20216 };
20217
20218 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20219     
20220     carousel : false,
20221     transition : false,
20222     bullets : 0,
20223     timer : 0,
20224     autoslide : false,
20225     slideFn : false,
20226     slideOnTouch : false,
20227     showarrow : true,
20228     
20229     getAutoCreate : function()
20230     {
20231         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20232         
20233         cfg.cls += ' tab-content';
20234         
20235         if (this.carousel) {
20236             cfg.cls += ' carousel slide';
20237             
20238             cfg.cn = [{
20239                cls : 'carousel-inner',
20240                cn : []
20241             }];
20242         
20243             if(this.bullets  && !Roo.isTouch){
20244                 
20245                 var bullets = {
20246                     cls : 'carousel-bullets',
20247                     cn : []
20248                 };
20249                
20250                 if(this.bullets_cls){
20251                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20252                 }
20253                 
20254                 bullets.cn.push({
20255                     cls : 'clear'
20256                 });
20257                 
20258                 cfg.cn[0].cn.push(bullets);
20259             }
20260             
20261             if(this.showarrow){
20262                 cfg.cn[0].cn.push({
20263                     tag : 'div',
20264                     class : 'carousel-arrow',
20265                     cn : [
20266                         {
20267                             tag : 'div',
20268                             class : 'carousel-prev',
20269                             cn : [
20270                                 {
20271                                     tag : 'i',
20272                                     class : 'fa fa-chevron-left'
20273                                 }
20274                             ]
20275                         },
20276                         {
20277                             tag : 'div',
20278                             class : 'carousel-next',
20279                             cn : [
20280                                 {
20281                                     tag : 'i',
20282                                     class : 'fa fa-chevron-right'
20283                                 }
20284                             ]
20285                         }
20286                     ]
20287                 });
20288             }
20289             
20290         }
20291         
20292         return cfg;
20293     },
20294     
20295     initEvents:  function()
20296     {
20297 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20298 //            this.el.on("touchstart", this.onTouchStart, this);
20299 //        }
20300         
20301         if(this.autoslide){
20302             var _this = this;
20303             
20304             this.slideFn = window.setInterval(function() {
20305                 _this.showPanelNext();
20306             }, this.timer);
20307         }
20308         
20309         if(this.showarrow){
20310             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20311             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20312         }
20313         
20314         
20315     },
20316     
20317 //    onTouchStart : function(e, el, o)
20318 //    {
20319 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20320 //            return;
20321 //        }
20322 //        
20323 //        this.showPanelNext();
20324 //    },
20325     
20326     
20327     getChildContainer : function()
20328     {
20329         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20330     },
20331     
20332     /**
20333     * register a Navigation item
20334     * @param {Roo.bootstrap.NavItem} the navitem to add
20335     */
20336     register : function(item)
20337     {
20338         this.tabs.push( item);
20339         item.navId = this.navId; // not really needed..
20340         this.addBullet();
20341     
20342     },
20343     
20344     getActivePanel : function()
20345     {
20346         var r = false;
20347         Roo.each(this.tabs, function(t) {
20348             if (t.active) {
20349                 r = t;
20350                 return false;
20351             }
20352             return null;
20353         });
20354         return r;
20355         
20356     },
20357     getPanelByName : function(n)
20358     {
20359         var r = false;
20360         Roo.each(this.tabs, function(t) {
20361             if (t.tabId == n) {
20362                 r = t;
20363                 return false;
20364             }
20365             return null;
20366         });
20367         return r;
20368     },
20369     indexOfPanel : function(p)
20370     {
20371         var r = false;
20372         Roo.each(this.tabs, function(t,i) {
20373             if (t.tabId == p.tabId) {
20374                 r = i;
20375                 return false;
20376             }
20377             return null;
20378         });
20379         return r;
20380     },
20381     /**
20382      * show a specific panel
20383      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20384      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20385      */
20386     showPanel : function (pan)
20387     {
20388         if(this.transition || typeof(pan) == 'undefined'){
20389             Roo.log("waiting for the transitionend");
20390             return false;
20391         }
20392         
20393         if (typeof(pan) == 'number') {
20394             pan = this.tabs[pan];
20395         }
20396         
20397         if (typeof(pan) == 'string') {
20398             pan = this.getPanelByName(pan);
20399         }
20400         
20401         var cur = this.getActivePanel();
20402         
20403         if(!pan || !cur){
20404             Roo.log('pan or acitve pan is undefined');
20405             return false;
20406         }
20407         
20408         if (pan.tabId == this.getActivePanel().tabId) {
20409             return true;
20410         }
20411         
20412         if (false === cur.fireEvent('beforedeactivate')) {
20413             return false;
20414         }
20415         
20416         if(this.bullets > 0 && !Roo.isTouch){
20417             this.setActiveBullet(this.indexOfPanel(pan));
20418         }
20419         
20420         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20421             
20422             //class="carousel-item carousel-item-next carousel-item-left"
20423             
20424             this.transition = true;
20425             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20426             var lr = dir == 'next' ? 'left' : 'right';
20427             pan.el.addClass(dir); // or prev
20428             pan.el.addClass('carousel-item-' + dir); // or prev
20429             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20430             cur.el.addClass(lr); // or right
20431             pan.el.addClass(lr);
20432             cur.el.addClass('carousel-item-' +lr); // or right
20433             pan.el.addClass('carousel-item-' +lr);
20434             
20435             
20436             var _this = this;
20437             cur.el.on('transitionend', function() {
20438                 Roo.log("trans end?");
20439                 
20440                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20441                 pan.setActive(true);
20442                 
20443                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20444                 cur.setActive(false);
20445                 
20446                 _this.transition = false;
20447                 
20448             }, this, { single:  true } );
20449             
20450             return true;
20451         }
20452         
20453         cur.setActive(false);
20454         pan.setActive(true);
20455         
20456         return true;
20457         
20458     },
20459     showPanelNext : function()
20460     {
20461         var i = this.indexOfPanel(this.getActivePanel());
20462         
20463         if (i >= this.tabs.length - 1 && !this.autoslide) {
20464             return;
20465         }
20466         
20467         if (i >= this.tabs.length - 1 && this.autoslide) {
20468             i = -1;
20469         }
20470         
20471         this.showPanel(this.tabs[i+1]);
20472     },
20473     
20474     showPanelPrev : function()
20475     {
20476         var i = this.indexOfPanel(this.getActivePanel());
20477         
20478         if (i  < 1 && !this.autoslide) {
20479             return;
20480         }
20481         
20482         if (i < 1 && this.autoslide) {
20483             i = this.tabs.length;
20484         }
20485         
20486         this.showPanel(this.tabs[i-1]);
20487     },
20488     
20489     
20490     addBullet: function()
20491     {
20492         if(!this.bullets || Roo.isTouch){
20493             return;
20494         }
20495         var ctr = this.el.select('.carousel-bullets',true).first();
20496         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20497         var bullet = ctr.createChild({
20498             cls : 'bullet bullet-' + i
20499         },ctr.dom.lastChild);
20500         
20501         
20502         var _this = this;
20503         
20504         bullet.on('click', (function(e, el, o, ii, t){
20505
20506             e.preventDefault();
20507
20508             this.showPanel(ii);
20509
20510             if(this.autoslide && this.slideFn){
20511                 clearInterval(this.slideFn);
20512                 this.slideFn = window.setInterval(function() {
20513                     _this.showPanelNext();
20514                 }, this.timer);
20515             }
20516
20517         }).createDelegate(this, [i, bullet], true));
20518                 
20519         
20520     },
20521      
20522     setActiveBullet : function(i)
20523     {
20524         if(Roo.isTouch){
20525             return;
20526         }
20527         
20528         Roo.each(this.el.select('.bullet', true).elements, function(el){
20529             el.removeClass('selected');
20530         });
20531
20532         var bullet = this.el.select('.bullet-' + i, true).first();
20533         
20534         if(!bullet){
20535             return;
20536         }
20537         
20538         bullet.addClass('selected');
20539     }
20540     
20541     
20542   
20543 });
20544
20545  
20546
20547  
20548  
20549 Roo.apply(Roo.bootstrap.TabGroup, {
20550     
20551     groups: {},
20552      /**
20553     * register a Navigation Group
20554     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20555     */
20556     register : function(navgrp)
20557     {
20558         this.groups[navgrp.navId] = navgrp;
20559         
20560     },
20561     /**
20562     * fetch a Navigation Group based on the navigation ID
20563     * if one does not exist , it will get created.
20564     * @param {string} the navgroup to add
20565     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20566     */
20567     get: function(navId) {
20568         if (typeof(this.groups[navId]) == 'undefined') {
20569             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20570         }
20571         return this.groups[navId] ;
20572     }
20573     
20574     
20575     
20576 });
20577
20578  /*
20579  * - LGPL
20580  *
20581  * TabPanel
20582  * 
20583  */
20584
20585 /**
20586  * @class Roo.bootstrap.TabPanel
20587  * @extends Roo.bootstrap.Component
20588  * Bootstrap TabPanel class
20589  * @cfg {Boolean} active panel active
20590  * @cfg {String} html panel content
20591  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20592  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20593  * @cfg {String} href click to link..
20594  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20595  * 
20596  * 
20597  * @constructor
20598  * Create a new TabPanel
20599  * @param {Object} config The config object
20600  */
20601
20602 Roo.bootstrap.TabPanel = function(config){
20603     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20604     this.addEvents({
20605         /**
20606              * @event changed
20607              * Fires when the active status changes
20608              * @param {Roo.bootstrap.TabPanel} this
20609              * @param {Boolean} state the new state
20610             
20611          */
20612         'changed': true,
20613         /**
20614              * @event beforedeactivate
20615              * Fires before a tab is de-activated - can be used to do validation on a form.
20616              * @param {Roo.bootstrap.TabPanel} this
20617              * @return {Boolean} false if there is an error
20618             
20619          */
20620         'beforedeactivate': true
20621      });
20622     
20623     this.tabId = this.tabId || Roo.id();
20624   
20625 };
20626
20627 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20628     
20629     active: false,
20630     html: false,
20631     tabId: false,
20632     navId : false,
20633     href : '',
20634     touchSlide : false,
20635     getAutoCreate : function(){
20636         
20637         
20638         var cfg = {
20639             tag: 'div',
20640             // item is needed for carousel - not sure if it has any effect otherwise
20641             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20642             html: this.html || ''
20643         };
20644         
20645         if(this.active){
20646             cfg.cls += ' active';
20647         }
20648         
20649         if(this.tabId){
20650             cfg.tabId = this.tabId;
20651         }
20652         
20653         
20654         
20655         return cfg;
20656     },
20657     
20658     initEvents:  function()
20659     {
20660         var p = this.parent();
20661         
20662         this.navId = this.navId || p.navId;
20663         
20664         if (typeof(this.navId) != 'undefined') {
20665             // not really needed.. but just in case.. parent should be a NavGroup.
20666             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20667             
20668             tg.register(this);
20669             
20670             var i = tg.tabs.length - 1;
20671             
20672             if(this.active && tg.bullets > 0 && i < tg.bullets){
20673                 tg.setActiveBullet(i);
20674             }
20675         }
20676         
20677         this.el.on('click', this.onClick, this);
20678         
20679         if(Roo.isTouch && this.touchSlide){
20680             this.el.on("touchstart", this.onTouchStart, this);
20681             this.el.on("touchmove", this.onTouchMove, this);
20682             this.el.on("touchend", this.onTouchEnd, this);
20683         }
20684         
20685     },
20686     
20687     onRender : function(ct, position)
20688     {
20689         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20690     },
20691     
20692     setActive : function(state)
20693     {
20694         Roo.log("panel - set active " + this.tabId + "=" + state);
20695         
20696         this.active = state;
20697         if (!state) {
20698             this.el.removeClass('active');
20699             
20700         } else  if (!this.el.hasClass('active')) {
20701             this.el.addClass('active');
20702         }
20703         
20704         this.fireEvent('changed', this, state);
20705     },
20706     
20707     onClick : function(e)
20708     {
20709         e.preventDefault();
20710         
20711         if(!this.href.length){
20712             return;
20713         }
20714         
20715         window.location.href = this.href;
20716     },
20717     
20718     startX : 0,
20719     startY : 0,
20720     endX : 0,
20721     endY : 0,
20722     swiping : false,
20723     
20724     onTouchStart : function(e)
20725     {
20726         this.swiping = false;
20727         
20728         this.startX = e.browserEvent.touches[0].clientX;
20729         this.startY = e.browserEvent.touches[0].clientY;
20730     },
20731     
20732     onTouchMove : function(e)
20733     {
20734         this.swiping = true;
20735         
20736         this.endX = e.browserEvent.touches[0].clientX;
20737         this.endY = e.browserEvent.touches[0].clientY;
20738     },
20739     
20740     onTouchEnd : function(e)
20741     {
20742         if(!this.swiping){
20743             this.onClick(e);
20744             return;
20745         }
20746         
20747         var tabGroup = this.parent();
20748         
20749         if(this.endX > this.startX){ // swiping right
20750             tabGroup.showPanelPrev();
20751             return;
20752         }
20753         
20754         if(this.startX > this.endX){ // swiping left
20755             tabGroup.showPanelNext();
20756             return;
20757         }
20758     }
20759     
20760     
20761 });
20762  
20763
20764  
20765
20766  /*
20767  * - LGPL
20768  *
20769  * DateField
20770  * 
20771  */
20772
20773 /**
20774  * @class Roo.bootstrap.DateField
20775  * @extends Roo.bootstrap.Input
20776  * Bootstrap DateField class
20777  * @cfg {Number} weekStart default 0
20778  * @cfg {String} viewMode default empty, (months|years)
20779  * @cfg {String} minViewMode default empty, (months|years)
20780  * @cfg {Number} startDate default -Infinity
20781  * @cfg {Number} endDate default Infinity
20782  * @cfg {Boolean} todayHighlight default false
20783  * @cfg {Boolean} todayBtn default false
20784  * @cfg {Boolean} calendarWeeks default false
20785  * @cfg {Object} daysOfWeekDisabled default empty
20786  * @cfg {Boolean} singleMode default false (true | false)
20787  * 
20788  * @cfg {Boolean} keyboardNavigation default true
20789  * @cfg {String} language default en
20790  * 
20791  * @constructor
20792  * Create a new DateField
20793  * @param {Object} config The config object
20794  */
20795
20796 Roo.bootstrap.DateField = function(config){
20797     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20798      this.addEvents({
20799             /**
20800              * @event show
20801              * Fires when this field show.
20802              * @param {Roo.bootstrap.DateField} this
20803              * @param {Mixed} date The date value
20804              */
20805             show : true,
20806             /**
20807              * @event show
20808              * Fires when this field hide.
20809              * @param {Roo.bootstrap.DateField} this
20810              * @param {Mixed} date The date value
20811              */
20812             hide : true,
20813             /**
20814              * @event select
20815              * Fires when select a date.
20816              * @param {Roo.bootstrap.DateField} this
20817              * @param {Mixed} date The date value
20818              */
20819             select : true,
20820             /**
20821              * @event beforeselect
20822              * Fires when before select a date.
20823              * @param {Roo.bootstrap.DateField} this
20824              * @param {Mixed} date The date value
20825              */
20826             beforeselect : true
20827         });
20828 };
20829
20830 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20831     
20832     /**
20833      * @cfg {String} format
20834      * The default date format string which can be overriden for localization support.  The format must be
20835      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20836      */
20837     format : "m/d/y",
20838     /**
20839      * @cfg {String} altFormats
20840      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20841      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20842      */
20843     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20844     
20845     weekStart : 0,
20846     
20847     viewMode : '',
20848     
20849     minViewMode : '',
20850     
20851     todayHighlight : false,
20852     
20853     todayBtn: false,
20854     
20855     language: 'en',
20856     
20857     keyboardNavigation: true,
20858     
20859     calendarWeeks: false,
20860     
20861     startDate: -Infinity,
20862     
20863     endDate: Infinity,
20864     
20865     daysOfWeekDisabled: [],
20866     
20867     _events: [],
20868     
20869     singleMode : false,
20870     
20871     UTCDate: function()
20872     {
20873         return new Date(Date.UTC.apply(Date, arguments));
20874     },
20875     
20876     UTCToday: function()
20877     {
20878         var today = new Date();
20879         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20880     },
20881     
20882     getDate: function() {
20883             var d = this.getUTCDate();
20884             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20885     },
20886     
20887     getUTCDate: function() {
20888             return this.date;
20889     },
20890     
20891     setDate: function(d) {
20892             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20893     },
20894     
20895     setUTCDate: function(d) {
20896             this.date = d;
20897             this.setValue(this.formatDate(this.date));
20898     },
20899         
20900     onRender: function(ct, position)
20901     {
20902         
20903         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20904         
20905         this.language = this.language || 'en';
20906         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20907         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20908         
20909         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20910         this.format = this.format || 'm/d/y';
20911         this.isInline = false;
20912         this.isInput = true;
20913         this.component = this.el.select('.add-on', true).first() || false;
20914         this.component = (this.component && this.component.length === 0) ? false : this.component;
20915         this.hasInput = this.component && this.inputEl().length;
20916         
20917         if (typeof(this.minViewMode === 'string')) {
20918             switch (this.minViewMode) {
20919                 case 'months':
20920                     this.minViewMode = 1;
20921                     break;
20922                 case 'years':
20923                     this.minViewMode = 2;
20924                     break;
20925                 default:
20926                     this.minViewMode = 0;
20927                     break;
20928             }
20929         }
20930         
20931         if (typeof(this.viewMode === 'string')) {
20932             switch (this.viewMode) {
20933                 case 'months':
20934                     this.viewMode = 1;
20935                     break;
20936                 case 'years':
20937                     this.viewMode = 2;
20938                     break;
20939                 default:
20940                     this.viewMode = 0;
20941                     break;
20942             }
20943         }
20944                 
20945         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20946         
20947 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20948         
20949         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20950         
20951         this.picker().on('mousedown', this.onMousedown, this);
20952         this.picker().on('click', this.onClick, this);
20953         
20954         this.picker().addClass('datepicker-dropdown');
20955         
20956         this.startViewMode = this.viewMode;
20957         
20958         if(this.singleMode){
20959             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20960                 v.setVisibilityMode(Roo.Element.DISPLAY);
20961                 v.hide();
20962             });
20963             
20964             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20965                 v.setStyle('width', '189px');
20966             });
20967         }
20968         
20969         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20970             if(!this.calendarWeeks){
20971                 v.remove();
20972                 return;
20973             }
20974             
20975             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20976             v.attr('colspan', function(i, val){
20977                 return parseInt(val) + 1;
20978             });
20979         });
20980                         
20981         
20982         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20983         
20984         this.setStartDate(this.startDate);
20985         this.setEndDate(this.endDate);
20986         
20987         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20988         
20989         this.fillDow();
20990         this.fillMonths();
20991         this.update();
20992         this.showMode();
20993         
20994         if(this.isInline) {
20995             this.showPopup();
20996         }
20997     },
20998     
20999     picker : function()
21000     {
21001         return this.pickerEl;
21002 //        return this.el.select('.datepicker', true).first();
21003     },
21004     
21005     fillDow: function()
21006     {
21007         var dowCnt = this.weekStart;
21008         
21009         var dow = {
21010             tag: 'tr',
21011             cn: [
21012                 
21013             ]
21014         };
21015         
21016         if(this.calendarWeeks){
21017             dow.cn.push({
21018                 tag: 'th',
21019                 cls: 'cw',
21020                 html: '&nbsp;'
21021             })
21022         }
21023         
21024         while (dowCnt < this.weekStart + 7) {
21025             dow.cn.push({
21026                 tag: 'th',
21027                 cls: 'dow',
21028                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21029             });
21030         }
21031         
21032         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21033     },
21034     
21035     fillMonths: function()
21036     {    
21037         var i = 0;
21038         var months = this.picker().select('>.datepicker-months td', true).first();
21039         
21040         months.dom.innerHTML = '';
21041         
21042         while (i < 12) {
21043             var month = {
21044                 tag: 'span',
21045                 cls: 'month',
21046                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21047             };
21048             
21049             months.createChild(month);
21050         }
21051         
21052     },
21053     
21054     update: function()
21055     {
21056         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;
21057         
21058         if (this.date < this.startDate) {
21059             this.viewDate = new Date(this.startDate);
21060         } else if (this.date > this.endDate) {
21061             this.viewDate = new Date(this.endDate);
21062         } else {
21063             this.viewDate = new Date(this.date);
21064         }
21065         
21066         this.fill();
21067     },
21068     
21069     fill: function() 
21070     {
21071         var d = new Date(this.viewDate),
21072                 year = d.getUTCFullYear(),
21073                 month = d.getUTCMonth(),
21074                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21075                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21076                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21077                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21078                 currentDate = this.date && this.date.valueOf(),
21079                 today = this.UTCToday();
21080         
21081         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21082         
21083 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21084         
21085 //        this.picker.select('>tfoot th.today').
21086 //                                              .text(dates[this.language].today)
21087 //                                              .toggle(this.todayBtn !== false);
21088     
21089         this.updateNavArrows();
21090         this.fillMonths();
21091                                                 
21092         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21093         
21094         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21095          
21096         prevMonth.setUTCDate(day);
21097         
21098         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21099         
21100         var nextMonth = new Date(prevMonth);
21101         
21102         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21103         
21104         nextMonth = nextMonth.valueOf();
21105         
21106         var fillMonths = false;
21107         
21108         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21109         
21110         while(prevMonth.valueOf() <= nextMonth) {
21111             var clsName = '';
21112             
21113             if (prevMonth.getUTCDay() === this.weekStart) {
21114                 if(fillMonths){
21115                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21116                 }
21117                     
21118                 fillMonths = {
21119                     tag: 'tr',
21120                     cn: []
21121                 };
21122                 
21123                 if(this.calendarWeeks){
21124                     // ISO 8601: First week contains first thursday.
21125                     // ISO also states week starts on Monday, but we can be more abstract here.
21126                     var
21127                     // Start of current week: based on weekstart/current date
21128                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21129                     // Thursday of this week
21130                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21131                     // First Thursday of year, year from thursday
21132                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21133                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21134                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21135                     
21136                     fillMonths.cn.push({
21137                         tag: 'td',
21138                         cls: 'cw',
21139                         html: calWeek
21140                     });
21141                 }
21142             }
21143             
21144             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21145                 clsName += ' old';
21146             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21147                 clsName += ' new';
21148             }
21149             if (this.todayHighlight &&
21150                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21151                 prevMonth.getUTCMonth() == today.getMonth() &&
21152                 prevMonth.getUTCDate() == today.getDate()) {
21153                 clsName += ' today';
21154             }
21155             
21156             if (currentDate && prevMonth.valueOf() === currentDate) {
21157                 clsName += ' active';
21158             }
21159             
21160             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21161                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21162                     clsName += ' disabled';
21163             }
21164             
21165             fillMonths.cn.push({
21166                 tag: 'td',
21167                 cls: 'day ' + clsName,
21168                 html: prevMonth.getDate()
21169             });
21170             
21171             prevMonth.setDate(prevMonth.getDate()+1);
21172         }
21173           
21174         var currentYear = this.date && this.date.getUTCFullYear();
21175         var currentMonth = this.date && this.date.getUTCMonth();
21176         
21177         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21178         
21179         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21180             v.removeClass('active');
21181             
21182             if(currentYear === year && k === currentMonth){
21183                 v.addClass('active');
21184             }
21185             
21186             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21187                 v.addClass('disabled');
21188             }
21189             
21190         });
21191         
21192         
21193         year = parseInt(year/10, 10) * 10;
21194         
21195         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21196         
21197         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21198         
21199         year -= 1;
21200         for (var i = -1; i < 11; i++) {
21201             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21202                 tag: 'span',
21203                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21204                 html: year
21205             });
21206             
21207             year += 1;
21208         }
21209     },
21210     
21211     showMode: function(dir) 
21212     {
21213         if (dir) {
21214             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21215         }
21216         
21217         Roo.each(this.picker().select('>div',true).elements, function(v){
21218             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21219             v.hide();
21220         });
21221         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21222     },
21223     
21224     place: function()
21225     {
21226         if(this.isInline) {
21227             return;
21228         }
21229         
21230         this.picker().removeClass(['bottom', 'top']);
21231         
21232         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21233             /*
21234              * place to the top of element!
21235              *
21236              */
21237             
21238             this.picker().addClass('top');
21239             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21240             
21241             return;
21242         }
21243         
21244         this.picker().addClass('bottom');
21245         
21246         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21247     },
21248     
21249     parseDate : function(value)
21250     {
21251         if(!value || value instanceof Date){
21252             return value;
21253         }
21254         var v = Date.parseDate(value, this.format);
21255         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21256             v = Date.parseDate(value, 'Y-m-d');
21257         }
21258         if(!v && this.altFormats){
21259             if(!this.altFormatsArray){
21260                 this.altFormatsArray = this.altFormats.split("|");
21261             }
21262             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21263                 v = Date.parseDate(value, this.altFormatsArray[i]);
21264             }
21265         }
21266         return v;
21267     },
21268     
21269     formatDate : function(date, fmt)
21270     {   
21271         return (!date || !(date instanceof Date)) ?
21272         date : date.dateFormat(fmt || this.format);
21273     },
21274     
21275     onFocus : function()
21276     {
21277         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21278         this.showPopup();
21279     },
21280     
21281     onBlur : function()
21282     {
21283         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21284         
21285         var d = this.inputEl().getValue();
21286         
21287         this.setValue(d);
21288                 
21289         this.hidePopup();
21290     },
21291     
21292     showPopup : function()
21293     {
21294         this.picker().show();
21295         this.update();
21296         this.place();
21297         
21298         this.fireEvent('showpopup', this, this.date);
21299     },
21300     
21301     hidePopup : function()
21302     {
21303         if(this.isInline) {
21304             return;
21305         }
21306         this.picker().hide();
21307         this.viewMode = this.startViewMode;
21308         this.showMode();
21309         
21310         this.fireEvent('hidepopup', this, this.date);
21311         
21312     },
21313     
21314     onMousedown: function(e)
21315     {
21316         e.stopPropagation();
21317         e.preventDefault();
21318     },
21319     
21320     keyup: function(e)
21321     {
21322         Roo.bootstrap.DateField.superclass.keyup.call(this);
21323         this.update();
21324     },
21325
21326     setValue: function(v)
21327     {
21328         if(this.fireEvent('beforeselect', this, v) !== false){
21329             var d = new Date(this.parseDate(v) ).clearTime();
21330         
21331             if(isNaN(d.getTime())){
21332                 this.date = this.viewDate = '';
21333                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21334                 return;
21335             }
21336
21337             v = this.formatDate(d);
21338
21339             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21340
21341             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21342
21343             this.update();
21344
21345             this.fireEvent('select', this, this.date);
21346         }
21347     },
21348     
21349     getValue: function()
21350     {
21351         return this.formatDate(this.date);
21352     },
21353     
21354     fireKey: function(e)
21355     {
21356         if (!this.picker().isVisible()){
21357             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21358                 this.showPopup();
21359             }
21360             return;
21361         }
21362         
21363         var dateChanged = false,
21364         dir, day, month,
21365         newDate, newViewDate;
21366         
21367         switch(e.keyCode){
21368             case 27: // escape
21369                 this.hidePopup();
21370                 e.preventDefault();
21371                 break;
21372             case 37: // left
21373             case 39: // right
21374                 if (!this.keyboardNavigation) {
21375                     break;
21376                 }
21377                 dir = e.keyCode == 37 ? -1 : 1;
21378                 
21379                 if (e.ctrlKey){
21380                     newDate = this.moveYear(this.date, dir);
21381                     newViewDate = this.moveYear(this.viewDate, dir);
21382                 } else if (e.shiftKey){
21383                     newDate = this.moveMonth(this.date, dir);
21384                     newViewDate = this.moveMonth(this.viewDate, dir);
21385                 } else {
21386                     newDate = new Date(this.date);
21387                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21388                     newViewDate = new Date(this.viewDate);
21389                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21390                 }
21391                 if (this.dateWithinRange(newDate)){
21392                     this.date = newDate;
21393                     this.viewDate = newViewDate;
21394                     this.setValue(this.formatDate(this.date));
21395 //                    this.update();
21396                     e.preventDefault();
21397                     dateChanged = true;
21398                 }
21399                 break;
21400             case 38: // up
21401             case 40: // down
21402                 if (!this.keyboardNavigation) {
21403                     break;
21404                 }
21405                 dir = e.keyCode == 38 ? -1 : 1;
21406                 if (e.ctrlKey){
21407                     newDate = this.moveYear(this.date, dir);
21408                     newViewDate = this.moveYear(this.viewDate, dir);
21409                 } else if (e.shiftKey){
21410                     newDate = this.moveMonth(this.date, dir);
21411                     newViewDate = this.moveMonth(this.viewDate, dir);
21412                 } else {
21413                     newDate = new Date(this.date);
21414                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21415                     newViewDate = new Date(this.viewDate);
21416                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21417                 }
21418                 if (this.dateWithinRange(newDate)){
21419                     this.date = newDate;
21420                     this.viewDate = newViewDate;
21421                     this.setValue(this.formatDate(this.date));
21422 //                    this.update();
21423                     e.preventDefault();
21424                     dateChanged = true;
21425                 }
21426                 break;
21427             case 13: // enter
21428                 this.setValue(this.formatDate(this.date));
21429                 this.hidePopup();
21430                 e.preventDefault();
21431                 break;
21432             case 9: // tab
21433                 this.setValue(this.formatDate(this.date));
21434                 this.hidePopup();
21435                 break;
21436             case 16: // shift
21437             case 17: // ctrl
21438             case 18: // alt
21439                 break;
21440             default :
21441                 this.hidePopup();
21442                 
21443         }
21444     },
21445     
21446     
21447     onClick: function(e) 
21448     {
21449         e.stopPropagation();
21450         e.preventDefault();
21451         
21452         var target = e.getTarget();
21453         
21454         if(target.nodeName.toLowerCase() === 'i'){
21455             target = Roo.get(target).dom.parentNode;
21456         }
21457         
21458         var nodeName = target.nodeName;
21459         var className = target.className;
21460         var html = target.innerHTML;
21461         //Roo.log(nodeName);
21462         
21463         switch(nodeName.toLowerCase()) {
21464             case 'th':
21465                 switch(className) {
21466                     case 'switch':
21467                         this.showMode(1);
21468                         break;
21469                     case 'prev':
21470                     case 'next':
21471                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21472                         switch(this.viewMode){
21473                                 case 0:
21474                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21475                                         break;
21476                                 case 1:
21477                                 case 2:
21478                                         this.viewDate = this.moveYear(this.viewDate, dir);
21479                                         break;
21480                         }
21481                         this.fill();
21482                         break;
21483                     case 'today':
21484                         var date = new Date();
21485                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21486 //                        this.fill()
21487                         this.setValue(this.formatDate(this.date));
21488                         
21489                         this.hidePopup();
21490                         break;
21491                 }
21492                 break;
21493             case 'span':
21494                 if (className.indexOf('disabled') < 0) {
21495                     this.viewDate.setUTCDate(1);
21496                     if (className.indexOf('month') > -1) {
21497                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21498                     } else {
21499                         var year = parseInt(html, 10) || 0;
21500                         this.viewDate.setUTCFullYear(year);
21501                         
21502                     }
21503                     
21504                     if(this.singleMode){
21505                         this.setValue(this.formatDate(this.viewDate));
21506                         this.hidePopup();
21507                         return;
21508                     }
21509                     
21510                     this.showMode(-1);
21511                     this.fill();
21512                 }
21513                 break;
21514                 
21515             case 'td':
21516                 //Roo.log(className);
21517                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21518                     var day = parseInt(html, 10) || 1;
21519                     var year = this.viewDate.getUTCFullYear(),
21520                         month = this.viewDate.getUTCMonth();
21521
21522                     if (className.indexOf('old') > -1) {
21523                         if(month === 0 ){
21524                             month = 11;
21525                             year -= 1;
21526                         }else{
21527                             month -= 1;
21528                         }
21529                     } else if (className.indexOf('new') > -1) {
21530                         if (month == 11) {
21531                             month = 0;
21532                             year += 1;
21533                         } else {
21534                             month += 1;
21535                         }
21536                     }
21537                     //Roo.log([year,month,day]);
21538                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21539                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21540 //                    this.fill();
21541                     //Roo.log(this.formatDate(this.date));
21542                     this.setValue(this.formatDate(this.date));
21543                     this.hidePopup();
21544                 }
21545                 break;
21546         }
21547     },
21548     
21549     setStartDate: function(startDate)
21550     {
21551         this.startDate = startDate || -Infinity;
21552         if (this.startDate !== -Infinity) {
21553             this.startDate = this.parseDate(this.startDate);
21554         }
21555         this.update();
21556         this.updateNavArrows();
21557     },
21558
21559     setEndDate: function(endDate)
21560     {
21561         this.endDate = endDate || Infinity;
21562         if (this.endDate !== Infinity) {
21563             this.endDate = this.parseDate(this.endDate);
21564         }
21565         this.update();
21566         this.updateNavArrows();
21567     },
21568     
21569     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21570     {
21571         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21572         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21573             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21574         }
21575         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21576             return parseInt(d, 10);
21577         });
21578         this.update();
21579         this.updateNavArrows();
21580     },
21581     
21582     updateNavArrows: function() 
21583     {
21584         if(this.singleMode){
21585             return;
21586         }
21587         
21588         var d = new Date(this.viewDate),
21589         year = d.getUTCFullYear(),
21590         month = d.getUTCMonth();
21591         
21592         Roo.each(this.picker().select('.prev', true).elements, function(v){
21593             v.show();
21594             switch (this.viewMode) {
21595                 case 0:
21596
21597                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21598                         v.hide();
21599                     }
21600                     break;
21601                 case 1:
21602                 case 2:
21603                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21604                         v.hide();
21605                     }
21606                     break;
21607             }
21608         });
21609         
21610         Roo.each(this.picker().select('.next', true).elements, function(v){
21611             v.show();
21612             switch (this.viewMode) {
21613                 case 0:
21614
21615                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21616                         v.hide();
21617                     }
21618                     break;
21619                 case 1:
21620                 case 2:
21621                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21622                         v.hide();
21623                     }
21624                     break;
21625             }
21626         })
21627     },
21628     
21629     moveMonth: function(date, dir)
21630     {
21631         if (!dir) {
21632             return date;
21633         }
21634         var new_date = new Date(date.valueOf()),
21635         day = new_date.getUTCDate(),
21636         month = new_date.getUTCMonth(),
21637         mag = Math.abs(dir),
21638         new_month, test;
21639         dir = dir > 0 ? 1 : -1;
21640         if (mag == 1){
21641             test = dir == -1
21642             // If going back one month, make sure month is not current month
21643             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21644             ? function(){
21645                 return new_date.getUTCMonth() == month;
21646             }
21647             // If going forward one month, make sure month is as expected
21648             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21649             : function(){
21650                 return new_date.getUTCMonth() != new_month;
21651             };
21652             new_month = month + dir;
21653             new_date.setUTCMonth(new_month);
21654             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21655             if (new_month < 0 || new_month > 11) {
21656                 new_month = (new_month + 12) % 12;
21657             }
21658         } else {
21659             // For magnitudes >1, move one month at a time...
21660             for (var i=0; i<mag; i++) {
21661                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21662                 new_date = this.moveMonth(new_date, dir);
21663             }
21664             // ...then reset the day, keeping it in the new month
21665             new_month = new_date.getUTCMonth();
21666             new_date.setUTCDate(day);
21667             test = function(){
21668                 return new_month != new_date.getUTCMonth();
21669             };
21670         }
21671         // Common date-resetting loop -- if date is beyond end of month, make it
21672         // end of month
21673         while (test()){
21674             new_date.setUTCDate(--day);
21675             new_date.setUTCMonth(new_month);
21676         }
21677         return new_date;
21678     },
21679
21680     moveYear: function(date, dir)
21681     {
21682         return this.moveMonth(date, dir*12);
21683     },
21684
21685     dateWithinRange: function(date)
21686     {
21687         return date >= this.startDate && date <= this.endDate;
21688     },
21689
21690     
21691     remove: function() 
21692     {
21693         this.picker().remove();
21694     },
21695     
21696     validateValue : function(value)
21697     {
21698         if(this.getVisibilityEl().hasClass('hidden')){
21699             return true;
21700         }
21701         
21702         if(value.length < 1)  {
21703             if(this.allowBlank){
21704                 return true;
21705             }
21706             return false;
21707         }
21708         
21709         if(value.length < this.minLength){
21710             return false;
21711         }
21712         if(value.length > this.maxLength){
21713             return false;
21714         }
21715         if(this.vtype){
21716             var vt = Roo.form.VTypes;
21717             if(!vt[this.vtype](value, this)){
21718                 return false;
21719             }
21720         }
21721         if(typeof this.validator == "function"){
21722             var msg = this.validator(value);
21723             if(msg !== true){
21724                 return false;
21725             }
21726         }
21727         
21728         if(this.regex && !this.regex.test(value)){
21729             return false;
21730         }
21731         
21732         if(typeof(this.parseDate(value)) == 'undefined'){
21733             return false;
21734         }
21735         
21736         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21737             return false;
21738         }      
21739         
21740         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21741             return false;
21742         } 
21743         
21744         
21745         return true;
21746     },
21747     
21748     reset : function()
21749     {
21750         this.date = this.viewDate = '';
21751         
21752         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21753     }
21754    
21755 });
21756
21757 Roo.apply(Roo.bootstrap.DateField,  {
21758     
21759     head : {
21760         tag: 'thead',
21761         cn: [
21762         {
21763             tag: 'tr',
21764             cn: [
21765             {
21766                 tag: 'th',
21767                 cls: 'prev',
21768                 html: '<i class="fa fa-arrow-left"/>'
21769             },
21770             {
21771                 tag: 'th',
21772                 cls: 'switch',
21773                 colspan: '5'
21774             },
21775             {
21776                 tag: 'th',
21777                 cls: 'next',
21778                 html: '<i class="fa fa-arrow-right"/>'
21779             }
21780
21781             ]
21782         }
21783         ]
21784     },
21785     
21786     content : {
21787         tag: 'tbody',
21788         cn: [
21789         {
21790             tag: 'tr',
21791             cn: [
21792             {
21793                 tag: 'td',
21794                 colspan: '7'
21795             }
21796             ]
21797         }
21798         ]
21799     },
21800     
21801     footer : {
21802         tag: 'tfoot',
21803         cn: [
21804         {
21805             tag: 'tr',
21806             cn: [
21807             {
21808                 tag: 'th',
21809                 colspan: '7',
21810                 cls: 'today'
21811             }
21812                     
21813             ]
21814         }
21815         ]
21816     },
21817     
21818     dates:{
21819         en: {
21820             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21821             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21822             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21823             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21824             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21825             today: "Today"
21826         }
21827     },
21828     
21829     modes: [
21830     {
21831         clsName: 'days',
21832         navFnc: 'Month',
21833         navStep: 1
21834     },
21835     {
21836         clsName: 'months',
21837         navFnc: 'FullYear',
21838         navStep: 1
21839     },
21840     {
21841         clsName: 'years',
21842         navFnc: 'FullYear',
21843         navStep: 10
21844     }]
21845 });
21846
21847 Roo.apply(Roo.bootstrap.DateField,  {
21848   
21849     template : {
21850         tag: 'div',
21851         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21852         cn: [
21853         {
21854             tag: 'div',
21855             cls: 'datepicker-days',
21856             cn: [
21857             {
21858                 tag: 'table',
21859                 cls: 'table-condensed',
21860                 cn:[
21861                 Roo.bootstrap.DateField.head,
21862                 {
21863                     tag: 'tbody'
21864                 },
21865                 Roo.bootstrap.DateField.footer
21866                 ]
21867             }
21868             ]
21869         },
21870         {
21871             tag: 'div',
21872             cls: 'datepicker-months',
21873             cn: [
21874             {
21875                 tag: 'table',
21876                 cls: 'table-condensed',
21877                 cn:[
21878                 Roo.bootstrap.DateField.head,
21879                 Roo.bootstrap.DateField.content,
21880                 Roo.bootstrap.DateField.footer
21881                 ]
21882             }
21883             ]
21884         },
21885         {
21886             tag: 'div',
21887             cls: 'datepicker-years',
21888             cn: [
21889             {
21890                 tag: 'table',
21891                 cls: 'table-condensed',
21892                 cn:[
21893                 Roo.bootstrap.DateField.head,
21894                 Roo.bootstrap.DateField.content,
21895                 Roo.bootstrap.DateField.footer
21896                 ]
21897             }
21898             ]
21899         }
21900         ]
21901     }
21902 });
21903
21904  
21905
21906  /*
21907  * - LGPL
21908  *
21909  * TimeField
21910  * 
21911  */
21912
21913 /**
21914  * @class Roo.bootstrap.TimeField
21915  * @extends Roo.bootstrap.Input
21916  * Bootstrap DateField class
21917  * 
21918  * 
21919  * @constructor
21920  * Create a new TimeField
21921  * @param {Object} config The config object
21922  */
21923
21924 Roo.bootstrap.TimeField = function(config){
21925     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21926     this.addEvents({
21927             /**
21928              * @event show
21929              * Fires when this field show.
21930              * @param {Roo.bootstrap.DateField} thisthis
21931              * @param {Mixed} date The date value
21932              */
21933             show : true,
21934             /**
21935              * @event show
21936              * Fires when this field hide.
21937              * @param {Roo.bootstrap.DateField} this
21938              * @param {Mixed} date The date value
21939              */
21940             hide : true,
21941             /**
21942              * @event select
21943              * Fires when select a date.
21944              * @param {Roo.bootstrap.DateField} this
21945              * @param {Mixed} date The date value
21946              */
21947             select : true
21948         });
21949 };
21950
21951 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21952     
21953     /**
21954      * @cfg {String} format
21955      * The default time format string which can be overriden for localization support.  The format must be
21956      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21957      */
21958     format : "H:i",
21959        
21960     onRender: function(ct, position)
21961     {
21962         
21963         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21964                 
21965         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21966         
21967         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21968         
21969         this.pop = this.picker().select('>.datepicker-time',true).first();
21970         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21971         
21972         this.picker().on('mousedown', this.onMousedown, this);
21973         this.picker().on('click', this.onClick, this);
21974         
21975         this.picker().addClass('datepicker-dropdown');
21976     
21977         this.fillTime();
21978         this.update();
21979             
21980         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21981         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21982         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21983         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21984         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21985         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21986
21987     },
21988     
21989     fireKey: function(e){
21990         if (!this.picker().isVisible()){
21991             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21992                 this.show();
21993             }
21994             return;
21995         }
21996
21997         e.preventDefault();
21998         
21999         switch(e.keyCode){
22000             case 27: // escape
22001                 this.hide();
22002                 break;
22003             case 37: // left
22004             case 39: // right
22005                 this.onTogglePeriod();
22006                 break;
22007             case 38: // up
22008                 this.onIncrementMinutes();
22009                 break;
22010             case 40: // down
22011                 this.onDecrementMinutes();
22012                 break;
22013             case 13: // enter
22014             case 9: // tab
22015                 this.setTime();
22016                 break;
22017         }
22018     },
22019     
22020     onClick: function(e) {
22021         e.stopPropagation();
22022         e.preventDefault();
22023     },
22024     
22025     picker : function()
22026     {
22027         return this.el.select('.datepicker', true).first();
22028     },
22029     
22030     fillTime: function()
22031     {    
22032         var time = this.pop.select('tbody', true).first();
22033         
22034         time.dom.innerHTML = '';
22035         
22036         time.createChild({
22037             tag: 'tr',
22038             cn: [
22039                 {
22040                     tag: 'td',
22041                     cn: [
22042                         {
22043                             tag: 'a',
22044                             href: '#',
22045                             cls: 'btn',
22046                             cn: [
22047                                 {
22048                                     tag: 'span',
22049                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
22050                                 }
22051                             ]
22052                         } 
22053                     ]
22054                 },
22055                 {
22056                     tag: 'td',
22057                     cls: 'separator'
22058                 },
22059                 {
22060                     tag: 'td',
22061                     cn: [
22062                         {
22063                             tag: 'a',
22064                             href: '#',
22065                             cls: 'btn',
22066                             cn: [
22067                                 {
22068                                     tag: 'span',
22069                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22070                                 }
22071                             ]
22072                         }
22073                     ]
22074                 },
22075                 {
22076                     tag: 'td',
22077                     cls: 'separator'
22078                 }
22079             ]
22080         });
22081         
22082         time.createChild({
22083             tag: 'tr',
22084             cn: [
22085                 {
22086                     tag: 'td',
22087                     cn: [
22088                         {
22089                             tag: 'span',
22090                             cls: 'timepicker-hour',
22091                             html: '00'
22092                         }  
22093                     ]
22094                 },
22095                 {
22096                     tag: 'td',
22097                     cls: 'separator',
22098                     html: ':'
22099                 },
22100                 {
22101                     tag: 'td',
22102                     cn: [
22103                         {
22104                             tag: 'span',
22105                             cls: 'timepicker-minute',
22106                             html: '00'
22107                         }  
22108                     ]
22109                 },
22110                 {
22111                     tag: 'td',
22112                     cls: 'separator'
22113                 },
22114                 {
22115                     tag: 'td',
22116                     cn: [
22117                         {
22118                             tag: 'button',
22119                             type: 'button',
22120                             cls: 'btn btn-primary period',
22121                             html: 'AM'
22122                             
22123                         }
22124                     ]
22125                 }
22126             ]
22127         });
22128         
22129         time.createChild({
22130             tag: 'tr',
22131             cn: [
22132                 {
22133                     tag: 'td',
22134                     cn: [
22135                         {
22136                             tag: 'a',
22137                             href: '#',
22138                             cls: 'btn',
22139                             cn: [
22140                                 {
22141                                     tag: 'span',
22142                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22143                                 }
22144                             ]
22145                         }
22146                     ]
22147                 },
22148                 {
22149                     tag: 'td',
22150                     cls: 'separator'
22151                 },
22152                 {
22153                     tag: 'td',
22154                     cn: [
22155                         {
22156                             tag: 'a',
22157                             href: '#',
22158                             cls: 'btn',
22159                             cn: [
22160                                 {
22161                                     tag: 'span',
22162                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22163                                 }
22164                             ]
22165                         }
22166                     ]
22167                 },
22168                 {
22169                     tag: 'td',
22170                     cls: 'separator'
22171                 }
22172             ]
22173         });
22174         
22175     },
22176     
22177     update: function()
22178     {
22179         
22180         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22181         
22182         this.fill();
22183     },
22184     
22185     fill: function() 
22186     {
22187         var hours = this.time.getHours();
22188         var minutes = this.time.getMinutes();
22189         var period = 'AM';
22190         
22191         if(hours > 11){
22192             period = 'PM';
22193         }
22194         
22195         if(hours == 0){
22196             hours = 12;
22197         }
22198         
22199         
22200         if(hours > 12){
22201             hours = hours - 12;
22202         }
22203         
22204         if(hours < 10){
22205             hours = '0' + hours;
22206         }
22207         
22208         if(minutes < 10){
22209             minutes = '0' + minutes;
22210         }
22211         
22212         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22213         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22214         this.pop.select('button', true).first().dom.innerHTML = period;
22215         
22216     },
22217     
22218     place: function()
22219     {   
22220         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22221         
22222         var cls = ['bottom'];
22223         
22224         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22225             cls.pop();
22226             cls.push('top');
22227         }
22228         
22229         cls.push('right');
22230         
22231         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22232             cls.pop();
22233             cls.push('left');
22234         }
22235         
22236         this.picker().addClass(cls.join('-'));
22237         
22238         var _this = this;
22239         
22240         Roo.each(cls, function(c){
22241             if(c == 'bottom'){
22242                 _this.picker().setTop(_this.inputEl().getHeight());
22243                 return;
22244             }
22245             if(c == 'top'){
22246                 _this.picker().setTop(0 - _this.picker().getHeight());
22247                 return;
22248             }
22249             
22250             if(c == 'left'){
22251                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22252                 return;
22253             }
22254             if(c == 'right'){
22255                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22256                 return;
22257             }
22258         });
22259         
22260     },
22261   
22262     onFocus : function()
22263     {
22264         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22265         this.show();
22266     },
22267     
22268     onBlur : function()
22269     {
22270         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22271         this.hide();
22272     },
22273     
22274     show : function()
22275     {
22276         this.picker().show();
22277         this.pop.show();
22278         this.update();
22279         this.place();
22280         
22281         this.fireEvent('show', this, this.date);
22282     },
22283     
22284     hide : function()
22285     {
22286         this.picker().hide();
22287         this.pop.hide();
22288         
22289         this.fireEvent('hide', this, this.date);
22290     },
22291     
22292     setTime : function()
22293     {
22294         this.hide();
22295         this.setValue(this.time.format(this.format));
22296         
22297         this.fireEvent('select', this, this.date);
22298         
22299         
22300     },
22301     
22302     onMousedown: function(e){
22303         e.stopPropagation();
22304         e.preventDefault();
22305     },
22306     
22307     onIncrementHours: function()
22308     {
22309         Roo.log('onIncrementHours');
22310         this.time = this.time.add(Date.HOUR, 1);
22311         this.update();
22312         
22313     },
22314     
22315     onDecrementHours: function()
22316     {
22317         Roo.log('onDecrementHours');
22318         this.time = this.time.add(Date.HOUR, -1);
22319         this.update();
22320     },
22321     
22322     onIncrementMinutes: function()
22323     {
22324         Roo.log('onIncrementMinutes');
22325         this.time = this.time.add(Date.MINUTE, 1);
22326         this.update();
22327     },
22328     
22329     onDecrementMinutes: function()
22330     {
22331         Roo.log('onDecrementMinutes');
22332         this.time = this.time.add(Date.MINUTE, -1);
22333         this.update();
22334     },
22335     
22336     onTogglePeriod: function()
22337     {
22338         Roo.log('onTogglePeriod');
22339         this.time = this.time.add(Date.HOUR, 12);
22340         this.update();
22341     }
22342     
22343    
22344 });
22345
22346 Roo.apply(Roo.bootstrap.TimeField,  {
22347     
22348     content : {
22349         tag: 'tbody',
22350         cn: [
22351             {
22352                 tag: 'tr',
22353                 cn: [
22354                 {
22355                     tag: 'td',
22356                     colspan: '7'
22357                 }
22358                 ]
22359             }
22360         ]
22361     },
22362     
22363     footer : {
22364         tag: 'tfoot',
22365         cn: [
22366             {
22367                 tag: 'tr',
22368                 cn: [
22369                 {
22370                     tag: 'th',
22371                     colspan: '7',
22372                     cls: '',
22373                     cn: [
22374                         {
22375                             tag: 'button',
22376                             cls: 'btn btn-info ok',
22377                             html: 'OK'
22378                         }
22379                     ]
22380                 }
22381
22382                 ]
22383             }
22384         ]
22385     }
22386 });
22387
22388 Roo.apply(Roo.bootstrap.TimeField,  {
22389   
22390     template : {
22391         tag: 'div',
22392         cls: 'datepicker dropdown-menu',
22393         cn: [
22394             {
22395                 tag: 'div',
22396                 cls: 'datepicker-time',
22397                 cn: [
22398                 {
22399                     tag: 'table',
22400                     cls: 'table-condensed',
22401                     cn:[
22402                     Roo.bootstrap.TimeField.content,
22403                     Roo.bootstrap.TimeField.footer
22404                     ]
22405                 }
22406                 ]
22407             }
22408         ]
22409     }
22410 });
22411
22412  
22413
22414  /*
22415  * - LGPL
22416  *
22417  * MonthField
22418  * 
22419  */
22420
22421 /**
22422  * @class Roo.bootstrap.MonthField
22423  * @extends Roo.bootstrap.Input
22424  * Bootstrap MonthField class
22425  * 
22426  * @cfg {String} language default en
22427  * 
22428  * @constructor
22429  * Create a new MonthField
22430  * @param {Object} config The config object
22431  */
22432
22433 Roo.bootstrap.MonthField = function(config){
22434     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22435     
22436     this.addEvents({
22437         /**
22438          * @event show
22439          * Fires when this field show.
22440          * @param {Roo.bootstrap.MonthField} this
22441          * @param {Mixed} date The date value
22442          */
22443         show : true,
22444         /**
22445          * @event show
22446          * Fires when this field hide.
22447          * @param {Roo.bootstrap.MonthField} this
22448          * @param {Mixed} date The date value
22449          */
22450         hide : true,
22451         /**
22452          * @event select
22453          * Fires when select a date.
22454          * @param {Roo.bootstrap.MonthField} this
22455          * @param {String} oldvalue The old value
22456          * @param {String} newvalue The new value
22457          */
22458         select : true
22459     });
22460 };
22461
22462 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22463     
22464     onRender: function(ct, position)
22465     {
22466         
22467         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22468         
22469         this.language = this.language || 'en';
22470         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22471         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22472         
22473         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22474         this.isInline = false;
22475         this.isInput = true;
22476         this.component = this.el.select('.add-on', true).first() || false;
22477         this.component = (this.component && this.component.length === 0) ? false : this.component;
22478         this.hasInput = this.component && this.inputEL().length;
22479         
22480         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22481         
22482         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22483         
22484         this.picker().on('mousedown', this.onMousedown, this);
22485         this.picker().on('click', this.onClick, this);
22486         
22487         this.picker().addClass('datepicker-dropdown');
22488         
22489         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22490             v.setStyle('width', '189px');
22491         });
22492         
22493         this.fillMonths();
22494         
22495         this.update();
22496         
22497         if(this.isInline) {
22498             this.show();
22499         }
22500         
22501     },
22502     
22503     setValue: function(v, suppressEvent)
22504     {   
22505         var o = this.getValue();
22506         
22507         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22508         
22509         this.update();
22510
22511         if(suppressEvent !== true){
22512             this.fireEvent('select', this, o, v);
22513         }
22514         
22515     },
22516     
22517     getValue: function()
22518     {
22519         return this.value;
22520     },
22521     
22522     onClick: function(e) 
22523     {
22524         e.stopPropagation();
22525         e.preventDefault();
22526         
22527         var target = e.getTarget();
22528         
22529         if(target.nodeName.toLowerCase() === 'i'){
22530             target = Roo.get(target).dom.parentNode;
22531         }
22532         
22533         var nodeName = target.nodeName;
22534         var className = target.className;
22535         var html = target.innerHTML;
22536         
22537         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22538             return;
22539         }
22540         
22541         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22542         
22543         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22544         
22545         this.hide();
22546                         
22547     },
22548     
22549     picker : function()
22550     {
22551         return this.pickerEl;
22552     },
22553     
22554     fillMonths: function()
22555     {    
22556         var i = 0;
22557         var months = this.picker().select('>.datepicker-months td', true).first();
22558         
22559         months.dom.innerHTML = '';
22560         
22561         while (i < 12) {
22562             var month = {
22563                 tag: 'span',
22564                 cls: 'month',
22565                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22566             };
22567             
22568             months.createChild(month);
22569         }
22570         
22571     },
22572     
22573     update: function()
22574     {
22575         var _this = this;
22576         
22577         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22578             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22579         }
22580         
22581         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22582             e.removeClass('active');
22583             
22584             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22585                 e.addClass('active');
22586             }
22587         })
22588     },
22589     
22590     place: function()
22591     {
22592         if(this.isInline) {
22593             return;
22594         }
22595         
22596         this.picker().removeClass(['bottom', 'top']);
22597         
22598         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22599             /*
22600              * place to the top of element!
22601              *
22602              */
22603             
22604             this.picker().addClass('top');
22605             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22606             
22607             return;
22608         }
22609         
22610         this.picker().addClass('bottom');
22611         
22612         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22613     },
22614     
22615     onFocus : function()
22616     {
22617         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22618         this.show();
22619     },
22620     
22621     onBlur : function()
22622     {
22623         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22624         
22625         var d = this.inputEl().getValue();
22626         
22627         this.setValue(d);
22628                 
22629         this.hide();
22630     },
22631     
22632     show : function()
22633     {
22634         this.picker().show();
22635         this.picker().select('>.datepicker-months', true).first().show();
22636         this.update();
22637         this.place();
22638         
22639         this.fireEvent('show', this, this.date);
22640     },
22641     
22642     hide : function()
22643     {
22644         if(this.isInline) {
22645             return;
22646         }
22647         this.picker().hide();
22648         this.fireEvent('hide', this, this.date);
22649         
22650     },
22651     
22652     onMousedown: function(e)
22653     {
22654         e.stopPropagation();
22655         e.preventDefault();
22656     },
22657     
22658     keyup: function(e)
22659     {
22660         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22661         this.update();
22662     },
22663
22664     fireKey: function(e)
22665     {
22666         if (!this.picker().isVisible()){
22667             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22668                 this.show();
22669             }
22670             return;
22671         }
22672         
22673         var dir;
22674         
22675         switch(e.keyCode){
22676             case 27: // escape
22677                 this.hide();
22678                 e.preventDefault();
22679                 break;
22680             case 37: // left
22681             case 39: // right
22682                 dir = e.keyCode == 37 ? -1 : 1;
22683                 
22684                 this.vIndex = this.vIndex + dir;
22685                 
22686                 if(this.vIndex < 0){
22687                     this.vIndex = 0;
22688                 }
22689                 
22690                 if(this.vIndex > 11){
22691                     this.vIndex = 11;
22692                 }
22693                 
22694                 if(isNaN(this.vIndex)){
22695                     this.vIndex = 0;
22696                 }
22697                 
22698                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22699                 
22700                 break;
22701             case 38: // up
22702             case 40: // down
22703                 
22704                 dir = e.keyCode == 38 ? -1 : 1;
22705                 
22706                 this.vIndex = this.vIndex + dir * 4;
22707                 
22708                 if(this.vIndex < 0){
22709                     this.vIndex = 0;
22710                 }
22711                 
22712                 if(this.vIndex > 11){
22713                     this.vIndex = 11;
22714                 }
22715                 
22716                 if(isNaN(this.vIndex)){
22717                     this.vIndex = 0;
22718                 }
22719                 
22720                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22721                 break;
22722                 
22723             case 13: // enter
22724                 
22725                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22726                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22727                 }
22728                 
22729                 this.hide();
22730                 e.preventDefault();
22731                 break;
22732             case 9: // tab
22733                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22734                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22735                 }
22736                 this.hide();
22737                 break;
22738             case 16: // shift
22739             case 17: // ctrl
22740             case 18: // alt
22741                 break;
22742             default :
22743                 this.hide();
22744                 
22745         }
22746     },
22747     
22748     remove: function() 
22749     {
22750         this.picker().remove();
22751     }
22752    
22753 });
22754
22755 Roo.apply(Roo.bootstrap.MonthField,  {
22756     
22757     content : {
22758         tag: 'tbody',
22759         cn: [
22760         {
22761             tag: 'tr',
22762             cn: [
22763             {
22764                 tag: 'td',
22765                 colspan: '7'
22766             }
22767             ]
22768         }
22769         ]
22770     },
22771     
22772     dates:{
22773         en: {
22774             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22775             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22776         }
22777     }
22778 });
22779
22780 Roo.apply(Roo.bootstrap.MonthField,  {
22781   
22782     template : {
22783         tag: 'div',
22784         cls: 'datepicker dropdown-menu roo-dynamic',
22785         cn: [
22786             {
22787                 tag: 'div',
22788                 cls: 'datepicker-months',
22789                 cn: [
22790                 {
22791                     tag: 'table',
22792                     cls: 'table-condensed',
22793                     cn:[
22794                         Roo.bootstrap.DateField.content
22795                     ]
22796                 }
22797                 ]
22798             }
22799         ]
22800     }
22801 });
22802
22803  
22804
22805  
22806  /*
22807  * - LGPL
22808  *
22809  * CheckBox
22810  * 
22811  */
22812
22813 /**
22814  * @class Roo.bootstrap.CheckBox
22815  * @extends Roo.bootstrap.Input
22816  * Bootstrap CheckBox class
22817  * 
22818  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22819  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22820  * @cfg {String} boxLabel The text that appears beside the checkbox
22821  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22822  * @cfg {Boolean} checked initnal the element
22823  * @cfg {Boolean} inline inline the element (default false)
22824  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22825  * @cfg {String} tooltip label tooltip
22826  * 
22827  * @constructor
22828  * Create a new CheckBox
22829  * @param {Object} config The config object
22830  */
22831
22832 Roo.bootstrap.CheckBox = function(config){
22833     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22834    
22835     this.addEvents({
22836         /**
22837         * @event check
22838         * Fires when the element is checked or unchecked.
22839         * @param {Roo.bootstrap.CheckBox} this This input
22840         * @param {Boolean} checked The new checked value
22841         */
22842        check : true,
22843        /**
22844         * @event click
22845         * Fires when the element is click.
22846         * @param {Roo.bootstrap.CheckBox} this This input
22847         */
22848        click : true
22849     });
22850     
22851 };
22852
22853 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22854   
22855     inputType: 'checkbox',
22856     inputValue: 1,
22857     valueOff: 0,
22858     boxLabel: false,
22859     checked: false,
22860     weight : false,
22861     inline: false,
22862     tooltip : '',
22863     
22864     // checkbox success does not make any sense really.. 
22865     invalidClass : "",
22866     validClass : "",
22867     
22868     
22869     getAutoCreate : function()
22870     {
22871         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22872         
22873         var id = Roo.id();
22874         
22875         var cfg = {};
22876         
22877         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22878         
22879         if(this.inline){
22880             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22881         }
22882         
22883         var input =  {
22884             tag: 'input',
22885             id : id,
22886             type : this.inputType,
22887             value : this.inputValue,
22888             cls : 'roo-' + this.inputType, //'form-box',
22889             placeholder : this.placeholder || ''
22890             
22891         };
22892         
22893         if(this.inputType != 'radio'){
22894             var hidden =  {
22895                 tag: 'input',
22896                 type : 'hidden',
22897                 cls : 'roo-hidden-value',
22898                 value : this.checked ? this.inputValue : this.valueOff
22899             };
22900         }
22901         
22902             
22903         if (this.weight) { // Validity check?
22904             cfg.cls += " " + this.inputType + "-" + this.weight;
22905         }
22906         
22907         if (this.disabled) {
22908             input.disabled=true;
22909         }
22910         
22911         if(this.checked){
22912             input.checked = this.checked;
22913         }
22914         
22915         if (this.name) {
22916             
22917             input.name = this.name;
22918             
22919             if(this.inputType != 'radio'){
22920                 hidden.name = this.name;
22921                 input.name = '_hidden_' + this.name;
22922             }
22923         }
22924         
22925         if (this.size) {
22926             input.cls += ' input-' + this.size;
22927         }
22928         
22929         var settings=this;
22930         
22931         ['xs','sm','md','lg'].map(function(size){
22932             if (settings[size]) {
22933                 cfg.cls += ' col-' + size + '-' + settings[size];
22934             }
22935         });
22936         
22937         var inputblock = input;
22938          
22939         if (this.before || this.after) {
22940             
22941             inputblock = {
22942                 cls : 'input-group',
22943                 cn :  [] 
22944             };
22945             
22946             if (this.before) {
22947                 inputblock.cn.push({
22948                     tag :'span',
22949                     cls : 'input-group-addon',
22950                     html : this.before
22951                 });
22952             }
22953             
22954             inputblock.cn.push(input);
22955             
22956             if(this.inputType != 'radio'){
22957                 inputblock.cn.push(hidden);
22958             }
22959             
22960             if (this.after) {
22961                 inputblock.cn.push({
22962                     tag :'span',
22963                     cls : 'input-group-addon',
22964                     html : this.after
22965                 });
22966             }
22967             
22968         }
22969         var boxLabelCfg = false;
22970         
22971         if(this.boxLabel){
22972            
22973             boxLabelCfg = {
22974                 tag: 'label',
22975                 //'for': id, // box label is handled by onclick - so no for...
22976                 cls: 'box-label',
22977                 html: this.boxLabel
22978             };
22979             if(this.tooltip){
22980                 boxLabelCfg.tooltip = this.tooltip;
22981             }
22982              
22983         }
22984         
22985         
22986         if (align ==='left' && this.fieldLabel.length) {
22987 //                Roo.log("left and has label");
22988             cfg.cn = [
22989                 {
22990                     tag: 'label',
22991                     'for' :  id,
22992                     cls : 'control-label',
22993                     html : this.fieldLabel
22994                 },
22995                 {
22996                     cls : "", 
22997                     cn: [
22998                         inputblock
22999                     ]
23000                 }
23001             ];
23002             
23003             if (boxLabelCfg) {
23004                 cfg.cn[1].cn.push(boxLabelCfg);
23005             }
23006             
23007             if(this.labelWidth > 12){
23008                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23009             }
23010             
23011             if(this.labelWidth < 13 && this.labelmd == 0){
23012                 this.labelmd = this.labelWidth;
23013             }
23014             
23015             if(this.labellg > 0){
23016                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23017                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23018             }
23019             
23020             if(this.labelmd > 0){
23021                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23022                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23023             }
23024             
23025             if(this.labelsm > 0){
23026                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23027                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23028             }
23029             
23030             if(this.labelxs > 0){
23031                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23032                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23033             }
23034             
23035         } else if ( this.fieldLabel.length) {
23036 //                Roo.log(" label");
23037                 cfg.cn = [
23038                    
23039                     {
23040                         tag: this.boxLabel ? 'span' : 'label',
23041                         'for': id,
23042                         cls: 'control-label box-input-label',
23043                         //cls : 'input-group-addon',
23044                         html : this.fieldLabel
23045                     },
23046                     
23047                     inputblock
23048                     
23049                 ];
23050                 if (boxLabelCfg) {
23051                     cfg.cn.push(boxLabelCfg);
23052                 }
23053
23054         } else {
23055             
23056 //                Roo.log(" no label && no align");
23057                 cfg.cn = [  inputblock ] ;
23058                 if (boxLabelCfg) {
23059                     cfg.cn.push(boxLabelCfg);
23060                 }
23061
23062                 
23063         }
23064         
23065        
23066         
23067         if(this.inputType != 'radio'){
23068             cfg.cn.push(hidden);
23069         }
23070         
23071         return cfg;
23072         
23073     },
23074     
23075     /**
23076      * return the real input element.
23077      */
23078     inputEl: function ()
23079     {
23080         return this.el.select('input.roo-' + this.inputType,true).first();
23081     },
23082     hiddenEl: function ()
23083     {
23084         return this.el.select('input.roo-hidden-value',true).first();
23085     },
23086     
23087     labelEl: function()
23088     {
23089         return this.el.select('label.control-label',true).first();
23090     },
23091     /* depricated... */
23092     
23093     label: function()
23094     {
23095         return this.labelEl();
23096     },
23097     
23098     boxLabelEl: function()
23099     {
23100         return this.el.select('label.box-label',true).first();
23101     },
23102     
23103     initEvents : function()
23104     {
23105 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23106         
23107         this.inputEl().on('click', this.onClick,  this);
23108         
23109         if (this.boxLabel) { 
23110             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23111         }
23112         
23113         this.startValue = this.getValue();
23114         
23115         if(this.groupId){
23116             Roo.bootstrap.CheckBox.register(this);
23117         }
23118     },
23119     
23120     onClick : function(e)
23121     {   
23122         if(this.fireEvent('click', this, e) !== false){
23123             this.setChecked(!this.checked);
23124         }
23125         
23126     },
23127     
23128     setChecked : function(state,suppressEvent)
23129     {
23130         this.startValue = this.getValue();
23131
23132         if(this.inputType == 'radio'){
23133             
23134             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23135                 e.dom.checked = false;
23136             });
23137             
23138             this.inputEl().dom.checked = true;
23139             
23140             this.inputEl().dom.value = this.inputValue;
23141             
23142             if(suppressEvent !== true){
23143                 this.fireEvent('check', this, true);
23144             }
23145             
23146             this.validate();
23147             
23148             return;
23149         }
23150         
23151         this.checked = state;
23152         
23153         this.inputEl().dom.checked = state;
23154         
23155         
23156         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23157         
23158         if(suppressEvent !== true){
23159             this.fireEvent('check', this, state);
23160         }
23161         
23162         this.validate();
23163     },
23164     
23165     getValue : function()
23166     {
23167         if(this.inputType == 'radio'){
23168             return this.getGroupValue();
23169         }
23170         
23171         return this.hiddenEl().dom.value;
23172         
23173     },
23174     
23175     getGroupValue : function()
23176     {
23177         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23178             return '';
23179         }
23180         
23181         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23182     },
23183     
23184     setValue : function(v,suppressEvent)
23185     {
23186         if(this.inputType == 'radio'){
23187             this.setGroupValue(v, suppressEvent);
23188             return;
23189         }
23190         
23191         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23192         
23193         this.validate();
23194     },
23195     
23196     setGroupValue : function(v, suppressEvent)
23197     {
23198         this.startValue = this.getValue();
23199         
23200         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23201             e.dom.checked = false;
23202             
23203             if(e.dom.value == v){
23204                 e.dom.checked = true;
23205             }
23206         });
23207         
23208         if(suppressEvent !== true){
23209             this.fireEvent('check', this, true);
23210         }
23211
23212         this.validate();
23213         
23214         return;
23215     },
23216     
23217     validate : function()
23218     {
23219         if(this.getVisibilityEl().hasClass('hidden')){
23220             return true;
23221         }
23222         
23223         if(
23224                 this.disabled || 
23225                 (this.inputType == 'radio' && this.validateRadio()) ||
23226                 (this.inputType == 'checkbox' && this.validateCheckbox())
23227         ){
23228             this.markValid();
23229             return true;
23230         }
23231         
23232         this.markInvalid();
23233         return false;
23234     },
23235     
23236     validateRadio : function()
23237     {
23238         if(this.getVisibilityEl().hasClass('hidden')){
23239             return true;
23240         }
23241         
23242         if(this.allowBlank){
23243             return true;
23244         }
23245         
23246         var valid = false;
23247         
23248         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23249             if(!e.dom.checked){
23250                 return;
23251             }
23252             
23253             valid = true;
23254             
23255             return false;
23256         });
23257         
23258         return valid;
23259     },
23260     
23261     validateCheckbox : function()
23262     {
23263         if(!this.groupId){
23264             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23265             //return (this.getValue() == this.inputValue) ? true : false;
23266         }
23267         
23268         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23269         
23270         if(!group){
23271             return false;
23272         }
23273         
23274         var r = false;
23275         
23276         for(var i in group){
23277             if(group[i].el.isVisible(true)){
23278                 r = false;
23279                 break;
23280             }
23281             
23282             r = true;
23283         }
23284         
23285         for(var i in group){
23286             if(r){
23287                 break;
23288             }
23289             
23290             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23291         }
23292         
23293         return r;
23294     },
23295     
23296     /**
23297      * Mark this field as valid
23298      */
23299     markValid : function()
23300     {
23301         var _this = this;
23302         
23303         this.fireEvent('valid', this);
23304         
23305         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23306         
23307         if(this.groupId){
23308             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23309         }
23310         
23311         if(label){
23312             label.markValid();
23313         }
23314
23315         if(this.inputType == 'radio'){
23316             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23317                 var fg = e.findParent('.form-group', false, true);
23318                 if (Roo.bootstrap.version == 3) {
23319                     fg.removeClass([_this.invalidClass, _this.validClass]);
23320                     fg.addClass(_this.validClass);
23321                 } else {
23322                     fg.removeClass(['is-valid', 'is-invalid']);
23323                     fg.addClass('is-valid');
23324                 }
23325             });
23326             
23327             return;
23328         }
23329
23330         if(!this.groupId){
23331             var fg = this.el.findParent('.form-group', false, true);
23332             if (Roo.bootstrap.version == 3) {
23333                 fg.removeClass([this.invalidClass, this.validClass]);
23334                 fg.addClass(this.validClass);
23335             } else {
23336                 fg.removeClass(['is-valid', 'is-invalid']);
23337                 fg.addClass('is-valid');
23338             }
23339             return;
23340         }
23341         
23342         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23343         
23344         if(!group){
23345             return;
23346         }
23347         
23348         for(var i in group){
23349             var fg = group[i].el.findParent('.form-group', false, true);
23350             if (Roo.bootstrap.version == 3) {
23351                 fg.removeClass([this.invalidClass, this.validClass]);
23352                 fg.addClass(this.validClass);
23353             } else {
23354                 fg.removeClass(['is-valid', 'is-invalid']);
23355                 fg.addClass('is-valid');
23356             }
23357         }
23358     },
23359     
23360      /**
23361      * Mark this field as invalid
23362      * @param {String} msg The validation message
23363      */
23364     markInvalid : function(msg)
23365     {
23366         if(this.allowBlank){
23367             return;
23368         }
23369         
23370         var _this = this;
23371         
23372         this.fireEvent('invalid', this, msg);
23373         
23374         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23375         
23376         if(this.groupId){
23377             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23378         }
23379         
23380         if(label){
23381             label.markInvalid();
23382         }
23383             
23384         if(this.inputType == 'radio'){
23385             
23386             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23387                 var fg = e.findParent('.form-group', false, true);
23388                 if (Roo.bootstrap.version == 3) {
23389                     fg.removeClass([_this.invalidClass, _this.validClass]);
23390                     fg.addClass(_this.invalidClass);
23391                 } else {
23392                     fg.removeClass(['is-invalid', 'is-valid']);
23393                     fg.addClass('is-invalid');
23394                 }
23395             });
23396             
23397             return;
23398         }
23399         
23400         if(!this.groupId){
23401             var fg = this.el.findParent('.form-group', false, true);
23402             if (Roo.bootstrap.version == 3) {
23403                 fg.removeClass([_this.invalidClass, _this.validClass]);
23404                 fg.addClass(_this.invalidClass);
23405             } else {
23406                 fg.removeClass(['is-invalid', 'is-valid']);
23407                 fg.addClass('is-invalid');
23408             }
23409             return;
23410         }
23411         
23412         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23413         
23414         if(!group){
23415             return;
23416         }
23417         
23418         for(var i in group){
23419             var fg = group[i].el.findParent('.form-group', false, true);
23420             if (Roo.bootstrap.version == 3) {
23421                 fg.removeClass([_this.invalidClass, _this.validClass]);
23422                 fg.addClass(_this.invalidClass);
23423             } else {
23424                 fg.removeClass(['is-invalid', 'is-valid']);
23425                 fg.addClass('is-invalid');
23426             }
23427         }
23428         
23429     },
23430     
23431     clearInvalid : function()
23432     {
23433         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23434         
23435         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23436         
23437         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23438         
23439         if (label && label.iconEl) {
23440             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23441             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23442         }
23443     },
23444     
23445     disable : function()
23446     {
23447         if(this.inputType != 'radio'){
23448             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23449             return;
23450         }
23451         
23452         var _this = this;
23453         
23454         if(this.rendered){
23455             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23456                 _this.getActionEl().addClass(this.disabledClass);
23457                 e.dom.disabled = true;
23458             });
23459         }
23460         
23461         this.disabled = true;
23462         this.fireEvent("disable", this);
23463         return this;
23464     },
23465
23466     enable : function()
23467     {
23468         if(this.inputType != 'radio'){
23469             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23470             return;
23471         }
23472         
23473         var _this = this;
23474         
23475         if(this.rendered){
23476             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23477                 _this.getActionEl().removeClass(this.disabledClass);
23478                 e.dom.disabled = false;
23479             });
23480         }
23481         
23482         this.disabled = false;
23483         this.fireEvent("enable", this);
23484         return this;
23485     },
23486     
23487     setBoxLabel : function(v)
23488     {
23489         this.boxLabel = v;
23490         
23491         if(this.rendered){
23492             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23493         }
23494     }
23495
23496 });
23497
23498 Roo.apply(Roo.bootstrap.CheckBox, {
23499     
23500     groups: {},
23501     
23502      /**
23503     * register a CheckBox Group
23504     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23505     */
23506     register : function(checkbox)
23507     {
23508         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23509             this.groups[checkbox.groupId] = {};
23510         }
23511         
23512         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23513             return;
23514         }
23515         
23516         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23517         
23518     },
23519     /**
23520     * fetch a CheckBox Group based on the group ID
23521     * @param {string} the group ID
23522     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23523     */
23524     get: function(groupId) {
23525         if (typeof(this.groups[groupId]) == 'undefined') {
23526             return false;
23527         }
23528         
23529         return this.groups[groupId] ;
23530     }
23531     
23532     
23533 });
23534 /*
23535  * - LGPL
23536  *
23537  * RadioItem
23538  * 
23539  */
23540
23541 /**
23542  * @class Roo.bootstrap.Radio
23543  * @extends Roo.bootstrap.Component
23544  * Bootstrap Radio class
23545  * @cfg {String} boxLabel - the label associated
23546  * @cfg {String} value - the value of radio
23547  * 
23548  * @constructor
23549  * Create a new Radio
23550  * @param {Object} config The config object
23551  */
23552 Roo.bootstrap.Radio = function(config){
23553     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23554     
23555 };
23556
23557 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23558     
23559     boxLabel : '',
23560     
23561     value : '',
23562     
23563     getAutoCreate : function()
23564     {
23565         var cfg = {
23566             tag : 'div',
23567             cls : 'form-group radio',
23568             cn : [
23569                 {
23570                     tag : 'label',
23571                     cls : 'box-label',
23572                     html : this.boxLabel
23573                 }
23574             ]
23575         };
23576         
23577         return cfg;
23578     },
23579     
23580     initEvents : function() 
23581     {
23582         this.parent().register(this);
23583         
23584         this.el.on('click', this.onClick, this);
23585         
23586     },
23587     
23588     onClick : function(e)
23589     {
23590         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23591             this.setChecked(true);
23592         }
23593     },
23594     
23595     setChecked : function(state, suppressEvent)
23596     {
23597         this.parent().setValue(this.value, suppressEvent);
23598         
23599     },
23600     
23601     setBoxLabel : function(v)
23602     {
23603         this.boxLabel = v;
23604         
23605         if(this.rendered){
23606             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23607         }
23608     }
23609     
23610 });
23611  
23612
23613  /*
23614  * - LGPL
23615  *
23616  * Input
23617  * 
23618  */
23619
23620 /**
23621  * @class Roo.bootstrap.SecurePass
23622  * @extends Roo.bootstrap.Input
23623  * Bootstrap SecurePass class
23624  *
23625  * 
23626  * @constructor
23627  * Create a new SecurePass
23628  * @param {Object} config The config object
23629  */
23630  
23631 Roo.bootstrap.SecurePass = function (config) {
23632     // these go here, so the translation tool can replace them..
23633     this.errors = {
23634         PwdEmpty: "Please type a password, and then retype it to confirm.",
23635         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23636         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23637         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23638         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23639         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23640         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23641         TooWeak: "Your password is Too Weak."
23642     },
23643     this.meterLabel = "Password strength:";
23644     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23645     this.meterClass = [
23646         "roo-password-meter-tooweak", 
23647         "roo-password-meter-weak", 
23648         "roo-password-meter-medium", 
23649         "roo-password-meter-strong", 
23650         "roo-password-meter-grey"
23651     ];
23652     
23653     this.errors = {};
23654     
23655     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23656 }
23657
23658 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23659     /**
23660      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23661      * {
23662      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23663      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23664      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23665      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23666      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23667      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23668      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23669      * })
23670      */
23671     // private
23672     
23673     meterWidth: 300,
23674     errorMsg :'',    
23675     errors: false,
23676     imageRoot: '/',
23677     /**
23678      * @cfg {String/Object} Label for the strength meter (defaults to
23679      * 'Password strength:')
23680      */
23681     // private
23682     meterLabel: '',
23683     /**
23684      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23685      * ['Weak', 'Medium', 'Strong'])
23686      */
23687     // private    
23688     pwdStrengths: false,    
23689     // private
23690     strength: 0,
23691     // private
23692     _lastPwd: null,
23693     // private
23694     kCapitalLetter: 0,
23695     kSmallLetter: 1,
23696     kDigit: 2,
23697     kPunctuation: 3,
23698     
23699     insecure: false,
23700     // private
23701     initEvents: function ()
23702     {
23703         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23704
23705         if (this.el.is('input[type=password]') && Roo.isSafari) {
23706             this.el.on('keydown', this.SafariOnKeyDown, this);
23707         }
23708
23709         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23710     },
23711     // private
23712     onRender: function (ct, position)
23713     {
23714         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23715         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23716         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23717
23718         this.trigger.createChild({
23719                    cn: [
23720                     {
23721                     //id: 'PwdMeter',
23722                     tag: 'div',
23723                     cls: 'roo-password-meter-grey col-xs-12',
23724                     style: {
23725                         //width: 0,
23726                         //width: this.meterWidth + 'px'                                                
23727                         }
23728                     },
23729                     {                            
23730                          cls: 'roo-password-meter-text'                          
23731                     }
23732                 ]            
23733         });
23734
23735          
23736         if (this.hideTrigger) {
23737             this.trigger.setDisplayed(false);
23738         }
23739         this.setSize(this.width || '', this.height || '');
23740     },
23741     // private
23742     onDestroy: function ()
23743     {
23744         if (this.trigger) {
23745             this.trigger.removeAllListeners();
23746             this.trigger.remove();
23747         }
23748         if (this.wrap) {
23749             this.wrap.remove();
23750         }
23751         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23752     },
23753     // private
23754     checkStrength: function ()
23755     {
23756         var pwd = this.inputEl().getValue();
23757         if (pwd == this._lastPwd) {
23758             return;
23759         }
23760
23761         var strength;
23762         if (this.ClientSideStrongPassword(pwd)) {
23763             strength = 3;
23764         } else if (this.ClientSideMediumPassword(pwd)) {
23765             strength = 2;
23766         } else if (this.ClientSideWeakPassword(pwd)) {
23767             strength = 1;
23768         } else {
23769             strength = 0;
23770         }
23771         
23772         Roo.log('strength1: ' + strength);
23773         
23774         //var pm = this.trigger.child('div/div/div').dom;
23775         var pm = this.trigger.child('div/div');
23776         pm.removeClass(this.meterClass);
23777         pm.addClass(this.meterClass[strength]);
23778                 
23779         
23780         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23781                 
23782         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23783         
23784         this._lastPwd = pwd;
23785     },
23786     reset: function ()
23787     {
23788         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23789         
23790         this._lastPwd = '';
23791         
23792         var pm = this.trigger.child('div/div');
23793         pm.removeClass(this.meterClass);
23794         pm.addClass('roo-password-meter-grey');        
23795         
23796         
23797         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23798         
23799         pt.innerHTML = '';
23800         this.inputEl().dom.type='password';
23801     },
23802     // private
23803     validateValue: function (value)
23804     {
23805         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23806             return false;
23807         }
23808         if (value.length == 0) {
23809             if (this.allowBlank) {
23810                 this.clearInvalid();
23811                 return true;
23812             }
23813
23814             this.markInvalid(this.errors.PwdEmpty);
23815             this.errorMsg = this.errors.PwdEmpty;
23816             return false;
23817         }
23818         
23819         if(this.insecure){
23820             return true;
23821         }
23822         
23823         if (!value.match(/[\x21-\x7e]+/)) {
23824             this.markInvalid(this.errors.PwdBadChar);
23825             this.errorMsg = this.errors.PwdBadChar;
23826             return false;
23827         }
23828         if (value.length < 6) {
23829             this.markInvalid(this.errors.PwdShort);
23830             this.errorMsg = this.errors.PwdShort;
23831             return false;
23832         }
23833         if (value.length > 16) {
23834             this.markInvalid(this.errors.PwdLong);
23835             this.errorMsg = this.errors.PwdLong;
23836             return false;
23837         }
23838         var strength;
23839         if (this.ClientSideStrongPassword(value)) {
23840             strength = 3;
23841         } else if (this.ClientSideMediumPassword(value)) {
23842             strength = 2;
23843         } else if (this.ClientSideWeakPassword(value)) {
23844             strength = 1;
23845         } else {
23846             strength = 0;
23847         }
23848
23849         
23850         if (strength < 2) {
23851             //this.markInvalid(this.errors.TooWeak);
23852             this.errorMsg = this.errors.TooWeak;
23853             //return false;
23854         }
23855         
23856         
23857         console.log('strength2: ' + strength);
23858         
23859         //var pm = this.trigger.child('div/div/div').dom;
23860         
23861         var pm = this.trigger.child('div/div');
23862         pm.removeClass(this.meterClass);
23863         pm.addClass(this.meterClass[strength]);
23864                 
23865         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23866                 
23867         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23868         
23869         this.errorMsg = ''; 
23870         return true;
23871     },
23872     // private
23873     CharacterSetChecks: function (type)
23874     {
23875         this.type = type;
23876         this.fResult = false;
23877     },
23878     // private
23879     isctype: function (character, type)
23880     {
23881         switch (type) {  
23882             case this.kCapitalLetter:
23883                 if (character >= 'A' && character <= 'Z') {
23884                     return true;
23885                 }
23886                 break;
23887             
23888             case this.kSmallLetter:
23889                 if (character >= 'a' && character <= 'z') {
23890                     return true;
23891                 }
23892                 break;
23893             
23894             case this.kDigit:
23895                 if (character >= '0' && character <= '9') {
23896                     return true;
23897                 }
23898                 break;
23899             
23900             case this.kPunctuation:
23901                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23902                     return true;
23903                 }
23904                 break;
23905             
23906             default:
23907                 return false;
23908         }
23909
23910     },
23911     // private
23912     IsLongEnough: function (pwd, size)
23913     {
23914         return !(pwd == null || isNaN(size) || pwd.length < size);
23915     },
23916     // private
23917     SpansEnoughCharacterSets: function (word, nb)
23918     {
23919         if (!this.IsLongEnough(word, nb))
23920         {
23921             return false;
23922         }
23923
23924         var characterSetChecks = new Array(
23925             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23926             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23927         );
23928         
23929         for (var index = 0; index < word.length; ++index) {
23930             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23931                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23932                     characterSetChecks[nCharSet].fResult = true;
23933                     break;
23934                 }
23935             }
23936         }
23937
23938         var nCharSets = 0;
23939         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23940             if (characterSetChecks[nCharSet].fResult) {
23941                 ++nCharSets;
23942             }
23943         }
23944
23945         if (nCharSets < nb) {
23946             return false;
23947         }
23948         return true;
23949     },
23950     // private
23951     ClientSideStrongPassword: function (pwd)
23952     {
23953         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23954     },
23955     // private
23956     ClientSideMediumPassword: function (pwd)
23957     {
23958         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23959     },
23960     // private
23961     ClientSideWeakPassword: function (pwd)
23962     {
23963         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23964     }
23965           
23966 })//<script type="text/javascript">
23967
23968 /*
23969  * Based  Ext JS Library 1.1.1
23970  * Copyright(c) 2006-2007, Ext JS, LLC.
23971  * LGPL
23972  *
23973  */
23974  
23975 /**
23976  * @class Roo.HtmlEditorCore
23977  * @extends Roo.Component
23978  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23979  *
23980  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23981  */
23982
23983 Roo.HtmlEditorCore = function(config){
23984     
23985     
23986     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23987     
23988     
23989     this.addEvents({
23990         /**
23991          * @event initialize
23992          * Fires when the editor is fully initialized (including the iframe)
23993          * @param {Roo.HtmlEditorCore} this
23994          */
23995         initialize: true,
23996         /**
23997          * @event activate
23998          * Fires when the editor is first receives the focus. Any insertion must wait
23999          * until after this event.
24000          * @param {Roo.HtmlEditorCore} this
24001          */
24002         activate: true,
24003          /**
24004          * @event beforesync
24005          * Fires before the textarea is updated with content from the editor iframe. Return false
24006          * to cancel the sync.
24007          * @param {Roo.HtmlEditorCore} this
24008          * @param {String} html
24009          */
24010         beforesync: true,
24011          /**
24012          * @event beforepush
24013          * Fires before the iframe editor is updated with content from the textarea. Return false
24014          * to cancel the push.
24015          * @param {Roo.HtmlEditorCore} this
24016          * @param {String} html
24017          */
24018         beforepush: true,
24019          /**
24020          * @event sync
24021          * Fires when the textarea is updated with content from the editor iframe.
24022          * @param {Roo.HtmlEditorCore} this
24023          * @param {String} html
24024          */
24025         sync: true,
24026          /**
24027          * @event push
24028          * Fires when the iframe editor is updated with content from the textarea.
24029          * @param {Roo.HtmlEditorCore} this
24030          * @param {String} html
24031          */
24032         push: true,
24033         
24034         /**
24035          * @event editorevent
24036          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24037          * @param {Roo.HtmlEditorCore} this
24038          */
24039         editorevent: true
24040         
24041     });
24042     
24043     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24044     
24045     // defaults : white / black...
24046     this.applyBlacklists();
24047     
24048     
24049     
24050 };
24051
24052
24053 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24054
24055
24056      /**
24057      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24058      */
24059     
24060     owner : false,
24061     
24062      /**
24063      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24064      *                        Roo.resizable.
24065      */
24066     resizable : false,
24067      /**
24068      * @cfg {Number} height (in pixels)
24069      */   
24070     height: 300,
24071    /**
24072      * @cfg {Number} width (in pixels)
24073      */   
24074     width: 500,
24075     
24076     /**
24077      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24078      * 
24079      */
24080     stylesheets: false,
24081     
24082     // id of frame..
24083     frameId: false,
24084     
24085     // private properties
24086     validationEvent : false,
24087     deferHeight: true,
24088     initialized : false,
24089     activated : false,
24090     sourceEditMode : false,
24091     onFocus : Roo.emptyFn,
24092     iframePad:3,
24093     hideMode:'offsets',
24094     
24095     clearUp: true,
24096     
24097     // blacklist + whitelisted elements..
24098     black: false,
24099     white: false,
24100      
24101     bodyCls : '',
24102
24103     /**
24104      * Protected method that will not generally be called directly. It
24105      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24106      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24107      */
24108     getDocMarkup : function(){
24109         // body styles..
24110         var st = '';
24111         
24112         // inherit styels from page...?? 
24113         if (this.stylesheets === false) {
24114             
24115             Roo.get(document.head).select('style').each(function(node) {
24116                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24117             });
24118             
24119             Roo.get(document.head).select('link').each(function(node) { 
24120                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24121             });
24122             
24123         } else if (!this.stylesheets.length) {
24124                 // simple..
24125                 st = '<style type="text/css">' +
24126                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24127                    '</style>';
24128         } else {
24129             for (var i in this.stylesheets) { 
24130                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24131             }
24132             
24133         }
24134         
24135         st +=  '<style type="text/css">' +
24136             'IMG { cursor: pointer } ' +
24137         '</style>';
24138
24139         var cls = 'roo-htmleditor-body';
24140         
24141         if(this.bodyCls.length){
24142             cls += ' ' + this.bodyCls;
24143         }
24144         
24145         return '<html><head>' + st  +
24146             //<style type="text/css">' +
24147             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24148             //'</style>' +
24149             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24150     },
24151
24152     // private
24153     onRender : function(ct, position)
24154     {
24155         var _t = this;
24156         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24157         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24158         
24159         
24160         this.el.dom.style.border = '0 none';
24161         this.el.dom.setAttribute('tabIndex', -1);
24162         this.el.addClass('x-hidden hide');
24163         
24164         
24165         
24166         if(Roo.isIE){ // fix IE 1px bogus margin
24167             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24168         }
24169        
24170         
24171         this.frameId = Roo.id();
24172         
24173          
24174         
24175         var iframe = this.owner.wrap.createChild({
24176             tag: 'iframe',
24177             cls: 'form-control', // bootstrap..
24178             id: this.frameId,
24179             name: this.frameId,
24180             frameBorder : 'no',
24181             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24182         }, this.el
24183         );
24184         
24185         
24186         this.iframe = iframe.dom;
24187
24188          this.assignDocWin();
24189         
24190         this.doc.designMode = 'on';
24191        
24192         this.doc.open();
24193         this.doc.write(this.getDocMarkup());
24194         this.doc.close();
24195
24196         
24197         var task = { // must defer to wait for browser to be ready
24198             run : function(){
24199                 //console.log("run task?" + this.doc.readyState);
24200                 this.assignDocWin();
24201                 if(this.doc.body || this.doc.readyState == 'complete'){
24202                     try {
24203                         this.doc.designMode="on";
24204                     } catch (e) {
24205                         return;
24206                     }
24207                     Roo.TaskMgr.stop(task);
24208                     this.initEditor.defer(10, this);
24209                 }
24210             },
24211             interval : 10,
24212             duration: 10000,
24213             scope: this
24214         };
24215         Roo.TaskMgr.start(task);
24216
24217     },
24218
24219     // private
24220     onResize : function(w, h)
24221     {
24222          Roo.log('resize: ' +w + ',' + h );
24223         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24224         if(!this.iframe){
24225             return;
24226         }
24227         if(typeof w == 'number'){
24228             
24229             this.iframe.style.width = w + 'px';
24230         }
24231         if(typeof h == 'number'){
24232             
24233             this.iframe.style.height = h + 'px';
24234             if(this.doc){
24235                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24236             }
24237         }
24238         
24239     },
24240
24241     /**
24242      * Toggles the editor between standard and source edit mode.
24243      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24244      */
24245     toggleSourceEdit : function(sourceEditMode){
24246         
24247         this.sourceEditMode = sourceEditMode === true;
24248         
24249         if(this.sourceEditMode){
24250  
24251             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24252             
24253         }else{
24254             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24255             //this.iframe.className = '';
24256             this.deferFocus();
24257         }
24258         //this.setSize(this.owner.wrap.getSize());
24259         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24260     },
24261
24262     
24263   
24264
24265     /**
24266      * Protected method that will not generally be called directly. If you need/want
24267      * custom HTML cleanup, this is the method you should override.
24268      * @param {String} html The HTML to be cleaned
24269      * return {String} The cleaned HTML
24270      */
24271     cleanHtml : function(html){
24272         html = String(html);
24273         if(html.length > 5){
24274             if(Roo.isSafari){ // strip safari nonsense
24275                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24276             }
24277         }
24278         if(html == '&nbsp;'){
24279             html = '';
24280         }
24281         return html;
24282     },
24283
24284     /**
24285      * HTML Editor -> Textarea
24286      * Protected method that will not generally be called directly. Syncs the contents
24287      * of the editor iframe with the textarea.
24288      */
24289     syncValue : function(){
24290         if(this.initialized){
24291             var bd = (this.doc.body || this.doc.documentElement);
24292             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24293             var html = bd.innerHTML;
24294             if(Roo.isSafari){
24295                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24296                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24297                 if(m && m[1]){
24298                     html = '<div style="'+m[0]+'">' + html + '</div>';
24299                 }
24300             }
24301             html = this.cleanHtml(html);
24302             // fix up the special chars.. normaly like back quotes in word...
24303             // however we do not want to do this with chinese..
24304             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24305                 
24306                 var cc = match.charCodeAt();
24307
24308                 // Get the character value, handling surrogate pairs
24309                 if (match.length == 2) {
24310                     // It's a surrogate pair, calculate the Unicode code point
24311                     var high = match.charCodeAt(0) - 0xD800;
24312                     var low  = match.charCodeAt(1) - 0xDC00;
24313                     cc = (high * 0x400) + low + 0x10000;
24314                 }  else if (
24315                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24316                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24317                     (cc >= 0xf900 && cc < 0xfb00 )
24318                 ) {
24319                         return match;
24320                 }  
24321          
24322                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24323                 return "&#" + cc + ";";
24324                 
24325                 
24326             });
24327             
24328             
24329              
24330             if(this.owner.fireEvent('beforesync', this, html) !== false){
24331                 this.el.dom.value = html;
24332                 this.owner.fireEvent('sync', this, html);
24333             }
24334         }
24335     },
24336
24337     /**
24338      * Protected method that will not generally be called directly. Pushes the value of the textarea
24339      * into the iframe editor.
24340      */
24341     pushValue : function(){
24342         if(this.initialized){
24343             var v = this.el.dom.value.trim();
24344             
24345 //            if(v.length < 1){
24346 //                v = '&#160;';
24347 //            }
24348             
24349             if(this.owner.fireEvent('beforepush', this, v) !== false){
24350                 var d = (this.doc.body || this.doc.documentElement);
24351                 d.innerHTML = v;
24352                 this.cleanUpPaste();
24353                 this.el.dom.value = d.innerHTML;
24354                 this.owner.fireEvent('push', this, v);
24355             }
24356         }
24357     },
24358
24359     // private
24360     deferFocus : function(){
24361         this.focus.defer(10, this);
24362     },
24363
24364     // doc'ed in Field
24365     focus : function(){
24366         if(this.win && !this.sourceEditMode){
24367             this.win.focus();
24368         }else{
24369             this.el.focus();
24370         }
24371     },
24372     
24373     assignDocWin: function()
24374     {
24375         var iframe = this.iframe;
24376         
24377          if(Roo.isIE){
24378             this.doc = iframe.contentWindow.document;
24379             this.win = iframe.contentWindow;
24380         } else {
24381 //            if (!Roo.get(this.frameId)) {
24382 //                return;
24383 //            }
24384 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24385 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24386             
24387             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24388                 return;
24389             }
24390             
24391             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24392             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24393         }
24394     },
24395     
24396     // private
24397     initEditor : function(){
24398         //console.log("INIT EDITOR");
24399         this.assignDocWin();
24400         
24401         
24402         
24403         this.doc.designMode="on";
24404         this.doc.open();
24405         this.doc.write(this.getDocMarkup());
24406         this.doc.close();
24407         
24408         var dbody = (this.doc.body || this.doc.documentElement);
24409         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24410         // this copies styles from the containing element into thsi one..
24411         // not sure why we need all of this..
24412         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24413         
24414         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24415         //ss['background-attachment'] = 'fixed'; // w3c
24416         dbody.bgProperties = 'fixed'; // ie
24417         //Roo.DomHelper.applyStyles(dbody, ss);
24418         Roo.EventManager.on(this.doc, {
24419             //'mousedown': this.onEditorEvent,
24420             'mouseup': this.onEditorEvent,
24421             'dblclick': this.onEditorEvent,
24422             'click': this.onEditorEvent,
24423             'keyup': this.onEditorEvent,
24424             buffer:100,
24425             scope: this
24426         });
24427         if(Roo.isGecko){
24428             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24429         }
24430         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24431             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24432         }
24433         this.initialized = true;
24434
24435         this.owner.fireEvent('initialize', this);
24436         this.pushValue();
24437     },
24438
24439     // private
24440     onDestroy : function(){
24441         
24442         
24443         
24444         if(this.rendered){
24445             
24446             //for (var i =0; i < this.toolbars.length;i++) {
24447             //    // fixme - ask toolbars for heights?
24448             //    this.toolbars[i].onDestroy();
24449            // }
24450             
24451             //this.wrap.dom.innerHTML = '';
24452             //this.wrap.remove();
24453         }
24454     },
24455
24456     // private
24457     onFirstFocus : function(){
24458         
24459         this.assignDocWin();
24460         
24461         
24462         this.activated = true;
24463          
24464     
24465         if(Roo.isGecko){ // prevent silly gecko errors
24466             this.win.focus();
24467             var s = this.win.getSelection();
24468             if(!s.focusNode || s.focusNode.nodeType != 3){
24469                 var r = s.getRangeAt(0);
24470                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24471                 r.collapse(true);
24472                 this.deferFocus();
24473             }
24474             try{
24475                 this.execCmd('useCSS', true);
24476                 this.execCmd('styleWithCSS', false);
24477             }catch(e){}
24478         }
24479         this.owner.fireEvent('activate', this);
24480     },
24481
24482     // private
24483     adjustFont: function(btn){
24484         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24485         //if(Roo.isSafari){ // safari
24486         //    adjust *= 2;
24487        // }
24488         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24489         if(Roo.isSafari){ // safari
24490             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24491             v =  (v < 10) ? 10 : v;
24492             v =  (v > 48) ? 48 : v;
24493             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24494             
24495         }
24496         
24497         
24498         v = Math.max(1, v+adjust);
24499         
24500         this.execCmd('FontSize', v  );
24501     },
24502
24503     onEditorEvent : function(e)
24504     {
24505         this.owner.fireEvent('editorevent', this, e);
24506       //  this.updateToolbar();
24507         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24508     },
24509
24510     insertTag : function(tg)
24511     {
24512         // could be a bit smarter... -> wrap the current selected tRoo..
24513         if (tg.toLowerCase() == 'span' ||
24514             tg.toLowerCase() == 'code' ||
24515             tg.toLowerCase() == 'sup' ||
24516             tg.toLowerCase() == 'sub' 
24517             ) {
24518             
24519             range = this.createRange(this.getSelection());
24520             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24521             wrappingNode.appendChild(range.extractContents());
24522             range.insertNode(wrappingNode);
24523
24524             return;
24525             
24526             
24527             
24528         }
24529         this.execCmd("formatblock",   tg);
24530         
24531     },
24532     
24533     insertText : function(txt)
24534     {
24535         
24536         
24537         var range = this.createRange();
24538         range.deleteContents();
24539                //alert(Sender.getAttribute('label'));
24540                
24541         range.insertNode(this.doc.createTextNode(txt));
24542     } ,
24543     
24544      
24545
24546     /**
24547      * Executes a Midas editor command on the editor document and performs necessary focus and
24548      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24549      * @param {String} cmd The Midas command
24550      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24551      */
24552     relayCmd : function(cmd, value){
24553         this.win.focus();
24554         this.execCmd(cmd, value);
24555         this.owner.fireEvent('editorevent', this);
24556         //this.updateToolbar();
24557         this.owner.deferFocus();
24558     },
24559
24560     /**
24561      * Executes a Midas editor command directly on the editor document.
24562      * For visual commands, you should use {@link #relayCmd} instead.
24563      * <b>This should only be called after the editor is initialized.</b>
24564      * @param {String} cmd The Midas command
24565      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24566      */
24567     execCmd : function(cmd, value){
24568         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24569         this.syncValue();
24570     },
24571  
24572  
24573    
24574     /**
24575      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24576      * to insert tRoo.
24577      * @param {String} text | dom node.. 
24578      */
24579     insertAtCursor : function(text)
24580     {
24581         
24582         if(!this.activated){
24583             return;
24584         }
24585         /*
24586         if(Roo.isIE){
24587             this.win.focus();
24588             var r = this.doc.selection.createRange();
24589             if(r){
24590                 r.collapse(true);
24591                 r.pasteHTML(text);
24592                 this.syncValue();
24593                 this.deferFocus();
24594             
24595             }
24596             return;
24597         }
24598         */
24599         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24600             this.win.focus();
24601             
24602             
24603             // from jquery ui (MIT licenced)
24604             var range, node;
24605             var win = this.win;
24606             
24607             if (win.getSelection && win.getSelection().getRangeAt) {
24608                 range = win.getSelection().getRangeAt(0);
24609                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24610                 range.insertNode(node);
24611             } else if (win.document.selection && win.document.selection.createRange) {
24612                 // no firefox support
24613                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24614                 win.document.selection.createRange().pasteHTML(txt);
24615             } else {
24616                 // no firefox support
24617                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24618                 this.execCmd('InsertHTML', txt);
24619             } 
24620             
24621             this.syncValue();
24622             
24623             this.deferFocus();
24624         }
24625     },
24626  // private
24627     mozKeyPress : function(e){
24628         if(e.ctrlKey){
24629             var c = e.getCharCode(), cmd;
24630           
24631             if(c > 0){
24632                 c = String.fromCharCode(c).toLowerCase();
24633                 switch(c){
24634                     case 'b':
24635                         cmd = 'bold';
24636                         break;
24637                     case 'i':
24638                         cmd = 'italic';
24639                         break;
24640                     
24641                     case 'u':
24642                         cmd = 'underline';
24643                         break;
24644                     
24645                     case 'v':
24646                         this.cleanUpPaste.defer(100, this);
24647                         return;
24648                         
24649                 }
24650                 if(cmd){
24651                     this.win.focus();
24652                     this.execCmd(cmd);
24653                     this.deferFocus();
24654                     e.preventDefault();
24655                 }
24656                 
24657             }
24658         }
24659     },
24660
24661     // private
24662     fixKeys : function(){ // load time branching for fastest keydown performance
24663         if(Roo.isIE){
24664             return function(e){
24665                 var k = e.getKey(), r;
24666                 if(k == e.TAB){
24667                     e.stopEvent();
24668                     r = this.doc.selection.createRange();
24669                     if(r){
24670                         r.collapse(true);
24671                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24672                         this.deferFocus();
24673                     }
24674                     return;
24675                 }
24676                 
24677                 if(k == e.ENTER){
24678                     r = this.doc.selection.createRange();
24679                     if(r){
24680                         var target = r.parentElement();
24681                         if(!target || target.tagName.toLowerCase() != 'li'){
24682                             e.stopEvent();
24683                             r.pasteHTML('<br />');
24684                             r.collapse(false);
24685                             r.select();
24686                         }
24687                     }
24688                 }
24689                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24690                     this.cleanUpPaste.defer(100, this);
24691                     return;
24692                 }
24693                 
24694                 
24695             };
24696         }else if(Roo.isOpera){
24697             return function(e){
24698                 var k = e.getKey();
24699                 if(k == e.TAB){
24700                     e.stopEvent();
24701                     this.win.focus();
24702                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24703                     this.deferFocus();
24704                 }
24705                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24706                     this.cleanUpPaste.defer(100, this);
24707                     return;
24708                 }
24709                 
24710             };
24711         }else if(Roo.isSafari){
24712             return function(e){
24713                 var k = e.getKey();
24714                 
24715                 if(k == e.TAB){
24716                     e.stopEvent();
24717                     this.execCmd('InsertText','\t');
24718                     this.deferFocus();
24719                     return;
24720                 }
24721                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24722                     this.cleanUpPaste.defer(100, this);
24723                     return;
24724                 }
24725                 
24726              };
24727         }
24728     }(),
24729     
24730     getAllAncestors: function()
24731     {
24732         var p = this.getSelectedNode();
24733         var a = [];
24734         if (!p) {
24735             a.push(p); // push blank onto stack..
24736             p = this.getParentElement();
24737         }
24738         
24739         
24740         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24741             a.push(p);
24742             p = p.parentNode;
24743         }
24744         a.push(this.doc.body);
24745         return a;
24746     },
24747     lastSel : false,
24748     lastSelNode : false,
24749     
24750     
24751     getSelection : function() 
24752     {
24753         this.assignDocWin();
24754         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24755     },
24756     
24757     getSelectedNode: function() 
24758     {
24759         // this may only work on Gecko!!!
24760         
24761         // should we cache this!!!!
24762         
24763         
24764         
24765          
24766         var range = this.createRange(this.getSelection()).cloneRange();
24767         
24768         if (Roo.isIE) {
24769             var parent = range.parentElement();
24770             while (true) {
24771                 var testRange = range.duplicate();
24772                 testRange.moveToElementText(parent);
24773                 if (testRange.inRange(range)) {
24774                     break;
24775                 }
24776                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24777                     break;
24778                 }
24779                 parent = parent.parentElement;
24780             }
24781             return parent;
24782         }
24783         
24784         // is ancestor a text element.
24785         var ac =  range.commonAncestorContainer;
24786         if (ac.nodeType == 3) {
24787             ac = ac.parentNode;
24788         }
24789         
24790         var ar = ac.childNodes;
24791          
24792         var nodes = [];
24793         var other_nodes = [];
24794         var has_other_nodes = false;
24795         for (var i=0;i<ar.length;i++) {
24796             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24797                 continue;
24798             }
24799             // fullly contained node.
24800             
24801             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24802                 nodes.push(ar[i]);
24803                 continue;
24804             }
24805             
24806             // probably selected..
24807             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24808                 other_nodes.push(ar[i]);
24809                 continue;
24810             }
24811             // outer..
24812             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24813                 continue;
24814             }
24815             
24816             
24817             has_other_nodes = true;
24818         }
24819         if (!nodes.length && other_nodes.length) {
24820             nodes= other_nodes;
24821         }
24822         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24823             return false;
24824         }
24825         
24826         return nodes[0];
24827     },
24828     createRange: function(sel)
24829     {
24830         // this has strange effects when using with 
24831         // top toolbar - not sure if it's a great idea.
24832         //this.editor.contentWindow.focus();
24833         if (typeof sel != "undefined") {
24834             try {
24835                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24836             } catch(e) {
24837                 return this.doc.createRange();
24838             }
24839         } else {
24840             return this.doc.createRange();
24841         }
24842     },
24843     getParentElement: function()
24844     {
24845         
24846         this.assignDocWin();
24847         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24848         
24849         var range = this.createRange(sel);
24850          
24851         try {
24852             var p = range.commonAncestorContainer;
24853             while (p.nodeType == 3) { // text node
24854                 p = p.parentNode;
24855             }
24856             return p;
24857         } catch (e) {
24858             return null;
24859         }
24860     
24861     },
24862     /***
24863      *
24864      * Range intersection.. the hard stuff...
24865      *  '-1' = before
24866      *  '0' = hits..
24867      *  '1' = after.
24868      *         [ -- selected range --- ]
24869      *   [fail]                        [fail]
24870      *
24871      *    basically..
24872      *      if end is before start or  hits it. fail.
24873      *      if start is after end or hits it fail.
24874      *
24875      *   if either hits (but other is outside. - then it's not 
24876      *   
24877      *    
24878      **/
24879     
24880     
24881     // @see http://www.thismuchiknow.co.uk/?p=64.
24882     rangeIntersectsNode : function(range, node)
24883     {
24884         var nodeRange = node.ownerDocument.createRange();
24885         try {
24886             nodeRange.selectNode(node);
24887         } catch (e) {
24888             nodeRange.selectNodeContents(node);
24889         }
24890     
24891         var rangeStartRange = range.cloneRange();
24892         rangeStartRange.collapse(true);
24893     
24894         var rangeEndRange = range.cloneRange();
24895         rangeEndRange.collapse(false);
24896     
24897         var nodeStartRange = nodeRange.cloneRange();
24898         nodeStartRange.collapse(true);
24899     
24900         var nodeEndRange = nodeRange.cloneRange();
24901         nodeEndRange.collapse(false);
24902     
24903         return rangeStartRange.compareBoundaryPoints(
24904                  Range.START_TO_START, nodeEndRange) == -1 &&
24905                rangeEndRange.compareBoundaryPoints(
24906                  Range.START_TO_START, nodeStartRange) == 1;
24907         
24908          
24909     },
24910     rangeCompareNode : function(range, node)
24911     {
24912         var nodeRange = node.ownerDocument.createRange();
24913         try {
24914             nodeRange.selectNode(node);
24915         } catch (e) {
24916             nodeRange.selectNodeContents(node);
24917         }
24918         
24919         
24920         range.collapse(true);
24921     
24922         nodeRange.collapse(true);
24923      
24924         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24925         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24926          
24927         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24928         
24929         var nodeIsBefore   =  ss == 1;
24930         var nodeIsAfter    = ee == -1;
24931         
24932         if (nodeIsBefore && nodeIsAfter) {
24933             return 0; // outer
24934         }
24935         if (!nodeIsBefore && nodeIsAfter) {
24936             return 1; //right trailed.
24937         }
24938         
24939         if (nodeIsBefore && !nodeIsAfter) {
24940             return 2;  // left trailed.
24941         }
24942         // fully contined.
24943         return 3;
24944     },
24945
24946     // private? - in a new class?
24947     cleanUpPaste :  function()
24948     {
24949         // cleans up the whole document..
24950         Roo.log('cleanuppaste');
24951         
24952         this.cleanUpChildren(this.doc.body);
24953         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24954         if (clean != this.doc.body.innerHTML) {
24955             this.doc.body.innerHTML = clean;
24956         }
24957         
24958     },
24959     
24960     cleanWordChars : function(input) {// change the chars to hex code
24961         var he = Roo.HtmlEditorCore;
24962         
24963         var output = input;
24964         Roo.each(he.swapCodes, function(sw) { 
24965             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24966             
24967             output = output.replace(swapper, sw[1]);
24968         });
24969         
24970         return output;
24971     },
24972     
24973     
24974     cleanUpChildren : function (n)
24975     {
24976         if (!n.childNodes.length) {
24977             return;
24978         }
24979         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24980            this.cleanUpChild(n.childNodes[i]);
24981         }
24982     },
24983     
24984     
24985         
24986     
24987     cleanUpChild : function (node)
24988     {
24989         var ed = this;
24990         //console.log(node);
24991         if (node.nodeName == "#text") {
24992             // clean up silly Windows -- stuff?
24993             return; 
24994         }
24995         if (node.nodeName == "#comment") {
24996             node.parentNode.removeChild(node);
24997             // clean up silly Windows -- stuff?
24998             return; 
24999         }
25000         var lcname = node.tagName.toLowerCase();
25001         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25002         // whitelist of tags..
25003         
25004         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25005             // remove node.
25006             node.parentNode.removeChild(node);
25007             return;
25008             
25009         }
25010         
25011         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25012         
25013         // spans with no attributes - just remove them..
25014         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25015             remove_keep_children = true;
25016         }
25017         
25018         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25019         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25020         
25021         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25022         //    remove_keep_children = true;
25023         //}
25024         
25025         if (remove_keep_children) {
25026             this.cleanUpChildren(node);
25027             // inserts everything just before this node...
25028             while (node.childNodes.length) {
25029                 var cn = node.childNodes[0];
25030                 node.removeChild(cn);
25031                 node.parentNode.insertBefore(cn, node);
25032             }
25033             node.parentNode.removeChild(node);
25034             return;
25035         }
25036         
25037         if (!node.attributes || !node.attributes.length) {
25038             
25039           
25040             
25041             
25042             this.cleanUpChildren(node);
25043             return;
25044         }
25045         
25046         function cleanAttr(n,v)
25047         {
25048             
25049             if (v.match(/^\./) || v.match(/^\//)) {
25050                 return;
25051             }
25052             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25053                 return;
25054             }
25055             if (v.match(/^#/)) {
25056                 return;
25057             }
25058             if (v.match(/^\{/)) { // allow template editing.
25059                 return;
25060             }
25061 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25062             node.removeAttribute(n);
25063             
25064         }
25065         
25066         var cwhite = this.cwhite;
25067         var cblack = this.cblack;
25068             
25069         function cleanStyle(n,v)
25070         {
25071             if (v.match(/expression/)) { //XSS?? should we even bother..
25072                 node.removeAttribute(n);
25073                 return;
25074             }
25075             
25076             var parts = v.split(/;/);
25077             var clean = [];
25078             
25079             Roo.each(parts, function(p) {
25080                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25081                 if (!p.length) {
25082                     return true;
25083                 }
25084                 var l = p.split(':').shift().replace(/\s+/g,'');
25085                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25086                 
25087                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25088 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25089                     //node.removeAttribute(n);
25090                     return true;
25091                 }
25092                 //Roo.log()
25093                 // only allow 'c whitelisted system attributes'
25094                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25095 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25096                     //node.removeAttribute(n);
25097                     return true;
25098                 }
25099                 
25100                 
25101                  
25102                 
25103                 clean.push(p);
25104                 return true;
25105             });
25106             if (clean.length) { 
25107                 node.setAttribute(n, clean.join(';'));
25108             } else {
25109                 node.removeAttribute(n);
25110             }
25111             
25112         }
25113         
25114         
25115         for (var i = node.attributes.length-1; i > -1 ; i--) {
25116             var a = node.attributes[i];
25117             //console.log(a);
25118             
25119             if (a.name.toLowerCase().substr(0,2)=='on')  {
25120                 node.removeAttribute(a.name);
25121                 continue;
25122             }
25123             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25124                 node.removeAttribute(a.name);
25125                 continue;
25126             }
25127             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25128                 cleanAttr(a.name,a.value); // fixme..
25129                 continue;
25130             }
25131             if (a.name == 'style') {
25132                 cleanStyle(a.name,a.value);
25133                 continue;
25134             }
25135             /// clean up MS crap..
25136             // tecnically this should be a list of valid class'es..
25137             
25138             
25139             if (a.name == 'class') {
25140                 if (a.value.match(/^Mso/)) {
25141                     node.removeAttribute('class');
25142                 }
25143                 
25144                 if (a.value.match(/^body$/)) {
25145                     node.removeAttribute('class');
25146                 }
25147                 continue;
25148             }
25149             
25150             // style cleanup!?
25151             // class cleanup?
25152             
25153         }
25154         
25155         
25156         this.cleanUpChildren(node);
25157         
25158         
25159     },
25160     
25161     /**
25162      * Clean up MS wordisms...
25163      */
25164     cleanWord : function(node)
25165     {
25166         if (!node) {
25167             this.cleanWord(this.doc.body);
25168             return;
25169         }
25170         
25171         if(
25172                 node.nodeName == 'SPAN' &&
25173                 !node.hasAttributes() &&
25174                 node.childNodes.length == 1 &&
25175                 node.firstChild.nodeName == "#text"  
25176         ) {
25177             var textNode = node.firstChild;
25178             node.removeChild(textNode);
25179             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25180                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25181             }
25182             node.parentNode.insertBefore(textNode, node);
25183             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25184                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25185             }
25186             node.parentNode.removeChild(node);
25187         }
25188         
25189         if (node.nodeName == "#text") {
25190             // clean up silly Windows -- stuff?
25191             return; 
25192         }
25193         if (node.nodeName == "#comment") {
25194             node.parentNode.removeChild(node);
25195             // clean up silly Windows -- stuff?
25196             return; 
25197         }
25198         
25199         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25200             node.parentNode.removeChild(node);
25201             return;
25202         }
25203         //Roo.log(node.tagName);
25204         // remove - but keep children..
25205         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25206             //Roo.log('-- removed');
25207             while (node.childNodes.length) {
25208                 var cn = node.childNodes[0];
25209                 node.removeChild(cn);
25210                 node.parentNode.insertBefore(cn, node);
25211                 // move node to parent - and clean it..
25212                 this.cleanWord(cn);
25213             }
25214             node.parentNode.removeChild(node);
25215             /// no need to iterate chidlren = it's got none..
25216             //this.iterateChildren(node, this.cleanWord);
25217             return;
25218         }
25219         // clean styles
25220         if (node.className.length) {
25221             
25222             var cn = node.className.split(/\W+/);
25223             var cna = [];
25224             Roo.each(cn, function(cls) {
25225                 if (cls.match(/Mso[a-zA-Z]+/)) {
25226                     return;
25227                 }
25228                 cna.push(cls);
25229             });
25230             node.className = cna.length ? cna.join(' ') : '';
25231             if (!cna.length) {
25232                 node.removeAttribute("class");
25233             }
25234         }
25235         
25236         if (node.hasAttribute("lang")) {
25237             node.removeAttribute("lang");
25238         }
25239         
25240         if (node.hasAttribute("style")) {
25241             
25242             var styles = node.getAttribute("style").split(";");
25243             var nstyle = [];
25244             Roo.each(styles, function(s) {
25245                 if (!s.match(/:/)) {
25246                     return;
25247                 }
25248                 var kv = s.split(":");
25249                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25250                     return;
25251                 }
25252                 // what ever is left... we allow.
25253                 nstyle.push(s);
25254             });
25255             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25256             if (!nstyle.length) {
25257                 node.removeAttribute('style');
25258             }
25259         }
25260         this.iterateChildren(node, this.cleanWord);
25261         
25262         
25263         
25264     },
25265     /**
25266      * iterateChildren of a Node, calling fn each time, using this as the scole..
25267      * @param {DomNode} node node to iterate children of.
25268      * @param {Function} fn method of this class to call on each item.
25269      */
25270     iterateChildren : function(node, fn)
25271     {
25272         if (!node.childNodes.length) {
25273                 return;
25274         }
25275         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25276            fn.call(this, node.childNodes[i])
25277         }
25278     },
25279     
25280     
25281     /**
25282      * cleanTableWidths.
25283      *
25284      * Quite often pasting from word etc.. results in tables with column and widths.
25285      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25286      *
25287      */
25288     cleanTableWidths : function(node)
25289     {
25290          
25291          
25292         if (!node) {
25293             this.cleanTableWidths(this.doc.body);
25294             return;
25295         }
25296         
25297         // ignore list...
25298         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25299             return; 
25300         }
25301         Roo.log(node.tagName);
25302         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25303             this.iterateChildren(node, this.cleanTableWidths);
25304             return;
25305         }
25306         if (node.hasAttribute('width')) {
25307             node.removeAttribute('width');
25308         }
25309         
25310          
25311         if (node.hasAttribute("style")) {
25312             // pretty basic...
25313             
25314             var styles = node.getAttribute("style").split(";");
25315             var nstyle = [];
25316             Roo.each(styles, function(s) {
25317                 if (!s.match(/:/)) {
25318                     return;
25319                 }
25320                 var kv = s.split(":");
25321                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25322                     return;
25323                 }
25324                 // what ever is left... we allow.
25325                 nstyle.push(s);
25326             });
25327             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25328             if (!nstyle.length) {
25329                 node.removeAttribute('style');
25330             }
25331         }
25332         
25333         this.iterateChildren(node, this.cleanTableWidths);
25334         
25335         
25336     },
25337     
25338     
25339     
25340     
25341     domToHTML : function(currentElement, depth, nopadtext) {
25342         
25343         depth = depth || 0;
25344         nopadtext = nopadtext || false;
25345     
25346         if (!currentElement) {
25347             return this.domToHTML(this.doc.body);
25348         }
25349         
25350         //Roo.log(currentElement);
25351         var j;
25352         var allText = false;
25353         var nodeName = currentElement.nodeName;
25354         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25355         
25356         if  (nodeName == '#text') {
25357             
25358             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25359         }
25360         
25361         
25362         var ret = '';
25363         if (nodeName != 'BODY') {
25364              
25365             var i = 0;
25366             // Prints the node tagName, such as <A>, <IMG>, etc
25367             if (tagName) {
25368                 var attr = [];
25369                 for(i = 0; i < currentElement.attributes.length;i++) {
25370                     // quoting?
25371                     var aname = currentElement.attributes.item(i).name;
25372                     if (!currentElement.attributes.item(i).value.length) {
25373                         continue;
25374                     }
25375                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25376                 }
25377                 
25378                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25379             } 
25380             else {
25381                 
25382                 // eack
25383             }
25384         } else {
25385             tagName = false;
25386         }
25387         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25388             return ret;
25389         }
25390         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25391             nopadtext = true;
25392         }
25393         
25394         
25395         // Traverse the tree
25396         i = 0;
25397         var currentElementChild = currentElement.childNodes.item(i);
25398         var allText = true;
25399         var innerHTML  = '';
25400         lastnode = '';
25401         while (currentElementChild) {
25402             // Formatting code (indent the tree so it looks nice on the screen)
25403             var nopad = nopadtext;
25404             if (lastnode == 'SPAN') {
25405                 nopad  = true;
25406             }
25407             // text
25408             if  (currentElementChild.nodeName == '#text') {
25409                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25410                 toadd = nopadtext ? toadd : toadd.trim();
25411                 if (!nopad && toadd.length > 80) {
25412                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25413                 }
25414                 innerHTML  += toadd;
25415                 
25416                 i++;
25417                 currentElementChild = currentElement.childNodes.item(i);
25418                 lastNode = '';
25419                 continue;
25420             }
25421             allText = false;
25422             
25423             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25424                 
25425             // Recursively traverse the tree structure of the child node
25426             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25427             lastnode = currentElementChild.nodeName;
25428             i++;
25429             currentElementChild=currentElement.childNodes.item(i);
25430         }
25431         
25432         ret += innerHTML;
25433         
25434         if (!allText) {
25435                 // The remaining code is mostly for formatting the tree
25436             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25437         }
25438         
25439         
25440         if (tagName) {
25441             ret+= "</"+tagName+">";
25442         }
25443         return ret;
25444         
25445     },
25446         
25447     applyBlacklists : function()
25448     {
25449         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25450         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25451         
25452         this.white = [];
25453         this.black = [];
25454         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25455             if (b.indexOf(tag) > -1) {
25456                 return;
25457             }
25458             this.white.push(tag);
25459             
25460         }, this);
25461         
25462         Roo.each(w, function(tag) {
25463             if (b.indexOf(tag) > -1) {
25464                 return;
25465             }
25466             if (this.white.indexOf(tag) > -1) {
25467                 return;
25468             }
25469             this.white.push(tag);
25470             
25471         }, this);
25472         
25473         
25474         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25475             if (w.indexOf(tag) > -1) {
25476                 return;
25477             }
25478             this.black.push(tag);
25479             
25480         }, this);
25481         
25482         Roo.each(b, function(tag) {
25483             if (w.indexOf(tag) > -1) {
25484                 return;
25485             }
25486             if (this.black.indexOf(tag) > -1) {
25487                 return;
25488             }
25489             this.black.push(tag);
25490             
25491         }, this);
25492         
25493         
25494         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25495         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25496         
25497         this.cwhite = [];
25498         this.cblack = [];
25499         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25500             if (b.indexOf(tag) > -1) {
25501                 return;
25502             }
25503             this.cwhite.push(tag);
25504             
25505         }, this);
25506         
25507         Roo.each(w, function(tag) {
25508             if (b.indexOf(tag) > -1) {
25509                 return;
25510             }
25511             if (this.cwhite.indexOf(tag) > -1) {
25512                 return;
25513             }
25514             this.cwhite.push(tag);
25515             
25516         }, this);
25517         
25518         
25519         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25520             if (w.indexOf(tag) > -1) {
25521                 return;
25522             }
25523             this.cblack.push(tag);
25524             
25525         }, this);
25526         
25527         Roo.each(b, function(tag) {
25528             if (w.indexOf(tag) > -1) {
25529                 return;
25530             }
25531             if (this.cblack.indexOf(tag) > -1) {
25532                 return;
25533             }
25534             this.cblack.push(tag);
25535             
25536         }, this);
25537     },
25538     
25539     setStylesheets : function(stylesheets)
25540     {
25541         if(typeof(stylesheets) == 'string'){
25542             Roo.get(this.iframe.contentDocument.head).createChild({
25543                 tag : 'link',
25544                 rel : 'stylesheet',
25545                 type : 'text/css',
25546                 href : stylesheets
25547             });
25548             
25549             return;
25550         }
25551         var _this = this;
25552      
25553         Roo.each(stylesheets, function(s) {
25554             if(!s.length){
25555                 return;
25556             }
25557             
25558             Roo.get(_this.iframe.contentDocument.head).createChild({
25559                 tag : 'link',
25560                 rel : 'stylesheet',
25561                 type : 'text/css',
25562                 href : s
25563             });
25564         });
25565
25566         
25567     },
25568     
25569     removeStylesheets : function()
25570     {
25571         var _this = this;
25572         
25573         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25574             s.remove();
25575         });
25576     },
25577     
25578     setStyle : function(style)
25579     {
25580         Roo.get(this.iframe.contentDocument.head).createChild({
25581             tag : 'style',
25582             type : 'text/css',
25583             html : style
25584         });
25585
25586         return;
25587     }
25588     
25589     // hide stuff that is not compatible
25590     /**
25591      * @event blur
25592      * @hide
25593      */
25594     /**
25595      * @event change
25596      * @hide
25597      */
25598     /**
25599      * @event focus
25600      * @hide
25601      */
25602     /**
25603      * @event specialkey
25604      * @hide
25605      */
25606     /**
25607      * @cfg {String} fieldClass @hide
25608      */
25609     /**
25610      * @cfg {String} focusClass @hide
25611      */
25612     /**
25613      * @cfg {String} autoCreate @hide
25614      */
25615     /**
25616      * @cfg {String} inputType @hide
25617      */
25618     /**
25619      * @cfg {String} invalidClass @hide
25620      */
25621     /**
25622      * @cfg {String} invalidText @hide
25623      */
25624     /**
25625      * @cfg {String} msgFx @hide
25626      */
25627     /**
25628      * @cfg {String} validateOnBlur @hide
25629      */
25630 });
25631
25632 Roo.HtmlEditorCore.white = [
25633         'area', 'br', 'img', 'input', 'hr', 'wbr',
25634         
25635        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25636        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25637        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25638        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25639        'table',   'ul',         'xmp', 
25640        
25641        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25642       'thead',   'tr', 
25643      
25644       'dir', 'menu', 'ol', 'ul', 'dl',
25645        
25646       'embed',  'object'
25647 ];
25648
25649
25650 Roo.HtmlEditorCore.black = [
25651     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25652         'applet', // 
25653         'base',   'basefont', 'bgsound', 'blink',  'body', 
25654         'frame',  'frameset', 'head',    'html',   'ilayer', 
25655         'iframe', 'layer',  'link',     'meta',    'object',   
25656         'script', 'style' ,'title',  'xml' // clean later..
25657 ];
25658 Roo.HtmlEditorCore.clean = [
25659     'script', 'style', 'title', 'xml'
25660 ];
25661 Roo.HtmlEditorCore.remove = [
25662     'font'
25663 ];
25664 // attributes..
25665
25666 Roo.HtmlEditorCore.ablack = [
25667     'on'
25668 ];
25669     
25670 Roo.HtmlEditorCore.aclean = [ 
25671     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25672 ];
25673
25674 // protocols..
25675 Roo.HtmlEditorCore.pwhite= [
25676         'http',  'https',  'mailto'
25677 ];
25678
25679 // white listed style attributes.
25680 Roo.HtmlEditorCore.cwhite= [
25681       //  'text-align', /// default is to allow most things..
25682       
25683          
25684 //        'font-size'//??
25685 ];
25686
25687 // black listed style attributes.
25688 Roo.HtmlEditorCore.cblack= [
25689       //  'font-size' -- this can be set by the project 
25690 ];
25691
25692
25693 Roo.HtmlEditorCore.swapCodes   =[ 
25694     [    8211, "--" ], 
25695     [    8212, "--" ], 
25696     [    8216,  "'" ],  
25697     [    8217, "'" ],  
25698     [    8220, '"' ],  
25699     [    8221, '"' ],  
25700     [    8226, "*" ],  
25701     [    8230, "..." ]
25702 ]; 
25703
25704     /*
25705  * - LGPL
25706  *
25707  * HtmlEditor
25708  * 
25709  */
25710
25711 /**
25712  * @class Roo.bootstrap.HtmlEditor
25713  * @extends Roo.bootstrap.TextArea
25714  * Bootstrap HtmlEditor class
25715
25716  * @constructor
25717  * Create a new HtmlEditor
25718  * @param {Object} config The config object
25719  */
25720
25721 Roo.bootstrap.HtmlEditor = function(config){
25722     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25723     if (!this.toolbars) {
25724         this.toolbars = [];
25725     }
25726     
25727     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25728     this.addEvents({
25729             /**
25730              * @event initialize
25731              * Fires when the editor is fully initialized (including the iframe)
25732              * @param {HtmlEditor} this
25733              */
25734             initialize: true,
25735             /**
25736              * @event activate
25737              * Fires when the editor is first receives the focus. Any insertion must wait
25738              * until after this event.
25739              * @param {HtmlEditor} this
25740              */
25741             activate: true,
25742              /**
25743              * @event beforesync
25744              * Fires before the textarea is updated with content from the editor iframe. Return false
25745              * to cancel the sync.
25746              * @param {HtmlEditor} this
25747              * @param {String} html
25748              */
25749             beforesync: true,
25750              /**
25751              * @event beforepush
25752              * Fires before the iframe editor is updated with content from the textarea. Return false
25753              * to cancel the push.
25754              * @param {HtmlEditor} this
25755              * @param {String} html
25756              */
25757             beforepush: true,
25758              /**
25759              * @event sync
25760              * Fires when the textarea is updated with content from the editor iframe.
25761              * @param {HtmlEditor} this
25762              * @param {String} html
25763              */
25764             sync: true,
25765              /**
25766              * @event push
25767              * Fires when the iframe editor is updated with content from the textarea.
25768              * @param {HtmlEditor} this
25769              * @param {String} html
25770              */
25771             push: true,
25772              /**
25773              * @event editmodechange
25774              * Fires when the editor switches edit modes
25775              * @param {HtmlEditor} this
25776              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25777              */
25778             editmodechange: true,
25779             /**
25780              * @event editorevent
25781              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25782              * @param {HtmlEditor} this
25783              */
25784             editorevent: true,
25785             /**
25786              * @event firstfocus
25787              * Fires when on first focus - needed by toolbars..
25788              * @param {HtmlEditor} this
25789              */
25790             firstfocus: true,
25791             /**
25792              * @event autosave
25793              * Auto save the htmlEditor value as a file into Events
25794              * @param {HtmlEditor} this
25795              */
25796             autosave: true,
25797             /**
25798              * @event savedpreview
25799              * preview the saved version of htmlEditor
25800              * @param {HtmlEditor} this
25801              */
25802             savedpreview: true
25803         });
25804 };
25805
25806
25807 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25808     
25809     
25810       /**
25811      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25812      */
25813     toolbars : false,
25814     
25815      /**
25816     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25817     */
25818     btns : [],
25819    
25820      /**
25821      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25822      *                        Roo.resizable.
25823      */
25824     resizable : false,
25825      /**
25826      * @cfg {Number} height (in pixels)
25827      */   
25828     height: 300,
25829    /**
25830      * @cfg {Number} width (in pixels)
25831      */   
25832     width: false,
25833     
25834     /**
25835      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25836      * 
25837      */
25838     stylesheets: false,
25839     
25840     // id of frame..
25841     frameId: false,
25842     
25843     // private properties
25844     validationEvent : false,
25845     deferHeight: true,
25846     initialized : false,
25847     activated : false,
25848     
25849     onFocus : Roo.emptyFn,
25850     iframePad:3,
25851     hideMode:'offsets',
25852     
25853     tbContainer : false,
25854     
25855     bodyCls : '',
25856     
25857     toolbarContainer :function() {
25858         return this.wrap.select('.x-html-editor-tb',true).first();
25859     },
25860
25861     /**
25862      * Protected method that will not generally be called directly. It
25863      * is called when the editor creates its toolbar. Override this method if you need to
25864      * add custom toolbar buttons.
25865      * @param {HtmlEditor} editor
25866      */
25867     createToolbar : function(){
25868         Roo.log('renewing');
25869         Roo.log("create toolbars");
25870         
25871         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25872         this.toolbars[0].render(this.toolbarContainer());
25873         
25874         return;
25875         
25876 //        if (!editor.toolbars || !editor.toolbars.length) {
25877 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25878 //        }
25879 //        
25880 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25881 //            editor.toolbars[i] = Roo.factory(
25882 //                    typeof(editor.toolbars[i]) == 'string' ?
25883 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25884 //                Roo.bootstrap.HtmlEditor);
25885 //            editor.toolbars[i].init(editor);
25886 //        }
25887     },
25888
25889      
25890     // private
25891     onRender : function(ct, position)
25892     {
25893        // Roo.log("Call onRender: " + this.xtype);
25894         var _t = this;
25895         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25896       
25897         this.wrap = this.inputEl().wrap({
25898             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25899         });
25900         
25901         this.editorcore.onRender(ct, position);
25902          
25903         if (this.resizable) {
25904             this.resizeEl = new Roo.Resizable(this.wrap, {
25905                 pinned : true,
25906                 wrap: true,
25907                 dynamic : true,
25908                 minHeight : this.height,
25909                 height: this.height,
25910                 handles : this.resizable,
25911                 width: this.width,
25912                 listeners : {
25913                     resize : function(r, w, h) {
25914                         _t.onResize(w,h); // -something
25915                     }
25916                 }
25917             });
25918             
25919         }
25920         this.createToolbar(this);
25921        
25922         
25923         if(!this.width && this.resizable){
25924             this.setSize(this.wrap.getSize());
25925         }
25926         if (this.resizeEl) {
25927             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25928             // should trigger onReize..
25929         }
25930         
25931     },
25932
25933     // private
25934     onResize : function(w, h)
25935     {
25936         Roo.log('resize: ' +w + ',' + h );
25937         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25938         var ew = false;
25939         var eh = false;
25940         
25941         if(this.inputEl() ){
25942             if(typeof w == 'number'){
25943                 var aw = w - this.wrap.getFrameWidth('lr');
25944                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25945                 ew = aw;
25946             }
25947             if(typeof h == 'number'){
25948                  var tbh = -11;  // fixme it needs to tool bar size!
25949                 for (var i =0; i < this.toolbars.length;i++) {
25950                     // fixme - ask toolbars for heights?
25951                     tbh += this.toolbars[i].el.getHeight();
25952                     //if (this.toolbars[i].footer) {
25953                     //    tbh += this.toolbars[i].footer.el.getHeight();
25954                     //}
25955                 }
25956               
25957                 
25958                 
25959                 
25960                 
25961                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25962                 ah -= 5; // knock a few pixes off for look..
25963                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25964                 var eh = ah;
25965             }
25966         }
25967         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25968         this.editorcore.onResize(ew,eh);
25969         
25970     },
25971
25972     /**
25973      * Toggles the editor between standard and source edit mode.
25974      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25975      */
25976     toggleSourceEdit : function(sourceEditMode)
25977     {
25978         this.editorcore.toggleSourceEdit(sourceEditMode);
25979         
25980         if(this.editorcore.sourceEditMode){
25981             Roo.log('editor - showing textarea');
25982             
25983 //            Roo.log('in');
25984 //            Roo.log(this.syncValue());
25985             this.syncValue();
25986             this.inputEl().removeClass(['hide', 'x-hidden']);
25987             this.inputEl().dom.removeAttribute('tabIndex');
25988             this.inputEl().focus();
25989         }else{
25990             Roo.log('editor - hiding textarea');
25991 //            Roo.log('out')
25992 //            Roo.log(this.pushValue()); 
25993             this.pushValue();
25994             
25995             this.inputEl().addClass(['hide', 'x-hidden']);
25996             this.inputEl().dom.setAttribute('tabIndex', -1);
25997             //this.deferFocus();
25998         }
25999          
26000         if(this.resizable){
26001             this.setSize(this.wrap.getSize());
26002         }
26003         
26004         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26005     },
26006  
26007     // private (for BoxComponent)
26008     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26009
26010     // private (for BoxComponent)
26011     getResizeEl : function(){
26012         return this.wrap;
26013     },
26014
26015     // private (for BoxComponent)
26016     getPositionEl : function(){
26017         return this.wrap;
26018     },
26019
26020     // private
26021     initEvents : function(){
26022         this.originalValue = this.getValue();
26023     },
26024
26025 //    /**
26026 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26027 //     * @method
26028 //     */
26029 //    markInvalid : Roo.emptyFn,
26030 //    /**
26031 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26032 //     * @method
26033 //     */
26034 //    clearInvalid : Roo.emptyFn,
26035
26036     setValue : function(v){
26037         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26038         this.editorcore.pushValue();
26039     },
26040
26041      
26042     // private
26043     deferFocus : function(){
26044         this.focus.defer(10, this);
26045     },
26046
26047     // doc'ed in Field
26048     focus : function(){
26049         this.editorcore.focus();
26050         
26051     },
26052       
26053
26054     // private
26055     onDestroy : function(){
26056         
26057         
26058         
26059         if(this.rendered){
26060             
26061             for (var i =0; i < this.toolbars.length;i++) {
26062                 // fixme - ask toolbars for heights?
26063                 this.toolbars[i].onDestroy();
26064             }
26065             
26066             this.wrap.dom.innerHTML = '';
26067             this.wrap.remove();
26068         }
26069     },
26070
26071     // private
26072     onFirstFocus : function(){
26073         //Roo.log("onFirstFocus");
26074         this.editorcore.onFirstFocus();
26075          for (var i =0; i < this.toolbars.length;i++) {
26076             this.toolbars[i].onFirstFocus();
26077         }
26078         
26079     },
26080     
26081     // private
26082     syncValue : function()
26083     {   
26084         this.editorcore.syncValue();
26085     },
26086     
26087     pushValue : function()
26088     {   
26089         this.editorcore.pushValue();
26090     }
26091      
26092     
26093     // hide stuff that is not compatible
26094     /**
26095      * @event blur
26096      * @hide
26097      */
26098     /**
26099      * @event change
26100      * @hide
26101      */
26102     /**
26103      * @event focus
26104      * @hide
26105      */
26106     /**
26107      * @event specialkey
26108      * @hide
26109      */
26110     /**
26111      * @cfg {String} fieldClass @hide
26112      */
26113     /**
26114      * @cfg {String} focusClass @hide
26115      */
26116     /**
26117      * @cfg {String} autoCreate @hide
26118      */
26119     /**
26120      * @cfg {String} inputType @hide
26121      */
26122      
26123     /**
26124      * @cfg {String} invalidText @hide
26125      */
26126     /**
26127      * @cfg {String} msgFx @hide
26128      */
26129     /**
26130      * @cfg {String} validateOnBlur @hide
26131      */
26132 });
26133  
26134     
26135    
26136    
26137    
26138       
26139 Roo.namespace('Roo.bootstrap.htmleditor');
26140 /**
26141  * @class Roo.bootstrap.HtmlEditorToolbar1
26142  * Basic Toolbar
26143  * 
26144  * @example
26145  * Usage:
26146  *
26147  new Roo.bootstrap.HtmlEditor({
26148     ....
26149     toolbars : [
26150         new Roo.bootstrap.HtmlEditorToolbar1({
26151             disable : { fonts: 1 , format: 1, ..., ... , ...],
26152             btns : [ .... ]
26153         })
26154     }
26155      
26156  * 
26157  * @cfg {Object} disable List of elements to disable..
26158  * @cfg {Array} btns List of additional buttons.
26159  * 
26160  * 
26161  * NEEDS Extra CSS? 
26162  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26163  */
26164  
26165 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26166 {
26167     
26168     Roo.apply(this, config);
26169     
26170     // default disabled, based on 'good practice'..
26171     this.disable = this.disable || {};
26172     Roo.applyIf(this.disable, {
26173         fontSize : true,
26174         colors : true,
26175         specialElements : true
26176     });
26177     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26178     
26179     this.editor = config.editor;
26180     this.editorcore = config.editor.editorcore;
26181     
26182     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26183     
26184     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26185     // dont call parent... till later.
26186 }
26187 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26188      
26189     bar : true,
26190     
26191     editor : false,
26192     editorcore : false,
26193     
26194     
26195     formats : [
26196         "p" ,  
26197         "h1","h2","h3","h4","h5","h6", 
26198         "pre", "code", 
26199         "abbr", "acronym", "address", "cite", "samp", "var",
26200         'div','span'
26201     ],
26202     
26203     onRender : function(ct, position)
26204     {
26205        // Roo.log("Call onRender: " + this.xtype);
26206         
26207        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26208        Roo.log(this.el);
26209        this.el.dom.style.marginBottom = '0';
26210        var _this = this;
26211        var editorcore = this.editorcore;
26212        var editor= this.editor;
26213        
26214        var children = [];
26215        var btn = function(id,cmd , toggle, handler, html){
26216        
26217             var  event = toggle ? 'toggle' : 'click';
26218        
26219             var a = {
26220                 size : 'sm',
26221                 xtype: 'Button',
26222                 xns: Roo.bootstrap,
26223                 //glyphicon : id,
26224                 fa: id,
26225                 cmd : id || cmd,
26226                 enableToggle:toggle !== false,
26227                 html : html || '',
26228                 pressed : toggle ? false : null,
26229                 listeners : {}
26230             };
26231             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26232                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26233             };
26234             children.push(a);
26235             return a;
26236        }
26237        
26238     //    var cb_box = function...
26239         
26240         var style = {
26241                 xtype: 'Button',
26242                 size : 'sm',
26243                 xns: Roo.bootstrap,
26244                 fa : 'font',
26245                 //html : 'submit'
26246                 menu : {
26247                     xtype: 'Menu',
26248                     xns: Roo.bootstrap,
26249                     items:  []
26250                 }
26251         };
26252         Roo.each(this.formats, function(f) {
26253             style.menu.items.push({
26254                 xtype :'MenuItem',
26255                 xns: Roo.bootstrap,
26256                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26257                 tagname : f,
26258                 listeners : {
26259                     click : function()
26260                     {
26261                         editorcore.insertTag(this.tagname);
26262                         editor.focus();
26263                     }
26264                 }
26265                 
26266             });
26267         });
26268         children.push(style);   
26269         
26270         btn('bold',false,true);
26271         btn('italic',false,true);
26272         btn('align-left', 'justifyleft',true);
26273         btn('align-center', 'justifycenter',true);
26274         btn('align-right' , 'justifyright',true);
26275         btn('link', false, false, function(btn) {
26276             //Roo.log("create link?");
26277             var url = prompt(this.createLinkText, this.defaultLinkValue);
26278             if(url && url != 'http:/'+'/'){
26279                 this.editorcore.relayCmd('createlink', url);
26280             }
26281         }),
26282         btn('list','insertunorderedlist',true);
26283         btn('pencil', false,true, function(btn){
26284                 Roo.log(this);
26285                 this.toggleSourceEdit(btn.pressed);
26286         });
26287         
26288         if (this.editor.btns.length > 0) {
26289             for (var i = 0; i<this.editor.btns.length; i++) {
26290                 children.push(this.editor.btns[i]);
26291             }
26292         }
26293         
26294         /*
26295         var cog = {
26296                 xtype: 'Button',
26297                 size : 'sm',
26298                 xns: Roo.bootstrap,
26299                 glyphicon : 'cog',
26300                 //html : 'submit'
26301                 menu : {
26302                     xtype: 'Menu',
26303                     xns: Roo.bootstrap,
26304                     items:  []
26305                 }
26306         };
26307         
26308         cog.menu.items.push({
26309             xtype :'MenuItem',
26310             xns: Roo.bootstrap,
26311             html : Clean styles,
26312             tagname : f,
26313             listeners : {
26314                 click : function()
26315                 {
26316                     editorcore.insertTag(this.tagname);
26317                     editor.focus();
26318                 }
26319             }
26320             
26321         });
26322        */
26323         
26324          
26325        this.xtype = 'NavSimplebar';
26326         
26327         for(var i=0;i< children.length;i++) {
26328             
26329             this.buttons.add(this.addxtypeChild(children[i]));
26330             
26331         }
26332         
26333         editor.on('editorevent', this.updateToolbar, this);
26334     },
26335     onBtnClick : function(id)
26336     {
26337        this.editorcore.relayCmd(id);
26338        this.editorcore.focus();
26339     },
26340     
26341     /**
26342      * Protected method that will not generally be called directly. It triggers
26343      * a toolbar update by reading the markup state of the current selection in the editor.
26344      */
26345     updateToolbar: function(){
26346
26347         if(!this.editorcore.activated){
26348             this.editor.onFirstFocus(); // is this neeed?
26349             return;
26350         }
26351
26352         var btns = this.buttons; 
26353         var doc = this.editorcore.doc;
26354         btns.get('bold').setActive(doc.queryCommandState('bold'));
26355         btns.get('italic').setActive(doc.queryCommandState('italic'));
26356         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26357         
26358         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26359         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26360         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26361         
26362         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26363         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26364          /*
26365         
26366         var ans = this.editorcore.getAllAncestors();
26367         if (this.formatCombo) {
26368             
26369             
26370             var store = this.formatCombo.store;
26371             this.formatCombo.setValue("");
26372             for (var i =0; i < ans.length;i++) {
26373                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26374                     // select it..
26375                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26376                     break;
26377                 }
26378             }
26379         }
26380         
26381         
26382         
26383         // hides menus... - so this cant be on a menu...
26384         Roo.bootstrap.MenuMgr.hideAll();
26385         */
26386         Roo.bootstrap.MenuMgr.hideAll();
26387         //this.editorsyncValue();
26388     },
26389     onFirstFocus: function() {
26390         this.buttons.each(function(item){
26391            item.enable();
26392         });
26393     },
26394     toggleSourceEdit : function(sourceEditMode){
26395         
26396           
26397         if(sourceEditMode){
26398             Roo.log("disabling buttons");
26399            this.buttons.each( function(item){
26400                 if(item.cmd != 'pencil'){
26401                     item.disable();
26402                 }
26403             });
26404           
26405         }else{
26406             Roo.log("enabling buttons");
26407             if(this.editorcore.initialized){
26408                 this.buttons.each( function(item){
26409                     item.enable();
26410                 });
26411             }
26412             
26413         }
26414         Roo.log("calling toggole on editor");
26415         // tell the editor that it's been pressed..
26416         this.editor.toggleSourceEdit(sourceEditMode);
26417        
26418     }
26419 });
26420
26421
26422
26423
26424  
26425 /*
26426  * - LGPL
26427  */
26428
26429 /**
26430  * @class Roo.bootstrap.Markdown
26431  * @extends Roo.bootstrap.TextArea
26432  * Bootstrap Showdown editable area
26433  * @cfg {string} content
26434  * 
26435  * @constructor
26436  * Create a new Showdown
26437  */
26438
26439 Roo.bootstrap.Markdown = function(config){
26440     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26441    
26442 };
26443
26444 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26445     
26446     editing :false,
26447     
26448     initEvents : function()
26449     {
26450         
26451         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26452         this.markdownEl = this.el.createChild({
26453             cls : 'roo-markdown-area'
26454         });
26455         this.inputEl().addClass('d-none');
26456         if (this.getValue() == '') {
26457             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26458             
26459         } else {
26460             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26461         }
26462         this.markdownEl.on('click', this.toggleTextEdit, this);
26463         this.on('blur', this.toggleTextEdit, this);
26464         this.on('specialkey', this.resizeTextArea, this);
26465     },
26466     
26467     toggleTextEdit : function()
26468     {
26469         var sh = this.markdownEl.getHeight();
26470         this.inputEl().addClass('d-none');
26471         this.markdownEl.addClass('d-none');
26472         if (!this.editing) {
26473             // show editor?
26474             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26475             this.inputEl().removeClass('d-none');
26476             this.inputEl().focus();
26477             this.editing = true;
26478             return;
26479         }
26480         // show showdown...
26481         this.updateMarkdown();
26482         this.markdownEl.removeClass('d-none');
26483         this.editing = false;
26484         return;
26485     },
26486     updateMarkdown : function()
26487     {
26488         if (this.getValue() == '') {
26489             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26490             return;
26491         }
26492  
26493         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26494     },
26495     
26496     resizeTextArea: function () {
26497         
26498         var sh = 100;
26499         Roo.log([sh, this.getValue().split("\n").length * 30]);
26500         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26501     },
26502     setValue : function(val)
26503     {
26504         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26505         if (!this.editing) {
26506             this.updateMarkdown();
26507         }
26508         
26509     },
26510     focus : function()
26511     {
26512         if (!this.editing) {
26513             this.toggleTextEdit();
26514         }
26515         
26516     }
26517
26518
26519 });
26520 /**
26521  * @class Roo.bootstrap.Table.AbstractSelectionModel
26522  * @extends Roo.util.Observable
26523  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26524  * implemented by descendant classes.  This class should not be directly instantiated.
26525  * @constructor
26526  */
26527 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26528     this.locked = false;
26529     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26530 };
26531
26532
26533 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26534     /** @ignore Called by the grid automatically. Do not call directly. */
26535     init : function(grid){
26536         this.grid = grid;
26537         this.initEvents();
26538     },
26539
26540     /**
26541      * Locks the selections.
26542      */
26543     lock : function(){
26544         this.locked = true;
26545     },
26546
26547     /**
26548      * Unlocks the selections.
26549      */
26550     unlock : function(){
26551         this.locked = false;
26552     },
26553
26554     /**
26555      * Returns true if the selections are locked.
26556      * @return {Boolean}
26557      */
26558     isLocked : function(){
26559         return this.locked;
26560     },
26561     
26562     
26563     initEvents : function ()
26564     {
26565         
26566     }
26567 });
26568 /**
26569  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26570  * @class Roo.bootstrap.Table.RowSelectionModel
26571  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26572  * It supports multiple selections and keyboard selection/navigation. 
26573  * @constructor
26574  * @param {Object} config
26575  */
26576
26577 Roo.bootstrap.Table.RowSelectionModel = function(config){
26578     Roo.apply(this, config);
26579     this.selections = new Roo.util.MixedCollection(false, function(o){
26580         return o.id;
26581     });
26582
26583     this.last = false;
26584     this.lastActive = false;
26585
26586     this.addEvents({
26587         /**
26588              * @event selectionchange
26589              * Fires when the selection changes
26590              * @param {SelectionModel} this
26591              */
26592             "selectionchange" : true,
26593         /**
26594              * @event afterselectionchange
26595              * Fires after the selection changes (eg. by key press or clicking)
26596              * @param {SelectionModel} this
26597              */
26598             "afterselectionchange" : true,
26599         /**
26600              * @event beforerowselect
26601              * Fires when a row is selected being selected, return false to cancel.
26602              * @param {SelectionModel} this
26603              * @param {Number} rowIndex The selected index
26604              * @param {Boolean} keepExisting False if other selections will be cleared
26605              */
26606             "beforerowselect" : true,
26607         /**
26608              * @event rowselect
26609              * Fires when a row is selected.
26610              * @param {SelectionModel} this
26611              * @param {Number} rowIndex The selected index
26612              * @param {Roo.data.Record} r The record
26613              */
26614             "rowselect" : true,
26615         /**
26616              * @event rowdeselect
26617              * Fires when a row is deselected.
26618              * @param {SelectionModel} this
26619              * @param {Number} rowIndex The selected index
26620              */
26621         "rowdeselect" : true
26622     });
26623     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26624     this.locked = false;
26625  };
26626
26627 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26628     /**
26629      * @cfg {Boolean} singleSelect
26630      * True to allow selection of only one row at a time (defaults to false)
26631      */
26632     singleSelect : false,
26633
26634     // private
26635     initEvents : function()
26636     {
26637
26638         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26639         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26640         //}else{ // allow click to work like normal
26641          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26642         //}
26643         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26644         this.grid.on("rowclick", this.handleMouseDown, this);
26645         
26646         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26647             "up" : function(e){
26648                 if(!e.shiftKey){
26649                     this.selectPrevious(e.shiftKey);
26650                 }else if(this.last !== false && this.lastActive !== false){
26651                     var last = this.last;
26652                     this.selectRange(this.last,  this.lastActive-1);
26653                     this.grid.getView().focusRow(this.lastActive);
26654                     if(last !== false){
26655                         this.last = last;
26656                     }
26657                 }else{
26658                     this.selectFirstRow();
26659                 }
26660                 this.fireEvent("afterselectionchange", this);
26661             },
26662             "down" : function(e){
26663                 if(!e.shiftKey){
26664                     this.selectNext(e.shiftKey);
26665                 }else if(this.last !== false && this.lastActive !== false){
26666                     var last = this.last;
26667                     this.selectRange(this.last,  this.lastActive+1);
26668                     this.grid.getView().focusRow(this.lastActive);
26669                     if(last !== false){
26670                         this.last = last;
26671                     }
26672                 }else{
26673                     this.selectFirstRow();
26674                 }
26675                 this.fireEvent("afterselectionchange", this);
26676             },
26677             scope: this
26678         });
26679         this.grid.store.on('load', function(){
26680             this.selections.clear();
26681         },this);
26682         /*
26683         var view = this.grid.view;
26684         view.on("refresh", this.onRefresh, this);
26685         view.on("rowupdated", this.onRowUpdated, this);
26686         view.on("rowremoved", this.onRemove, this);
26687         */
26688     },
26689
26690     // private
26691     onRefresh : function()
26692     {
26693         var ds = this.grid.store, i, v = this.grid.view;
26694         var s = this.selections;
26695         s.each(function(r){
26696             if((i = ds.indexOfId(r.id)) != -1){
26697                 v.onRowSelect(i);
26698             }else{
26699                 s.remove(r);
26700             }
26701         });
26702     },
26703
26704     // private
26705     onRemove : function(v, index, r){
26706         this.selections.remove(r);
26707     },
26708
26709     // private
26710     onRowUpdated : function(v, index, r){
26711         if(this.isSelected(r)){
26712             v.onRowSelect(index);
26713         }
26714     },
26715
26716     /**
26717      * Select records.
26718      * @param {Array} records The records to select
26719      * @param {Boolean} keepExisting (optional) True to keep existing selections
26720      */
26721     selectRecords : function(records, keepExisting)
26722     {
26723         if(!keepExisting){
26724             this.clearSelections();
26725         }
26726             var ds = this.grid.store;
26727         for(var i = 0, len = records.length; i < len; i++){
26728             this.selectRow(ds.indexOf(records[i]), true);
26729         }
26730     },
26731
26732     /**
26733      * Gets the number of selected rows.
26734      * @return {Number}
26735      */
26736     getCount : function(){
26737         return this.selections.length;
26738     },
26739
26740     /**
26741      * Selects the first row in the grid.
26742      */
26743     selectFirstRow : function(){
26744         this.selectRow(0);
26745     },
26746
26747     /**
26748      * Select the last row.
26749      * @param {Boolean} keepExisting (optional) True to keep existing selections
26750      */
26751     selectLastRow : function(keepExisting){
26752         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26753         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26754     },
26755
26756     /**
26757      * Selects the row immediately following the last selected row.
26758      * @param {Boolean} keepExisting (optional) True to keep existing selections
26759      */
26760     selectNext : function(keepExisting)
26761     {
26762             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26763             this.selectRow(this.last+1, keepExisting);
26764             this.grid.getView().focusRow(this.last);
26765         }
26766     },
26767
26768     /**
26769      * Selects the row that precedes the last selected row.
26770      * @param {Boolean} keepExisting (optional) True to keep existing selections
26771      */
26772     selectPrevious : function(keepExisting){
26773         if(this.last){
26774             this.selectRow(this.last-1, keepExisting);
26775             this.grid.getView().focusRow(this.last);
26776         }
26777     },
26778
26779     /**
26780      * Returns the selected records
26781      * @return {Array} Array of selected records
26782      */
26783     getSelections : function(){
26784         return [].concat(this.selections.items);
26785     },
26786
26787     /**
26788      * Returns the first selected record.
26789      * @return {Record}
26790      */
26791     getSelected : function(){
26792         return this.selections.itemAt(0);
26793     },
26794
26795
26796     /**
26797      * Clears all selections.
26798      */
26799     clearSelections : function(fast)
26800     {
26801         if(this.locked) {
26802             return;
26803         }
26804         if(fast !== true){
26805                 var ds = this.grid.store;
26806             var s = this.selections;
26807             s.each(function(r){
26808                 this.deselectRow(ds.indexOfId(r.id));
26809             }, this);
26810             s.clear();
26811         }else{
26812             this.selections.clear();
26813         }
26814         this.last = false;
26815     },
26816
26817
26818     /**
26819      * Selects all rows.
26820      */
26821     selectAll : function(){
26822         if(this.locked) {
26823             return;
26824         }
26825         this.selections.clear();
26826         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26827             this.selectRow(i, true);
26828         }
26829     },
26830
26831     /**
26832      * Returns True if there is a selection.
26833      * @return {Boolean}
26834      */
26835     hasSelection : function(){
26836         return this.selections.length > 0;
26837     },
26838
26839     /**
26840      * Returns True if the specified row is selected.
26841      * @param {Number/Record} record The record or index of the record to check
26842      * @return {Boolean}
26843      */
26844     isSelected : function(index){
26845             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26846         return (r && this.selections.key(r.id) ? true : false);
26847     },
26848
26849     /**
26850      * Returns True if the specified record id is selected.
26851      * @param {String} id The id of record to check
26852      * @return {Boolean}
26853      */
26854     isIdSelected : function(id){
26855         return (this.selections.key(id) ? true : false);
26856     },
26857
26858
26859     // private
26860     handleMouseDBClick : function(e, t){
26861         
26862     },
26863     // private
26864     handleMouseDown : function(e, t)
26865     {
26866             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26867         if(this.isLocked() || rowIndex < 0 ){
26868             return;
26869         };
26870         if(e.shiftKey && this.last !== false){
26871             var last = this.last;
26872             this.selectRange(last, rowIndex, e.ctrlKey);
26873             this.last = last; // reset the last
26874             t.focus();
26875     
26876         }else{
26877             var isSelected = this.isSelected(rowIndex);
26878             //Roo.log("select row:" + rowIndex);
26879             if(isSelected){
26880                 this.deselectRow(rowIndex);
26881             } else {
26882                         this.selectRow(rowIndex, true);
26883             }
26884     
26885             /*
26886                 if(e.button !== 0 && isSelected){
26887                 alert('rowIndex 2: ' + rowIndex);
26888                     view.focusRow(rowIndex);
26889                 }else if(e.ctrlKey && isSelected){
26890                     this.deselectRow(rowIndex);
26891                 }else if(!isSelected){
26892                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26893                     view.focusRow(rowIndex);
26894                 }
26895             */
26896         }
26897         this.fireEvent("afterselectionchange", this);
26898     },
26899     // private
26900     handleDragableRowClick :  function(grid, rowIndex, e) 
26901     {
26902         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26903             this.selectRow(rowIndex, false);
26904             grid.view.focusRow(rowIndex);
26905              this.fireEvent("afterselectionchange", this);
26906         }
26907     },
26908     
26909     /**
26910      * Selects multiple rows.
26911      * @param {Array} rows Array of the indexes of the row to select
26912      * @param {Boolean} keepExisting (optional) True to keep existing selections
26913      */
26914     selectRows : function(rows, keepExisting){
26915         if(!keepExisting){
26916             this.clearSelections();
26917         }
26918         for(var i = 0, len = rows.length; i < len; i++){
26919             this.selectRow(rows[i], true);
26920         }
26921     },
26922
26923     /**
26924      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26925      * @param {Number} startRow The index of the first row in the range
26926      * @param {Number} endRow The index of the last row in the range
26927      * @param {Boolean} keepExisting (optional) True to retain existing selections
26928      */
26929     selectRange : function(startRow, endRow, keepExisting){
26930         if(this.locked) {
26931             return;
26932         }
26933         if(!keepExisting){
26934             this.clearSelections();
26935         }
26936         if(startRow <= endRow){
26937             for(var i = startRow; i <= endRow; i++){
26938                 this.selectRow(i, true);
26939             }
26940         }else{
26941             for(var i = startRow; i >= endRow; i--){
26942                 this.selectRow(i, true);
26943             }
26944         }
26945     },
26946
26947     /**
26948      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26949      * @param {Number} startRow The index of the first row in the range
26950      * @param {Number} endRow The index of the last row in the range
26951      */
26952     deselectRange : function(startRow, endRow, preventViewNotify){
26953         if(this.locked) {
26954             return;
26955         }
26956         for(var i = startRow; i <= endRow; i++){
26957             this.deselectRow(i, preventViewNotify);
26958         }
26959     },
26960
26961     /**
26962      * Selects a row.
26963      * @param {Number} row The index of the row to select
26964      * @param {Boolean} keepExisting (optional) True to keep existing selections
26965      */
26966     selectRow : function(index, keepExisting, preventViewNotify)
26967     {
26968             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26969             return;
26970         }
26971         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26972             if(!keepExisting || this.singleSelect){
26973                 this.clearSelections();
26974             }
26975             
26976             var r = this.grid.store.getAt(index);
26977             //console.log('selectRow - record id :' + r.id);
26978             
26979             this.selections.add(r);
26980             this.last = this.lastActive = index;
26981             if(!preventViewNotify){
26982                 var proxy = new Roo.Element(
26983                                 this.grid.getRowDom(index)
26984                 );
26985                 proxy.addClass('bg-info info');
26986             }
26987             this.fireEvent("rowselect", this, index, r);
26988             this.fireEvent("selectionchange", this);
26989         }
26990     },
26991
26992     /**
26993      * Deselects a row.
26994      * @param {Number} row The index of the row to deselect
26995      */
26996     deselectRow : function(index, preventViewNotify)
26997     {
26998         if(this.locked) {
26999             return;
27000         }
27001         if(this.last == index){
27002             this.last = false;
27003         }
27004         if(this.lastActive == index){
27005             this.lastActive = false;
27006         }
27007         
27008         var r = this.grid.store.getAt(index);
27009         if (!r) {
27010             return;
27011         }
27012         
27013         this.selections.remove(r);
27014         //.console.log('deselectRow - record id :' + r.id);
27015         if(!preventViewNotify){
27016         
27017             var proxy = new Roo.Element(
27018                 this.grid.getRowDom(index)
27019             );
27020             proxy.removeClass('bg-info info');
27021         }
27022         this.fireEvent("rowdeselect", this, index);
27023         this.fireEvent("selectionchange", this);
27024     },
27025
27026     // private
27027     restoreLast : function(){
27028         if(this._last){
27029             this.last = this._last;
27030         }
27031     },
27032
27033     // private
27034     acceptsNav : function(row, col, cm){
27035         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27036     },
27037
27038     // private
27039     onEditorKey : function(field, e){
27040         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27041         if(k == e.TAB){
27042             e.stopEvent();
27043             ed.completeEdit();
27044             if(e.shiftKey){
27045                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27046             }else{
27047                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27048             }
27049         }else if(k == e.ENTER && !e.ctrlKey){
27050             e.stopEvent();
27051             ed.completeEdit();
27052             if(e.shiftKey){
27053                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27054             }else{
27055                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27056             }
27057         }else if(k == e.ESC){
27058             ed.cancelEdit();
27059         }
27060         if(newCell){
27061             g.startEditing(newCell[0], newCell[1]);
27062         }
27063     }
27064 });
27065 /*
27066  * Based on:
27067  * Ext JS Library 1.1.1
27068  * Copyright(c) 2006-2007, Ext JS, LLC.
27069  *
27070  * Originally Released Under LGPL - original licence link has changed is not relivant.
27071  *
27072  * Fork - LGPL
27073  * <script type="text/javascript">
27074  */
27075  
27076 /**
27077  * @class Roo.bootstrap.PagingToolbar
27078  * @extends Roo.bootstrap.NavSimplebar
27079  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27080  * @constructor
27081  * Create a new PagingToolbar
27082  * @param {Object} config The config object
27083  * @param {Roo.data.Store} store
27084  */
27085 Roo.bootstrap.PagingToolbar = function(config)
27086 {
27087     // old args format still supported... - xtype is prefered..
27088         // created from xtype...
27089     
27090     this.ds = config.dataSource;
27091     
27092     if (config.store && !this.ds) {
27093         this.store= Roo.factory(config.store, Roo.data);
27094         this.ds = this.store;
27095         this.ds.xmodule = this.xmodule || false;
27096     }
27097     
27098     this.toolbarItems = [];
27099     if (config.items) {
27100         this.toolbarItems = config.items;
27101     }
27102     
27103     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27104     
27105     this.cursor = 0;
27106     
27107     if (this.ds) { 
27108         this.bind(this.ds);
27109     }
27110     
27111     if (Roo.bootstrap.version == 4) {
27112         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27113     } else {
27114         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27115     }
27116     
27117 };
27118
27119 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27120     /**
27121      * @cfg {Roo.data.Store} dataSource
27122      * The underlying data store providing the paged data
27123      */
27124     /**
27125      * @cfg {String/HTMLElement/Element} container
27126      * container The id or element that will contain the toolbar
27127      */
27128     /**
27129      * @cfg {Boolean} displayInfo
27130      * True to display the displayMsg (defaults to false)
27131      */
27132     /**
27133      * @cfg {Number} pageSize
27134      * The number of records to display per page (defaults to 20)
27135      */
27136     pageSize: 20,
27137     /**
27138      * @cfg {String} displayMsg
27139      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27140      */
27141     displayMsg : 'Displaying {0} - {1} of {2}',
27142     /**
27143      * @cfg {String} emptyMsg
27144      * The message to display when no records are found (defaults to "No data to display")
27145      */
27146     emptyMsg : 'No data to display',
27147     /**
27148      * Customizable piece of the default paging text (defaults to "Page")
27149      * @type String
27150      */
27151     beforePageText : "Page",
27152     /**
27153      * Customizable piece of the default paging text (defaults to "of %0")
27154      * @type String
27155      */
27156     afterPageText : "of {0}",
27157     /**
27158      * Customizable piece of the default paging text (defaults to "First Page")
27159      * @type String
27160      */
27161     firstText : "First Page",
27162     /**
27163      * Customizable piece of the default paging text (defaults to "Previous Page")
27164      * @type String
27165      */
27166     prevText : "Previous Page",
27167     /**
27168      * Customizable piece of the default paging text (defaults to "Next Page")
27169      * @type String
27170      */
27171     nextText : "Next Page",
27172     /**
27173      * Customizable piece of the default paging text (defaults to "Last Page")
27174      * @type String
27175      */
27176     lastText : "Last Page",
27177     /**
27178      * Customizable piece of the default paging text (defaults to "Refresh")
27179      * @type String
27180      */
27181     refreshText : "Refresh",
27182
27183     buttons : false,
27184     // private
27185     onRender : function(ct, position) 
27186     {
27187         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27188         this.navgroup.parentId = this.id;
27189         this.navgroup.onRender(this.el, null);
27190         // add the buttons to the navgroup
27191         
27192         if(this.displayInfo){
27193             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27194             this.displayEl = this.el.select('.x-paging-info', true).first();
27195 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27196 //            this.displayEl = navel.el.select('span',true).first();
27197         }
27198         
27199         var _this = this;
27200         
27201         if(this.buttons){
27202             Roo.each(_this.buttons, function(e){ // this might need to use render????
27203                Roo.factory(e).render(_this.el);
27204             });
27205         }
27206             
27207         Roo.each(_this.toolbarItems, function(e) {
27208             _this.navgroup.addItem(e);
27209         });
27210         
27211         
27212         this.first = this.navgroup.addItem({
27213             tooltip: this.firstText,
27214             cls: "prev btn-outline-secondary",
27215             html : ' <i class="fa fa-step-backward"></i>',
27216             disabled: true,
27217             preventDefault: true,
27218             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27219         });
27220         
27221         this.prev =  this.navgroup.addItem({
27222             tooltip: this.prevText,
27223             cls: "prev btn-outline-secondary",
27224             html : ' <i class="fa fa-backward"></i>',
27225             disabled: true,
27226             preventDefault: true,
27227             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27228         });
27229     //this.addSeparator();
27230         
27231         
27232         var field = this.navgroup.addItem( {
27233             tagtype : 'span',
27234             cls : 'x-paging-position  btn-outline-secondary',
27235              disabled: true,
27236             html : this.beforePageText  +
27237                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27238                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27239          } ); //?? escaped?
27240         
27241         this.field = field.el.select('input', true).first();
27242         this.field.on("keydown", this.onPagingKeydown, this);
27243         this.field.on("focus", function(){this.dom.select();});
27244     
27245     
27246         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27247         //this.field.setHeight(18);
27248         //this.addSeparator();
27249         this.next = this.navgroup.addItem({
27250             tooltip: this.nextText,
27251             cls: "next btn-outline-secondary",
27252             html : ' <i class="fa fa-forward"></i>',
27253             disabled: true,
27254             preventDefault: true,
27255             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27256         });
27257         this.last = this.navgroup.addItem({
27258             tooltip: this.lastText,
27259             html : ' <i class="fa fa-step-forward"></i>',
27260             cls: "next btn-outline-secondary",
27261             disabled: true,
27262             preventDefault: true,
27263             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27264         });
27265     //this.addSeparator();
27266         this.loading = this.navgroup.addItem({
27267             tooltip: this.refreshText,
27268             cls: "btn-outline-secondary",
27269             html : ' <i class="fa fa-refresh"></i>',
27270             preventDefault: true,
27271             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27272         });
27273         
27274     },
27275
27276     // private
27277     updateInfo : function(){
27278         if(this.displayEl){
27279             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27280             var msg = count == 0 ?
27281                 this.emptyMsg :
27282                 String.format(
27283                     this.displayMsg,
27284                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27285                 );
27286             this.displayEl.update(msg);
27287         }
27288     },
27289
27290     // private
27291     onLoad : function(ds, r, o)
27292     {
27293         this.cursor = o.params.start ? o.params.start : 0;
27294         
27295         var d = this.getPageData(),
27296             ap = d.activePage,
27297             ps = d.pages;
27298         
27299         
27300         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27301         this.field.dom.value = ap;
27302         this.first.setDisabled(ap == 1);
27303         this.prev.setDisabled(ap == 1);
27304         this.next.setDisabled(ap == ps);
27305         this.last.setDisabled(ap == ps);
27306         this.loading.enable();
27307         this.updateInfo();
27308     },
27309
27310     // private
27311     getPageData : function(){
27312         var total = this.ds.getTotalCount();
27313         return {
27314             total : total,
27315             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27316             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27317         };
27318     },
27319
27320     // private
27321     onLoadError : function(){
27322         this.loading.enable();
27323     },
27324
27325     // private
27326     onPagingKeydown : function(e){
27327         var k = e.getKey();
27328         var d = this.getPageData();
27329         if(k == e.RETURN){
27330             var v = this.field.dom.value, pageNum;
27331             if(!v || isNaN(pageNum = parseInt(v, 10))){
27332                 this.field.dom.value = d.activePage;
27333                 return;
27334             }
27335             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27336             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27337             e.stopEvent();
27338         }
27339         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))
27340         {
27341           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27342           this.field.dom.value = pageNum;
27343           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27344           e.stopEvent();
27345         }
27346         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27347         {
27348           var v = this.field.dom.value, pageNum; 
27349           var increment = (e.shiftKey) ? 10 : 1;
27350           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27351                 increment *= -1;
27352           }
27353           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27354             this.field.dom.value = d.activePage;
27355             return;
27356           }
27357           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27358           {
27359             this.field.dom.value = parseInt(v, 10) + increment;
27360             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27361             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27362           }
27363           e.stopEvent();
27364         }
27365     },
27366
27367     // private
27368     beforeLoad : function(){
27369         if(this.loading){
27370             this.loading.disable();
27371         }
27372     },
27373
27374     // private
27375     onClick : function(which){
27376         
27377         var ds = this.ds;
27378         if (!ds) {
27379             return;
27380         }
27381         
27382         switch(which){
27383             case "first":
27384                 ds.load({params:{start: 0, limit: this.pageSize}});
27385             break;
27386             case "prev":
27387                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27388             break;
27389             case "next":
27390                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27391             break;
27392             case "last":
27393                 var total = ds.getTotalCount();
27394                 var extra = total % this.pageSize;
27395                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27396                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27397             break;
27398             case "refresh":
27399                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27400             break;
27401         }
27402     },
27403
27404     /**
27405      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27406      * @param {Roo.data.Store} store The data store to unbind
27407      */
27408     unbind : function(ds){
27409         ds.un("beforeload", this.beforeLoad, this);
27410         ds.un("load", this.onLoad, this);
27411         ds.un("loadexception", this.onLoadError, this);
27412         ds.un("remove", this.updateInfo, this);
27413         ds.un("add", this.updateInfo, this);
27414         this.ds = undefined;
27415     },
27416
27417     /**
27418      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27419      * @param {Roo.data.Store} store The data store to bind
27420      */
27421     bind : function(ds){
27422         ds.on("beforeload", this.beforeLoad, this);
27423         ds.on("load", this.onLoad, this);
27424         ds.on("loadexception", this.onLoadError, this);
27425         ds.on("remove", this.updateInfo, this);
27426         ds.on("add", this.updateInfo, this);
27427         this.ds = ds;
27428     }
27429 });/*
27430  * - LGPL
27431  *
27432  * element
27433  * 
27434  */
27435
27436 /**
27437  * @class Roo.bootstrap.MessageBar
27438  * @extends Roo.bootstrap.Component
27439  * Bootstrap MessageBar class
27440  * @cfg {String} html contents of the MessageBar
27441  * @cfg {String} weight (info | success | warning | danger) default info
27442  * @cfg {String} beforeClass insert the bar before the given class
27443  * @cfg {Boolean} closable (true | false) default false
27444  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27445  * 
27446  * @constructor
27447  * Create a new Element
27448  * @param {Object} config The config object
27449  */
27450
27451 Roo.bootstrap.MessageBar = function(config){
27452     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27453 };
27454
27455 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27456     
27457     html: '',
27458     weight: 'info',
27459     closable: false,
27460     fixed: false,
27461     beforeClass: 'bootstrap-sticky-wrap',
27462     
27463     getAutoCreate : function(){
27464         
27465         var cfg = {
27466             tag: 'div',
27467             cls: 'alert alert-dismissable alert-' + this.weight,
27468             cn: [
27469                 {
27470                     tag: 'span',
27471                     cls: 'message',
27472                     html: this.html || ''
27473                 }
27474             ]
27475         };
27476         
27477         if(this.fixed){
27478             cfg.cls += ' alert-messages-fixed';
27479         }
27480         
27481         if(this.closable){
27482             cfg.cn.push({
27483                 tag: 'button',
27484                 cls: 'close',
27485                 html: 'x'
27486             });
27487         }
27488         
27489         return cfg;
27490     },
27491     
27492     onRender : function(ct, position)
27493     {
27494         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27495         
27496         if(!this.el){
27497             var cfg = Roo.apply({},  this.getAutoCreate());
27498             cfg.id = Roo.id();
27499             
27500             if (this.cls) {
27501                 cfg.cls += ' ' + this.cls;
27502             }
27503             if (this.style) {
27504                 cfg.style = this.style;
27505             }
27506             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27507             
27508             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27509         }
27510         
27511         this.el.select('>button.close').on('click', this.hide, this);
27512         
27513     },
27514     
27515     show : function()
27516     {
27517         if (!this.rendered) {
27518             this.render();
27519         }
27520         
27521         this.el.show();
27522         
27523         this.fireEvent('show', this);
27524         
27525     },
27526     
27527     hide : function()
27528     {
27529         if (!this.rendered) {
27530             this.render();
27531         }
27532         
27533         this.el.hide();
27534         
27535         this.fireEvent('hide', this);
27536     },
27537     
27538     update : function()
27539     {
27540 //        var e = this.el.dom.firstChild;
27541 //        
27542 //        if(this.closable){
27543 //            e = e.nextSibling;
27544 //        }
27545 //        
27546 //        e.data = this.html || '';
27547
27548         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27549     }
27550    
27551 });
27552
27553  
27554
27555      /*
27556  * - LGPL
27557  *
27558  * Graph
27559  * 
27560  */
27561
27562
27563 /**
27564  * @class Roo.bootstrap.Graph
27565  * @extends Roo.bootstrap.Component
27566  * Bootstrap Graph class
27567 > Prameters
27568  -sm {number} sm 4
27569  -md {number} md 5
27570  @cfg {String} graphtype  bar | vbar | pie
27571  @cfg {number} g_x coodinator | centre x (pie)
27572  @cfg {number} g_y coodinator | centre y (pie)
27573  @cfg {number} g_r radius (pie)
27574  @cfg {number} g_height height of the chart (respected by all elements in the set)
27575  @cfg {number} g_width width of the chart (respected by all elements in the set)
27576  @cfg {Object} title The title of the chart
27577     
27578  -{Array}  values
27579  -opts (object) options for the chart 
27580      o {
27581      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27582      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27583      o vgutter (number)
27584      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.
27585      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27586      o to
27587      o stretch (boolean)
27588      o }
27589  -opts (object) options for the pie
27590      o{
27591      o cut
27592      o startAngle (number)
27593      o endAngle (number)
27594      } 
27595  *
27596  * @constructor
27597  * Create a new Input
27598  * @param {Object} config The config object
27599  */
27600
27601 Roo.bootstrap.Graph = function(config){
27602     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27603     
27604     this.addEvents({
27605         // img events
27606         /**
27607          * @event click
27608          * The img click event for the img.
27609          * @param {Roo.EventObject} e
27610          */
27611         "click" : true
27612     });
27613 };
27614
27615 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27616     
27617     sm: 4,
27618     md: 5,
27619     graphtype: 'bar',
27620     g_height: 250,
27621     g_width: 400,
27622     g_x: 50,
27623     g_y: 50,
27624     g_r: 30,
27625     opts:{
27626         //g_colors: this.colors,
27627         g_type: 'soft',
27628         g_gutter: '20%'
27629
27630     },
27631     title : false,
27632
27633     getAutoCreate : function(){
27634         
27635         var cfg = {
27636             tag: 'div',
27637             html : null
27638         };
27639         
27640         
27641         return  cfg;
27642     },
27643
27644     onRender : function(ct,position){
27645         
27646         
27647         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27648         
27649         if (typeof(Raphael) == 'undefined') {
27650             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27651             return;
27652         }
27653         
27654         this.raphael = Raphael(this.el.dom);
27655         
27656                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27657                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27658                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27659                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27660                 /*
27661                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27662                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27663                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27664                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27665                 
27666                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27667                 r.barchart(330, 10, 300, 220, data1);
27668                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27669                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27670                 */
27671                 
27672                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27673                 // r.barchart(30, 30, 560, 250,  xdata, {
27674                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27675                 //     axis : "0 0 1 1",
27676                 //     axisxlabels :  xdata
27677                 //     //yvalues : cols,
27678                    
27679                 // });
27680 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27681 //        
27682 //        this.load(null,xdata,{
27683 //                axis : "0 0 1 1",
27684 //                axisxlabels :  xdata
27685 //                });
27686
27687     },
27688
27689     load : function(graphtype,xdata,opts)
27690     {
27691         this.raphael.clear();
27692         if(!graphtype) {
27693             graphtype = this.graphtype;
27694         }
27695         if(!opts){
27696             opts = this.opts;
27697         }
27698         var r = this.raphael,
27699             fin = function () {
27700                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27701             },
27702             fout = function () {
27703                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27704             },
27705             pfin = function() {
27706                 this.sector.stop();
27707                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27708
27709                 if (this.label) {
27710                     this.label[0].stop();
27711                     this.label[0].attr({ r: 7.5 });
27712                     this.label[1].attr({ "font-weight": 800 });
27713                 }
27714             },
27715             pfout = function() {
27716                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27717
27718                 if (this.label) {
27719                     this.label[0].animate({ r: 5 }, 500, "bounce");
27720                     this.label[1].attr({ "font-weight": 400 });
27721                 }
27722             };
27723
27724         switch(graphtype){
27725             case 'bar':
27726                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27727                 break;
27728             case 'hbar':
27729                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27730                 break;
27731             case 'pie':
27732 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27733 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27734 //            
27735                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27736                 
27737                 break;
27738
27739         }
27740         
27741         if(this.title){
27742             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27743         }
27744         
27745     },
27746     
27747     setTitle: function(o)
27748     {
27749         this.title = o;
27750     },
27751     
27752     initEvents: function() {
27753         
27754         if(!this.href){
27755             this.el.on('click', this.onClick, this);
27756         }
27757     },
27758     
27759     onClick : function(e)
27760     {
27761         Roo.log('img onclick');
27762         this.fireEvent('click', this, e);
27763     }
27764    
27765 });
27766
27767  
27768 /*
27769  * - LGPL
27770  *
27771  * numberBox
27772  * 
27773  */
27774 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27775
27776 /**
27777  * @class Roo.bootstrap.dash.NumberBox
27778  * @extends Roo.bootstrap.Component
27779  * Bootstrap NumberBox class
27780  * @cfg {String} headline Box headline
27781  * @cfg {String} content Box content
27782  * @cfg {String} icon Box icon
27783  * @cfg {String} footer Footer text
27784  * @cfg {String} fhref Footer href
27785  * 
27786  * @constructor
27787  * Create a new NumberBox
27788  * @param {Object} config The config object
27789  */
27790
27791
27792 Roo.bootstrap.dash.NumberBox = function(config){
27793     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27794     
27795 };
27796
27797 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27798     
27799     headline : '',
27800     content : '',
27801     icon : '',
27802     footer : '',
27803     fhref : '',
27804     ficon : '',
27805     
27806     getAutoCreate : function(){
27807         
27808         var cfg = {
27809             tag : 'div',
27810             cls : 'small-box ',
27811             cn : [
27812                 {
27813                     tag : 'div',
27814                     cls : 'inner',
27815                     cn :[
27816                         {
27817                             tag : 'h3',
27818                             cls : 'roo-headline',
27819                             html : this.headline
27820                         },
27821                         {
27822                             tag : 'p',
27823                             cls : 'roo-content',
27824                             html : this.content
27825                         }
27826                     ]
27827                 }
27828             ]
27829         };
27830         
27831         if(this.icon){
27832             cfg.cn.push({
27833                 tag : 'div',
27834                 cls : 'icon',
27835                 cn :[
27836                     {
27837                         tag : 'i',
27838                         cls : 'ion ' + this.icon
27839                     }
27840                 ]
27841             });
27842         }
27843         
27844         if(this.footer){
27845             var footer = {
27846                 tag : 'a',
27847                 cls : 'small-box-footer',
27848                 href : this.fhref || '#',
27849                 html : this.footer
27850             };
27851             
27852             cfg.cn.push(footer);
27853             
27854         }
27855         
27856         return  cfg;
27857     },
27858
27859     onRender : function(ct,position){
27860         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27861
27862
27863        
27864                 
27865     },
27866
27867     setHeadline: function (value)
27868     {
27869         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27870     },
27871     
27872     setFooter: function (value, href)
27873     {
27874         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27875         
27876         if(href){
27877             this.el.select('a.small-box-footer',true).first().attr('href', href);
27878         }
27879         
27880     },
27881
27882     setContent: function (value)
27883     {
27884         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27885     },
27886
27887     initEvents: function() 
27888     {   
27889         
27890     }
27891     
27892 });
27893
27894  
27895 /*
27896  * - LGPL
27897  *
27898  * TabBox
27899  * 
27900  */
27901 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27902
27903 /**
27904  * @class Roo.bootstrap.dash.TabBox
27905  * @extends Roo.bootstrap.Component
27906  * Bootstrap TabBox class
27907  * @cfg {String} title Title of the TabBox
27908  * @cfg {String} icon Icon of the TabBox
27909  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27910  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27911  * 
27912  * @constructor
27913  * Create a new TabBox
27914  * @param {Object} config The config object
27915  */
27916
27917
27918 Roo.bootstrap.dash.TabBox = function(config){
27919     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27920     this.addEvents({
27921         // raw events
27922         /**
27923          * @event addpane
27924          * When a pane is added
27925          * @param {Roo.bootstrap.dash.TabPane} pane
27926          */
27927         "addpane" : true,
27928         /**
27929          * @event activatepane
27930          * When a pane is activated
27931          * @param {Roo.bootstrap.dash.TabPane} pane
27932          */
27933         "activatepane" : true
27934         
27935          
27936     });
27937     
27938     this.panes = [];
27939 };
27940
27941 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27942
27943     title : '',
27944     icon : false,
27945     showtabs : true,
27946     tabScrollable : false,
27947     
27948     getChildContainer : function()
27949     {
27950         return this.el.select('.tab-content', true).first();
27951     },
27952     
27953     getAutoCreate : function(){
27954         
27955         var header = {
27956             tag: 'li',
27957             cls: 'pull-left header',
27958             html: this.title,
27959             cn : []
27960         };
27961         
27962         if(this.icon){
27963             header.cn.push({
27964                 tag: 'i',
27965                 cls: 'fa ' + this.icon
27966             });
27967         }
27968         
27969         var h = {
27970             tag: 'ul',
27971             cls: 'nav nav-tabs pull-right',
27972             cn: [
27973                 header
27974             ]
27975         };
27976         
27977         if(this.tabScrollable){
27978             h = {
27979                 tag: 'div',
27980                 cls: 'tab-header',
27981                 cn: [
27982                     {
27983                         tag: 'ul',
27984                         cls: 'nav nav-tabs pull-right',
27985                         cn: [
27986                             header
27987                         ]
27988                     }
27989                 ]
27990             };
27991         }
27992         
27993         var cfg = {
27994             tag: 'div',
27995             cls: 'nav-tabs-custom',
27996             cn: [
27997                 h,
27998                 {
27999                     tag: 'div',
28000                     cls: 'tab-content no-padding',
28001                     cn: []
28002                 }
28003             ]
28004         };
28005
28006         return  cfg;
28007     },
28008     initEvents : function()
28009     {
28010         //Roo.log('add add pane handler');
28011         this.on('addpane', this.onAddPane, this);
28012     },
28013      /**
28014      * Updates the box title
28015      * @param {String} html to set the title to.
28016      */
28017     setTitle : function(value)
28018     {
28019         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28020     },
28021     onAddPane : function(pane)
28022     {
28023         this.panes.push(pane);
28024         //Roo.log('addpane');
28025         //Roo.log(pane);
28026         // tabs are rendere left to right..
28027         if(!this.showtabs){
28028             return;
28029         }
28030         
28031         var ctr = this.el.select('.nav-tabs', true).first();
28032          
28033          
28034         var existing = ctr.select('.nav-tab',true);
28035         var qty = existing.getCount();;
28036         
28037         
28038         var tab = ctr.createChild({
28039             tag : 'li',
28040             cls : 'nav-tab' + (qty ? '' : ' active'),
28041             cn : [
28042                 {
28043                     tag : 'a',
28044                     href:'#',
28045                     html : pane.title
28046                 }
28047             ]
28048         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28049         pane.tab = tab;
28050         
28051         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28052         if (!qty) {
28053             pane.el.addClass('active');
28054         }
28055         
28056                 
28057     },
28058     onTabClick : function(ev,un,ob,pane)
28059     {
28060         //Roo.log('tab - prev default');
28061         ev.preventDefault();
28062         
28063         
28064         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28065         pane.tab.addClass('active');
28066         //Roo.log(pane.title);
28067         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28068         // technically we should have a deactivate event.. but maybe add later.
28069         // and it should not de-activate the selected tab...
28070         this.fireEvent('activatepane', pane);
28071         pane.el.addClass('active');
28072         pane.fireEvent('activate');
28073         
28074         
28075     },
28076     
28077     getActivePane : function()
28078     {
28079         var r = false;
28080         Roo.each(this.panes, function(p) {
28081             if(p.el.hasClass('active')){
28082                 r = p;
28083                 return false;
28084             }
28085             
28086             return;
28087         });
28088         
28089         return r;
28090     }
28091     
28092     
28093 });
28094
28095  
28096 /*
28097  * - LGPL
28098  *
28099  * Tab pane
28100  * 
28101  */
28102 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28103 /**
28104  * @class Roo.bootstrap.TabPane
28105  * @extends Roo.bootstrap.Component
28106  * Bootstrap TabPane class
28107  * @cfg {Boolean} active (false | true) Default false
28108  * @cfg {String} title title of panel
28109
28110  * 
28111  * @constructor
28112  * Create a new TabPane
28113  * @param {Object} config The config object
28114  */
28115
28116 Roo.bootstrap.dash.TabPane = function(config){
28117     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28118     
28119     this.addEvents({
28120         // raw events
28121         /**
28122          * @event activate
28123          * When a pane is activated
28124          * @param {Roo.bootstrap.dash.TabPane} pane
28125          */
28126         "activate" : true
28127          
28128     });
28129 };
28130
28131 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28132     
28133     active : false,
28134     title : '',
28135     
28136     // the tabBox that this is attached to.
28137     tab : false,
28138      
28139     getAutoCreate : function() 
28140     {
28141         var cfg = {
28142             tag: 'div',
28143             cls: 'tab-pane'
28144         };
28145         
28146         if(this.active){
28147             cfg.cls += ' active';
28148         }
28149         
28150         return cfg;
28151     },
28152     initEvents  : function()
28153     {
28154         //Roo.log('trigger add pane handler');
28155         this.parent().fireEvent('addpane', this)
28156     },
28157     
28158      /**
28159      * Updates the tab title 
28160      * @param {String} html to set the title to.
28161      */
28162     setTitle: function(str)
28163     {
28164         if (!this.tab) {
28165             return;
28166         }
28167         this.title = str;
28168         this.tab.select('a', true).first().dom.innerHTML = str;
28169         
28170     }
28171     
28172     
28173     
28174 });
28175
28176  
28177
28178
28179  /*
28180  * - LGPL
28181  *
28182  * menu
28183  * 
28184  */
28185 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28186
28187 /**
28188  * @class Roo.bootstrap.menu.Menu
28189  * @extends Roo.bootstrap.Component
28190  * Bootstrap Menu class - container for Menu
28191  * @cfg {String} html Text of the menu
28192  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28193  * @cfg {String} icon Font awesome icon
28194  * @cfg {String} pos Menu align to (top | bottom) default bottom
28195  * 
28196  * 
28197  * @constructor
28198  * Create a new Menu
28199  * @param {Object} config The config object
28200  */
28201
28202
28203 Roo.bootstrap.menu.Menu = function(config){
28204     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28205     
28206     this.addEvents({
28207         /**
28208          * @event beforeshow
28209          * Fires before this menu is displayed
28210          * @param {Roo.bootstrap.menu.Menu} this
28211          */
28212         beforeshow : true,
28213         /**
28214          * @event beforehide
28215          * Fires before this menu is hidden
28216          * @param {Roo.bootstrap.menu.Menu} this
28217          */
28218         beforehide : true,
28219         /**
28220          * @event show
28221          * Fires after this menu is displayed
28222          * @param {Roo.bootstrap.menu.Menu} this
28223          */
28224         show : true,
28225         /**
28226          * @event hide
28227          * Fires after this menu is hidden
28228          * @param {Roo.bootstrap.menu.Menu} this
28229          */
28230         hide : true,
28231         /**
28232          * @event click
28233          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28234          * @param {Roo.bootstrap.menu.Menu} this
28235          * @param {Roo.EventObject} e
28236          */
28237         click : true
28238     });
28239     
28240 };
28241
28242 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28243     
28244     submenu : false,
28245     html : '',
28246     weight : 'default',
28247     icon : false,
28248     pos : 'bottom',
28249     
28250     
28251     getChildContainer : function() {
28252         if(this.isSubMenu){
28253             return this.el;
28254         }
28255         
28256         return this.el.select('ul.dropdown-menu', true).first();  
28257     },
28258     
28259     getAutoCreate : function()
28260     {
28261         var text = [
28262             {
28263                 tag : 'span',
28264                 cls : 'roo-menu-text',
28265                 html : this.html
28266             }
28267         ];
28268         
28269         if(this.icon){
28270             text.unshift({
28271                 tag : 'i',
28272                 cls : 'fa ' + this.icon
28273             })
28274         }
28275         
28276         
28277         var cfg = {
28278             tag : 'div',
28279             cls : 'btn-group',
28280             cn : [
28281                 {
28282                     tag : 'button',
28283                     cls : 'dropdown-button btn btn-' + this.weight,
28284                     cn : text
28285                 },
28286                 {
28287                     tag : 'button',
28288                     cls : 'dropdown-toggle btn btn-' + this.weight,
28289                     cn : [
28290                         {
28291                             tag : 'span',
28292                             cls : 'caret'
28293                         }
28294                     ]
28295                 },
28296                 {
28297                     tag : 'ul',
28298                     cls : 'dropdown-menu'
28299                 }
28300             ]
28301             
28302         };
28303         
28304         if(this.pos == 'top'){
28305             cfg.cls += ' dropup';
28306         }
28307         
28308         if(this.isSubMenu){
28309             cfg = {
28310                 tag : 'ul',
28311                 cls : 'dropdown-menu'
28312             }
28313         }
28314         
28315         return cfg;
28316     },
28317     
28318     onRender : function(ct, position)
28319     {
28320         this.isSubMenu = ct.hasClass('dropdown-submenu');
28321         
28322         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28323     },
28324     
28325     initEvents : function() 
28326     {
28327         if(this.isSubMenu){
28328             return;
28329         }
28330         
28331         this.hidden = true;
28332         
28333         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28334         this.triggerEl.on('click', this.onTriggerPress, this);
28335         
28336         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28337         this.buttonEl.on('click', this.onClick, this);
28338         
28339     },
28340     
28341     list : function()
28342     {
28343         if(this.isSubMenu){
28344             return this.el;
28345         }
28346         
28347         return this.el.select('ul.dropdown-menu', true).first();
28348     },
28349     
28350     onClick : function(e)
28351     {
28352         this.fireEvent("click", this, e);
28353     },
28354     
28355     onTriggerPress  : function(e)
28356     {   
28357         if (this.isVisible()) {
28358             this.hide();
28359         } else {
28360             this.show();
28361         }
28362     },
28363     
28364     isVisible : function(){
28365         return !this.hidden;
28366     },
28367     
28368     show : function()
28369     {
28370         this.fireEvent("beforeshow", this);
28371         
28372         this.hidden = false;
28373         this.el.addClass('open');
28374         
28375         Roo.get(document).on("mouseup", this.onMouseUp, this);
28376         
28377         this.fireEvent("show", this);
28378         
28379         
28380     },
28381     
28382     hide : function()
28383     {
28384         this.fireEvent("beforehide", this);
28385         
28386         this.hidden = true;
28387         this.el.removeClass('open');
28388         
28389         Roo.get(document).un("mouseup", this.onMouseUp);
28390         
28391         this.fireEvent("hide", this);
28392     },
28393     
28394     onMouseUp : function()
28395     {
28396         this.hide();
28397     }
28398     
28399 });
28400
28401  
28402  /*
28403  * - LGPL
28404  *
28405  * menu item
28406  * 
28407  */
28408 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28409
28410 /**
28411  * @class Roo.bootstrap.menu.Item
28412  * @extends Roo.bootstrap.Component
28413  * Bootstrap MenuItem class
28414  * @cfg {Boolean} submenu (true | false) default false
28415  * @cfg {String} html text of the item
28416  * @cfg {String} href the link
28417  * @cfg {Boolean} disable (true | false) default false
28418  * @cfg {Boolean} preventDefault (true | false) default true
28419  * @cfg {String} icon Font awesome icon
28420  * @cfg {String} pos Submenu align to (left | right) default right 
28421  * 
28422  * 
28423  * @constructor
28424  * Create a new Item
28425  * @param {Object} config The config object
28426  */
28427
28428
28429 Roo.bootstrap.menu.Item = function(config){
28430     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28431     this.addEvents({
28432         /**
28433          * @event mouseover
28434          * Fires when the mouse is hovering over this menu
28435          * @param {Roo.bootstrap.menu.Item} this
28436          * @param {Roo.EventObject} e
28437          */
28438         mouseover : true,
28439         /**
28440          * @event mouseout
28441          * Fires when the mouse exits this menu
28442          * @param {Roo.bootstrap.menu.Item} this
28443          * @param {Roo.EventObject} e
28444          */
28445         mouseout : true,
28446         // raw events
28447         /**
28448          * @event click
28449          * The raw click event for the entire grid.
28450          * @param {Roo.EventObject} e
28451          */
28452         click : true
28453     });
28454 };
28455
28456 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28457     
28458     submenu : false,
28459     href : '',
28460     html : '',
28461     preventDefault: true,
28462     disable : false,
28463     icon : false,
28464     pos : 'right',
28465     
28466     getAutoCreate : function()
28467     {
28468         var text = [
28469             {
28470                 tag : 'span',
28471                 cls : 'roo-menu-item-text',
28472                 html : this.html
28473             }
28474         ];
28475         
28476         if(this.icon){
28477             text.unshift({
28478                 tag : 'i',
28479                 cls : 'fa ' + this.icon
28480             })
28481         }
28482         
28483         var cfg = {
28484             tag : 'li',
28485             cn : [
28486                 {
28487                     tag : 'a',
28488                     href : this.href || '#',
28489                     cn : text
28490                 }
28491             ]
28492         };
28493         
28494         if(this.disable){
28495             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28496         }
28497         
28498         if(this.submenu){
28499             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28500             
28501             if(this.pos == 'left'){
28502                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28503             }
28504         }
28505         
28506         return cfg;
28507     },
28508     
28509     initEvents : function() 
28510     {
28511         this.el.on('mouseover', this.onMouseOver, this);
28512         this.el.on('mouseout', this.onMouseOut, this);
28513         
28514         this.el.select('a', true).first().on('click', this.onClick, this);
28515         
28516     },
28517     
28518     onClick : function(e)
28519     {
28520         if(this.preventDefault){
28521             e.preventDefault();
28522         }
28523         
28524         this.fireEvent("click", this, e);
28525     },
28526     
28527     onMouseOver : function(e)
28528     {
28529         if(this.submenu && this.pos == 'left'){
28530             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28531         }
28532         
28533         this.fireEvent("mouseover", this, e);
28534     },
28535     
28536     onMouseOut : function(e)
28537     {
28538         this.fireEvent("mouseout", this, e);
28539     }
28540 });
28541
28542  
28543
28544  /*
28545  * - LGPL
28546  *
28547  * menu separator
28548  * 
28549  */
28550 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28551
28552 /**
28553  * @class Roo.bootstrap.menu.Separator
28554  * @extends Roo.bootstrap.Component
28555  * Bootstrap Separator class
28556  * 
28557  * @constructor
28558  * Create a new Separator
28559  * @param {Object} config The config object
28560  */
28561
28562
28563 Roo.bootstrap.menu.Separator = function(config){
28564     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28565 };
28566
28567 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28568     
28569     getAutoCreate : function(){
28570         var cfg = {
28571             tag : 'li',
28572             cls: 'divider'
28573         };
28574         
28575         return cfg;
28576     }
28577    
28578 });
28579
28580  
28581
28582  /*
28583  * - LGPL
28584  *
28585  * Tooltip
28586  * 
28587  */
28588
28589 /**
28590  * @class Roo.bootstrap.Tooltip
28591  * Bootstrap Tooltip class
28592  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28593  * to determine which dom element triggers the tooltip.
28594  * 
28595  * It needs to add support for additional attributes like tooltip-position
28596  * 
28597  * @constructor
28598  * Create a new Toolti
28599  * @param {Object} config The config object
28600  */
28601
28602 Roo.bootstrap.Tooltip = function(config){
28603     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28604     
28605     this.alignment = Roo.bootstrap.Tooltip.alignment;
28606     
28607     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28608         this.alignment = config.alignment;
28609     }
28610     
28611 };
28612
28613 Roo.apply(Roo.bootstrap.Tooltip, {
28614     /**
28615      * @function init initialize tooltip monitoring.
28616      * @static
28617      */
28618     currentEl : false,
28619     currentTip : false,
28620     currentRegion : false,
28621     
28622     //  init : delay?
28623     
28624     init : function()
28625     {
28626         Roo.get(document).on('mouseover', this.enter ,this);
28627         Roo.get(document).on('mouseout', this.leave, this);
28628          
28629         
28630         this.currentTip = new Roo.bootstrap.Tooltip();
28631     },
28632     
28633     enter : function(ev)
28634     {
28635         var dom = ev.getTarget();
28636         
28637         //Roo.log(['enter',dom]);
28638         var el = Roo.fly(dom);
28639         if (this.currentEl) {
28640             //Roo.log(dom);
28641             //Roo.log(this.currentEl);
28642             //Roo.log(this.currentEl.contains(dom));
28643             if (this.currentEl == el) {
28644                 return;
28645             }
28646             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28647                 return;
28648             }
28649
28650         }
28651         
28652         if (this.currentTip.el) {
28653             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28654         }    
28655         //Roo.log(ev);
28656         
28657         if(!el || el.dom == document){
28658             return;
28659         }
28660         
28661         var bindEl = el;
28662         
28663         // you can not look for children, as if el is the body.. then everythign is the child..
28664         if (!el.attr('tooltip')) { //
28665             if (!el.select("[tooltip]").elements.length) {
28666                 return;
28667             }
28668             // is the mouse over this child...?
28669             bindEl = el.select("[tooltip]").first();
28670             var xy = ev.getXY();
28671             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28672                 //Roo.log("not in region.");
28673                 return;
28674             }
28675             //Roo.log("child element over..");
28676             
28677         }
28678         this.currentEl = bindEl;
28679         this.currentTip.bind(bindEl);
28680         this.currentRegion = Roo.lib.Region.getRegion(dom);
28681         this.currentTip.enter();
28682         
28683     },
28684     leave : function(ev)
28685     {
28686         var dom = ev.getTarget();
28687         //Roo.log(['leave',dom]);
28688         if (!this.currentEl) {
28689             return;
28690         }
28691         
28692         
28693         if (dom != this.currentEl.dom) {
28694             return;
28695         }
28696         var xy = ev.getXY();
28697         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28698             return;
28699         }
28700         // only activate leave if mouse cursor is outside... bounding box..
28701         
28702         
28703         
28704         
28705         if (this.currentTip) {
28706             this.currentTip.leave();
28707         }
28708         //Roo.log('clear currentEl');
28709         this.currentEl = false;
28710         
28711         
28712     },
28713     alignment : {
28714         'left' : ['r-l', [-2,0], 'right'],
28715         'right' : ['l-r', [2,0], 'left'],
28716         'bottom' : ['t-b', [0,2], 'top'],
28717         'top' : [ 'b-t', [0,-2], 'bottom']
28718     }
28719     
28720 });
28721
28722
28723 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28724     
28725     
28726     bindEl : false,
28727     
28728     delay : null, // can be { show : 300 , hide: 500}
28729     
28730     timeout : null,
28731     
28732     hoverState : null, //???
28733     
28734     placement : 'bottom', 
28735     
28736     alignment : false,
28737     
28738     getAutoCreate : function(){
28739     
28740         var cfg = {
28741            cls : 'tooltip',   
28742            role : 'tooltip',
28743            cn : [
28744                 {
28745                     cls : 'tooltip-arrow arrow'
28746                 },
28747                 {
28748                     cls : 'tooltip-inner'
28749                 }
28750            ]
28751         };
28752         
28753         return cfg;
28754     },
28755     bind : function(el)
28756     {
28757         this.bindEl = el;
28758     },
28759     
28760     initEvents : function()
28761     {
28762         this.arrowEl = this.el.select('.arrow', true).first();
28763         this.innerEl = this.el.select('.tooltip-inner', true).first();
28764     },
28765     
28766     enter : function () {
28767        
28768         if (this.timeout != null) {
28769             clearTimeout(this.timeout);
28770         }
28771         
28772         this.hoverState = 'in';
28773          //Roo.log("enter - show");
28774         if (!this.delay || !this.delay.show) {
28775             this.show();
28776             return;
28777         }
28778         var _t = this;
28779         this.timeout = setTimeout(function () {
28780             if (_t.hoverState == 'in') {
28781                 _t.show();
28782             }
28783         }, this.delay.show);
28784     },
28785     leave : function()
28786     {
28787         clearTimeout(this.timeout);
28788     
28789         this.hoverState = 'out';
28790          if (!this.delay || !this.delay.hide) {
28791             this.hide();
28792             return;
28793         }
28794        
28795         var _t = this;
28796         this.timeout = setTimeout(function () {
28797             //Roo.log("leave - timeout");
28798             
28799             if (_t.hoverState == 'out') {
28800                 _t.hide();
28801                 Roo.bootstrap.Tooltip.currentEl = false;
28802             }
28803         }, delay);
28804     },
28805     
28806     show : function (msg)
28807     {
28808         if (!this.el) {
28809             this.render(document.body);
28810         }
28811         // set content.
28812         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28813         
28814         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28815         
28816         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28817         
28818         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28819                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28820         
28821         var placement = typeof this.placement == 'function' ?
28822             this.placement.call(this, this.el, on_el) :
28823             this.placement;
28824             
28825         var autoToken = /\s?auto?\s?/i;
28826         var autoPlace = autoToken.test(placement);
28827         if (autoPlace) {
28828             placement = placement.replace(autoToken, '') || 'top';
28829         }
28830         
28831         //this.el.detach()
28832         //this.el.setXY([0,0]);
28833         this.el.show();
28834         //this.el.dom.style.display='block';
28835         
28836         //this.el.appendTo(on_el);
28837         
28838         var p = this.getPosition();
28839         var box = this.el.getBox();
28840         
28841         if (autoPlace) {
28842             // fixme..
28843         }
28844         
28845         var align = this.alignment[placement];
28846         
28847         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28848         
28849         if(placement == 'top' || placement == 'bottom'){
28850             if(xy[0] < 0){
28851                 placement = 'right';
28852             }
28853             
28854             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28855                 placement = 'left';
28856             }
28857             
28858             var scroll = Roo.select('body', true).first().getScroll();
28859             
28860             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28861                 placement = 'top';
28862             }
28863             
28864             align = this.alignment[placement];
28865             
28866             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28867             
28868         }
28869         
28870         this.el.alignTo(this.bindEl, align[0],align[1]);
28871         //var arrow = this.el.select('.arrow',true).first();
28872         //arrow.set(align[2], 
28873         
28874         this.el.addClass(placement);
28875         this.el.addClass("bs-tooltip-"+ placement);
28876         
28877         this.el.addClass('in fade show');
28878         
28879         this.hoverState = null;
28880         
28881         if (this.el.hasClass('fade')) {
28882             // fade it?
28883         }
28884         
28885         
28886         
28887         
28888         
28889     },
28890     hide : function()
28891     {
28892          
28893         if (!this.el) {
28894             return;
28895         }
28896         //this.el.setXY([0,0]);
28897         this.el.removeClass(['show', 'in']);
28898         //this.el.hide();
28899         
28900     }
28901     
28902 });
28903  
28904
28905  /*
28906  * - LGPL
28907  *
28908  * Location Picker
28909  * 
28910  */
28911
28912 /**
28913  * @class Roo.bootstrap.LocationPicker
28914  * @extends Roo.bootstrap.Component
28915  * Bootstrap LocationPicker class
28916  * @cfg {Number} latitude Position when init default 0
28917  * @cfg {Number} longitude Position when init default 0
28918  * @cfg {Number} zoom default 15
28919  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28920  * @cfg {Boolean} mapTypeControl default false
28921  * @cfg {Boolean} disableDoubleClickZoom default false
28922  * @cfg {Boolean} scrollwheel default true
28923  * @cfg {Boolean} streetViewControl default false
28924  * @cfg {Number} radius default 0
28925  * @cfg {String} locationName
28926  * @cfg {Boolean} draggable default true
28927  * @cfg {Boolean} enableAutocomplete default false
28928  * @cfg {Boolean} enableReverseGeocode default true
28929  * @cfg {String} markerTitle
28930  * 
28931  * @constructor
28932  * Create a new LocationPicker
28933  * @param {Object} config The config object
28934  */
28935
28936
28937 Roo.bootstrap.LocationPicker = function(config){
28938     
28939     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28940     
28941     this.addEvents({
28942         /**
28943          * @event initial
28944          * Fires when the picker initialized.
28945          * @param {Roo.bootstrap.LocationPicker} this
28946          * @param {Google Location} location
28947          */
28948         initial : true,
28949         /**
28950          * @event positionchanged
28951          * Fires when the picker position changed.
28952          * @param {Roo.bootstrap.LocationPicker} this
28953          * @param {Google Location} location
28954          */
28955         positionchanged : true,
28956         /**
28957          * @event resize
28958          * Fires when the map resize.
28959          * @param {Roo.bootstrap.LocationPicker} this
28960          */
28961         resize : true,
28962         /**
28963          * @event show
28964          * Fires when the map show.
28965          * @param {Roo.bootstrap.LocationPicker} this
28966          */
28967         show : true,
28968         /**
28969          * @event hide
28970          * Fires when the map hide.
28971          * @param {Roo.bootstrap.LocationPicker} this
28972          */
28973         hide : true,
28974         /**
28975          * @event mapClick
28976          * Fires when click the map.
28977          * @param {Roo.bootstrap.LocationPicker} this
28978          * @param {Map event} e
28979          */
28980         mapClick : true,
28981         /**
28982          * @event mapRightClick
28983          * Fires when right click the map.
28984          * @param {Roo.bootstrap.LocationPicker} this
28985          * @param {Map event} e
28986          */
28987         mapRightClick : true,
28988         /**
28989          * @event markerClick
28990          * Fires when click the marker.
28991          * @param {Roo.bootstrap.LocationPicker} this
28992          * @param {Map event} e
28993          */
28994         markerClick : true,
28995         /**
28996          * @event markerRightClick
28997          * Fires when right click the marker.
28998          * @param {Roo.bootstrap.LocationPicker} this
28999          * @param {Map event} e
29000          */
29001         markerRightClick : true,
29002         /**
29003          * @event OverlayViewDraw
29004          * Fires when OverlayView Draw
29005          * @param {Roo.bootstrap.LocationPicker} this
29006          */
29007         OverlayViewDraw : true,
29008         /**
29009          * @event OverlayViewOnAdd
29010          * Fires when OverlayView Draw
29011          * @param {Roo.bootstrap.LocationPicker} this
29012          */
29013         OverlayViewOnAdd : true,
29014         /**
29015          * @event OverlayViewOnRemove
29016          * Fires when OverlayView Draw
29017          * @param {Roo.bootstrap.LocationPicker} this
29018          */
29019         OverlayViewOnRemove : true,
29020         /**
29021          * @event OverlayViewShow
29022          * Fires when OverlayView Draw
29023          * @param {Roo.bootstrap.LocationPicker} this
29024          * @param {Pixel} cpx
29025          */
29026         OverlayViewShow : true,
29027         /**
29028          * @event OverlayViewHide
29029          * Fires when OverlayView Draw
29030          * @param {Roo.bootstrap.LocationPicker} this
29031          */
29032         OverlayViewHide : true,
29033         /**
29034          * @event loadexception
29035          * Fires when load google lib failed.
29036          * @param {Roo.bootstrap.LocationPicker} this
29037          */
29038         loadexception : true
29039     });
29040         
29041 };
29042
29043 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29044     
29045     gMapContext: false,
29046     
29047     latitude: 0,
29048     longitude: 0,
29049     zoom: 15,
29050     mapTypeId: false,
29051     mapTypeControl: false,
29052     disableDoubleClickZoom: false,
29053     scrollwheel: true,
29054     streetViewControl: false,
29055     radius: 0,
29056     locationName: '',
29057     draggable: true,
29058     enableAutocomplete: false,
29059     enableReverseGeocode: true,
29060     markerTitle: '',
29061     
29062     getAutoCreate: function()
29063     {
29064
29065         var cfg = {
29066             tag: 'div',
29067             cls: 'roo-location-picker'
29068         };
29069         
29070         return cfg
29071     },
29072     
29073     initEvents: function(ct, position)
29074     {       
29075         if(!this.el.getWidth() || this.isApplied()){
29076             return;
29077         }
29078         
29079         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29080         
29081         this.initial();
29082     },
29083     
29084     initial: function()
29085     {
29086         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29087             this.fireEvent('loadexception', this);
29088             return;
29089         }
29090         
29091         if(!this.mapTypeId){
29092             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29093         }
29094         
29095         this.gMapContext = this.GMapContext();
29096         
29097         this.initOverlayView();
29098         
29099         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29100         
29101         var _this = this;
29102                 
29103         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29104             _this.setPosition(_this.gMapContext.marker.position);
29105         });
29106         
29107         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29108             _this.fireEvent('mapClick', this, event);
29109             
29110         });
29111
29112         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29113             _this.fireEvent('mapRightClick', this, event);
29114             
29115         });
29116         
29117         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29118             _this.fireEvent('markerClick', this, event);
29119             
29120         });
29121
29122         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29123             _this.fireEvent('markerRightClick', this, event);
29124             
29125         });
29126         
29127         this.setPosition(this.gMapContext.location);
29128         
29129         this.fireEvent('initial', this, this.gMapContext.location);
29130     },
29131     
29132     initOverlayView: function()
29133     {
29134         var _this = this;
29135         
29136         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29137             
29138             draw: function()
29139             {
29140                 _this.fireEvent('OverlayViewDraw', _this);
29141             },
29142             
29143             onAdd: function()
29144             {
29145                 _this.fireEvent('OverlayViewOnAdd', _this);
29146             },
29147             
29148             onRemove: function()
29149             {
29150                 _this.fireEvent('OverlayViewOnRemove', _this);
29151             },
29152             
29153             show: function(cpx)
29154             {
29155                 _this.fireEvent('OverlayViewShow', _this, cpx);
29156             },
29157             
29158             hide: function()
29159             {
29160                 _this.fireEvent('OverlayViewHide', _this);
29161             }
29162             
29163         });
29164     },
29165     
29166     fromLatLngToContainerPixel: function(event)
29167     {
29168         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29169     },
29170     
29171     isApplied: function() 
29172     {
29173         return this.getGmapContext() == false ? false : true;
29174     },
29175     
29176     getGmapContext: function() 
29177     {
29178         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29179     },
29180     
29181     GMapContext: function() 
29182     {
29183         var position = new google.maps.LatLng(this.latitude, this.longitude);
29184         
29185         var _map = new google.maps.Map(this.el.dom, {
29186             center: position,
29187             zoom: this.zoom,
29188             mapTypeId: this.mapTypeId,
29189             mapTypeControl: this.mapTypeControl,
29190             disableDoubleClickZoom: this.disableDoubleClickZoom,
29191             scrollwheel: this.scrollwheel,
29192             streetViewControl: this.streetViewControl,
29193             locationName: this.locationName,
29194             draggable: this.draggable,
29195             enableAutocomplete: this.enableAutocomplete,
29196             enableReverseGeocode: this.enableReverseGeocode
29197         });
29198         
29199         var _marker = new google.maps.Marker({
29200             position: position,
29201             map: _map,
29202             title: this.markerTitle,
29203             draggable: this.draggable
29204         });
29205         
29206         return {
29207             map: _map,
29208             marker: _marker,
29209             circle: null,
29210             location: position,
29211             radius: this.radius,
29212             locationName: this.locationName,
29213             addressComponents: {
29214                 formatted_address: null,
29215                 addressLine1: null,
29216                 addressLine2: null,
29217                 streetName: null,
29218                 streetNumber: null,
29219                 city: null,
29220                 district: null,
29221                 state: null,
29222                 stateOrProvince: null
29223             },
29224             settings: this,
29225             domContainer: this.el.dom,
29226             geodecoder: new google.maps.Geocoder()
29227         };
29228     },
29229     
29230     drawCircle: function(center, radius, options) 
29231     {
29232         if (this.gMapContext.circle != null) {
29233             this.gMapContext.circle.setMap(null);
29234         }
29235         if (radius > 0) {
29236             radius *= 1;
29237             options = Roo.apply({}, options, {
29238                 strokeColor: "#0000FF",
29239                 strokeOpacity: .35,
29240                 strokeWeight: 2,
29241                 fillColor: "#0000FF",
29242                 fillOpacity: .2
29243             });
29244             
29245             options.map = this.gMapContext.map;
29246             options.radius = radius;
29247             options.center = center;
29248             this.gMapContext.circle = new google.maps.Circle(options);
29249             return this.gMapContext.circle;
29250         }
29251         
29252         return null;
29253     },
29254     
29255     setPosition: function(location) 
29256     {
29257         this.gMapContext.location = location;
29258         this.gMapContext.marker.setPosition(location);
29259         this.gMapContext.map.panTo(location);
29260         this.drawCircle(location, this.gMapContext.radius, {});
29261         
29262         var _this = this;
29263         
29264         if (this.gMapContext.settings.enableReverseGeocode) {
29265             this.gMapContext.geodecoder.geocode({
29266                 latLng: this.gMapContext.location
29267             }, function(results, status) {
29268                 
29269                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29270                     _this.gMapContext.locationName = results[0].formatted_address;
29271                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29272                     
29273                     _this.fireEvent('positionchanged', this, location);
29274                 }
29275             });
29276             
29277             return;
29278         }
29279         
29280         this.fireEvent('positionchanged', this, location);
29281     },
29282     
29283     resize: function()
29284     {
29285         google.maps.event.trigger(this.gMapContext.map, "resize");
29286         
29287         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29288         
29289         this.fireEvent('resize', this);
29290     },
29291     
29292     setPositionByLatLng: function(latitude, longitude)
29293     {
29294         this.setPosition(new google.maps.LatLng(latitude, longitude));
29295     },
29296     
29297     getCurrentPosition: function() 
29298     {
29299         return {
29300             latitude: this.gMapContext.location.lat(),
29301             longitude: this.gMapContext.location.lng()
29302         };
29303     },
29304     
29305     getAddressName: function() 
29306     {
29307         return this.gMapContext.locationName;
29308     },
29309     
29310     getAddressComponents: function() 
29311     {
29312         return this.gMapContext.addressComponents;
29313     },
29314     
29315     address_component_from_google_geocode: function(address_components) 
29316     {
29317         var result = {};
29318         
29319         for (var i = 0; i < address_components.length; i++) {
29320             var component = address_components[i];
29321             if (component.types.indexOf("postal_code") >= 0) {
29322                 result.postalCode = component.short_name;
29323             } else if (component.types.indexOf("street_number") >= 0) {
29324                 result.streetNumber = component.short_name;
29325             } else if (component.types.indexOf("route") >= 0) {
29326                 result.streetName = component.short_name;
29327             } else if (component.types.indexOf("neighborhood") >= 0) {
29328                 result.city = component.short_name;
29329             } else if (component.types.indexOf("locality") >= 0) {
29330                 result.city = component.short_name;
29331             } else if (component.types.indexOf("sublocality") >= 0) {
29332                 result.district = component.short_name;
29333             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29334                 result.stateOrProvince = component.short_name;
29335             } else if (component.types.indexOf("country") >= 0) {
29336                 result.country = component.short_name;
29337             }
29338         }
29339         
29340         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29341         result.addressLine2 = "";
29342         return result;
29343     },
29344     
29345     setZoomLevel: function(zoom)
29346     {
29347         this.gMapContext.map.setZoom(zoom);
29348     },
29349     
29350     show: function()
29351     {
29352         if(!this.el){
29353             return;
29354         }
29355         
29356         this.el.show();
29357         
29358         this.resize();
29359         
29360         this.fireEvent('show', this);
29361     },
29362     
29363     hide: function()
29364     {
29365         if(!this.el){
29366             return;
29367         }
29368         
29369         this.el.hide();
29370         
29371         this.fireEvent('hide', this);
29372     }
29373     
29374 });
29375
29376 Roo.apply(Roo.bootstrap.LocationPicker, {
29377     
29378     OverlayView : function(map, options)
29379     {
29380         options = options || {};
29381         
29382         this.setMap(map);
29383     }
29384     
29385     
29386 });/**
29387  * @class Roo.bootstrap.Alert
29388  * @extends Roo.bootstrap.Component
29389  * Bootstrap Alert class - shows an alert area box
29390  * eg
29391  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29392   Enter a valid email address
29393 </div>
29394  * @licence LGPL
29395  * @cfg {String} title The title of alert
29396  * @cfg {String} html The content of alert
29397  * @cfg {String} weight (  success | info | warning | danger )
29398  * @cfg {String} faicon font-awesomeicon
29399  * 
29400  * @constructor
29401  * Create a new alert
29402  * @param {Object} config The config object
29403  */
29404
29405
29406 Roo.bootstrap.Alert = function(config){
29407     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29408     
29409 };
29410
29411 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29412     
29413     title: '',
29414     html: '',
29415     weight: false,
29416     faicon: false,
29417     
29418     getAutoCreate : function()
29419     {
29420         
29421         var cfg = {
29422             tag : 'div',
29423             cls : 'alert',
29424             cn : [
29425                 {
29426                     tag : 'i',
29427                     cls : 'roo-alert-icon'
29428                     
29429                 },
29430                 {
29431                     tag : 'b',
29432                     cls : 'roo-alert-title',
29433                     html : this.title
29434                 },
29435                 {
29436                     tag : 'span',
29437                     cls : 'roo-alert-text',
29438                     html : this.html
29439                 }
29440             ]
29441         };
29442         
29443         if(this.faicon){
29444             cfg.cn[0].cls += ' fa ' + this.faicon;
29445         }
29446         
29447         if(this.weight){
29448             cfg.cls += ' alert-' + this.weight;
29449         }
29450         
29451         return cfg;
29452     },
29453     
29454     initEvents: function() 
29455     {
29456         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29457     },
29458     
29459     setTitle : function(str)
29460     {
29461         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29462     },
29463     
29464     setText : function(str)
29465     {
29466         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29467     },
29468     
29469     setWeight : function(weight)
29470     {
29471         if(this.weight){
29472             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29473         }
29474         
29475         this.weight = weight;
29476         
29477         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29478     },
29479     
29480     setIcon : function(icon)
29481     {
29482         if(this.faicon){
29483             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29484         }
29485         
29486         this.faicon = icon;
29487         
29488         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29489     },
29490     
29491     hide: function() 
29492     {
29493         this.el.hide();   
29494     },
29495     
29496     show: function() 
29497     {  
29498         this.el.show();   
29499     }
29500     
29501 });
29502
29503  
29504 /*
29505 * Licence: LGPL
29506 */
29507
29508 /**
29509  * @class Roo.bootstrap.UploadCropbox
29510  * @extends Roo.bootstrap.Component
29511  * Bootstrap UploadCropbox class
29512  * @cfg {String} emptyText show when image has been loaded
29513  * @cfg {String} rotateNotify show when image too small to rotate
29514  * @cfg {Number} errorTimeout default 3000
29515  * @cfg {Number} minWidth default 300
29516  * @cfg {Number} minHeight default 300
29517  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29518  * @cfg {Boolean} isDocument (true|false) default false
29519  * @cfg {String} url action url
29520  * @cfg {String} paramName default 'imageUpload'
29521  * @cfg {String} method default POST
29522  * @cfg {Boolean} loadMask (true|false) default true
29523  * @cfg {Boolean} loadingText default 'Loading...'
29524  * 
29525  * @constructor
29526  * Create a new UploadCropbox
29527  * @param {Object} config The config object
29528  */
29529
29530 Roo.bootstrap.UploadCropbox = function(config){
29531     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29532     
29533     this.addEvents({
29534         /**
29535          * @event beforeselectfile
29536          * Fire before select file
29537          * @param {Roo.bootstrap.UploadCropbox} this
29538          */
29539         "beforeselectfile" : true,
29540         /**
29541          * @event initial
29542          * Fire after initEvent
29543          * @param {Roo.bootstrap.UploadCropbox} this
29544          */
29545         "initial" : true,
29546         /**
29547          * @event crop
29548          * Fire after initEvent
29549          * @param {Roo.bootstrap.UploadCropbox} this
29550          * @param {String} data
29551          */
29552         "crop" : true,
29553         /**
29554          * @event prepare
29555          * Fire when preparing the file data
29556          * @param {Roo.bootstrap.UploadCropbox} this
29557          * @param {Object} file
29558          */
29559         "prepare" : true,
29560         /**
29561          * @event exception
29562          * Fire when get exception
29563          * @param {Roo.bootstrap.UploadCropbox} this
29564          * @param {XMLHttpRequest} xhr
29565          */
29566         "exception" : true,
29567         /**
29568          * @event beforeloadcanvas
29569          * Fire before load the canvas
29570          * @param {Roo.bootstrap.UploadCropbox} this
29571          * @param {String} src
29572          */
29573         "beforeloadcanvas" : true,
29574         /**
29575          * @event trash
29576          * Fire when trash image
29577          * @param {Roo.bootstrap.UploadCropbox} this
29578          */
29579         "trash" : true,
29580         /**
29581          * @event download
29582          * Fire when download the image
29583          * @param {Roo.bootstrap.UploadCropbox} this
29584          */
29585         "download" : true,
29586         /**
29587          * @event footerbuttonclick
29588          * Fire when footerbuttonclick
29589          * @param {Roo.bootstrap.UploadCropbox} this
29590          * @param {String} type
29591          */
29592         "footerbuttonclick" : true,
29593         /**
29594          * @event resize
29595          * Fire when resize
29596          * @param {Roo.bootstrap.UploadCropbox} this
29597          */
29598         "resize" : true,
29599         /**
29600          * @event rotate
29601          * Fire when rotate the image
29602          * @param {Roo.bootstrap.UploadCropbox} this
29603          * @param {String} pos
29604          */
29605         "rotate" : true,
29606         /**
29607          * @event inspect
29608          * Fire when inspect the file
29609          * @param {Roo.bootstrap.UploadCropbox} this
29610          * @param {Object} file
29611          */
29612         "inspect" : true,
29613         /**
29614          * @event upload
29615          * Fire when xhr upload the file
29616          * @param {Roo.bootstrap.UploadCropbox} this
29617          * @param {Object} data
29618          */
29619         "upload" : true,
29620         /**
29621          * @event arrange
29622          * Fire when arrange the file data
29623          * @param {Roo.bootstrap.UploadCropbox} this
29624          * @param {Object} formData
29625          */
29626         "arrange" : true
29627     });
29628     
29629     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29630 };
29631
29632 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29633     
29634     emptyText : 'Click to upload image',
29635     rotateNotify : 'Image is too small to rotate',
29636     errorTimeout : 3000,
29637     scale : 0,
29638     baseScale : 1,
29639     rotate : 0,
29640     dragable : false,
29641     pinching : false,
29642     mouseX : 0,
29643     mouseY : 0,
29644     cropData : false,
29645     minWidth : 300,
29646     minHeight : 300,
29647     file : false,
29648     exif : {},
29649     baseRotate : 1,
29650     cropType : 'image/jpeg',
29651     buttons : false,
29652     canvasLoaded : false,
29653     isDocument : false,
29654     method : 'POST',
29655     paramName : 'imageUpload',
29656     loadMask : true,
29657     loadingText : 'Loading...',
29658     maskEl : false,
29659     
29660     getAutoCreate : function()
29661     {
29662         var cfg = {
29663             tag : 'div',
29664             cls : 'roo-upload-cropbox',
29665             cn : [
29666                 {
29667                     tag : 'input',
29668                     cls : 'roo-upload-cropbox-selector',
29669                     type : 'file'
29670                 },
29671                 {
29672                     tag : 'div',
29673                     cls : 'roo-upload-cropbox-body',
29674                     style : 'cursor:pointer',
29675                     cn : [
29676                         {
29677                             tag : 'div',
29678                             cls : 'roo-upload-cropbox-preview'
29679                         },
29680                         {
29681                             tag : 'div',
29682                             cls : 'roo-upload-cropbox-thumb'
29683                         },
29684                         {
29685                             tag : 'div',
29686                             cls : 'roo-upload-cropbox-empty-notify',
29687                             html : this.emptyText
29688                         },
29689                         {
29690                             tag : 'div',
29691                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29692                             html : this.rotateNotify
29693                         }
29694                     ]
29695                 },
29696                 {
29697                     tag : 'div',
29698                     cls : 'roo-upload-cropbox-footer',
29699                     cn : {
29700                         tag : 'div',
29701                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29702                         cn : []
29703                     }
29704                 }
29705             ]
29706         };
29707         
29708         return cfg;
29709     },
29710     
29711     onRender : function(ct, position)
29712     {
29713         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29714         
29715         if (this.buttons.length) {
29716             
29717             Roo.each(this.buttons, function(bb) {
29718                 
29719                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29720                 
29721                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29722                 
29723             }, this);
29724         }
29725         
29726         if(this.loadMask){
29727             this.maskEl = this.el;
29728         }
29729     },
29730     
29731     initEvents : function()
29732     {
29733         this.urlAPI = (window.createObjectURL && window) || 
29734                                 (window.URL && URL.revokeObjectURL && URL) || 
29735                                 (window.webkitURL && webkitURL);
29736                         
29737         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29738         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29739         
29740         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29741         this.selectorEl.hide();
29742         
29743         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29744         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29745         
29746         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29747         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29748         this.thumbEl.hide();
29749         
29750         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29751         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29752         
29753         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29754         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29755         this.errorEl.hide();
29756         
29757         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29758         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29759         this.footerEl.hide();
29760         
29761         this.setThumbBoxSize();
29762         
29763         this.bind();
29764         
29765         this.resize();
29766         
29767         this.fireEvent('initial', this);
29768     },
29769
29770     bind : function()
29771     {
29772         var _this = this;
29773         
29774         window.addEventListener("resize", function() { _this.resize(); } );
29775         
29776         this.bodyEl.on('click', this.beforeSelectFile, this);
29777         
29778         if(Roo.isTouch){
29779             this.bodyEl.on('touchstart', this.onTouchStart, this);
29780             this.bodyEl.on('touchmove', this.onTouchMove, this);
29781             this.bodyEl.on('touchend', this.onTouchEnd, this);
29782         }
29783         
29784         if(!Roo.isTouch){
29785             this.bodyEl.on('mousedown', this.onMouseDown, this);
29786             this.bodyEl.on('mousemove', this.onMouseMove, this);
29787             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29788             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29789             Roo.get(document).on('mouseup', this.onMouseUp, this);
29790         }
29791         
29792         this.selectorEl.on('change', this.onFileSelected, this);
29793     },
29794     
29795     reset : function()
29796     {    
29797         this.scale = 0;
29798         this.baseScale = 1;
29799         this.rotate = 0;
29800         this.baseRotate = 1;
29801         this.dragable = false;
29802         this.pinching = false;
29803         this.mouseX = 0;
29804         this.mouseY = 0;
29805         this.cropData = false;
29806         this.notifyEl.dom.innerHTML = this.emptyText;
29807         
29808         this.selectorEl.dom.value = '';
29809         
29810     },
29811     
29812     resize : function()
29813     {
29814         if(this.fireEvent('resize', this) != false){
29815             this.setThumbBoxPosition();
29816             this.setCanvasPosition();
29817         }
29818     },
29819     
29820     onFooterButtonClick : function(e, el, o, type)
29821     {
29822         switch (type) {
29823             case 'rotate-left' :
29824                 this.onRotateLeft(e);
29825                 break;
29826             case 'rotate-right' :
29827                 this.onRotateRight(e);
29828                 break;
29829             case 'picture' :
29830                 this.beforeSelectFile(e);
29831                 break;
29832             case 'trash' :
29833                 this.trash(e);
29834                 break;
29835             case 'crop' :
29836                 this.crop(e);
29837                 break;
29838             case 'download' :
29839                 this.download(e);
29840                 break;
29841             default :
29842                 break;
29843         }
29844         
29845         this.fireEvent('footerbuttonclick', this, type);
29846     },
29847     
29848     beforeSelectFile : function(e)
29849     {
29850         e.preventDefault();
29851         
29852         if(this.fireEvent('beforeselectfile', this) != false){
29853             this.selectorEl.dom.click();
29854         }
29855     },
29856     
29857     onFileSelected : function(e)
29858     {
29859         e.preventDefault();
29860         
29861         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29862             return;
29863         }
29864         
29865         var file = this.selectorEl.dom.files[0];
29866         
29867         if(this.fireEvent('inspect', this, file) != false){
29868             this.prepare(file);
29869         }
29870         
29871     },
29872     
29873     trash : function(e)
29874     {
29875         this.fireEvent('trash', this);
29876     },
29877     
29878     download : function(e)
29879     {
29880         this.fireEvent('download', this);
29881     },
29882     
29883     loadCanvas : function(src)
29884     {   
29885         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29886             
29887             this.reset();
29888             
29889             this.imageEl = document.createElement('img');
29890             
29891             var _this = this;
29892             
29893             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29894             
29895             this.imageEl.src = src;
29896         }
29897     },
29898     
29899     onLoadCanvas : function()
29900     {   
29901         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29902         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29903         
29904         this.bodyEl.un('click', this.beforeSelectFile, this);
29905         
29906         this.notifyEl.hide();
29907         this.thumbEl.show();
29908         this.footerEl.show();
29909         
29910         this.baseRotateLevel();
29911         
29912         if(this.isDocument){
29913             this.setThumbBoxSize();
29914         }
29915         
29916         this.setThumbBoxPosition();
29917         
29918         this.baseScaleLevel();
29919         
29920         this.draw();
29921         
29922         this.resize();
29923         
29924         this.canvasLoaded = true;
29925         
29926         if(this.loadMask){
29927             this.maskEl.unmask();
29928         }
29929         
29930     },
29931     
29932     setCanvasPosition : function()
29933     {   
29934         if(!this.canvasEl){
29935             return;
29936         }
29937         
29938         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29939         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29940         
29941         this.previewEl.setLeft(pw);
29942         this.previewEl.setTop(ph);
29943         
29944     },
29945     
29946     onMouseDown : function(e)
29947     {   
29948         e.stopEvent();
29949         
29950         this.dragable = true;
29951         this.pinching = false;
29952         
29953         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29954             this.dragable = false;
29955             return;
29956         }
29957         
29958         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29959         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29960         
29961     },
29962     
29963     onMouseMove : function(e)
29964     {   
29965         e.stopEvent();
29966         
29967         if(!this.canvasLoaded){
29968             return;
29969         }
29970         
29971         if (!this.dragable){
29972             return;
29973         }
29974         
29975         var minX = Math.ceil(this.thumbEl.getLeft(true));
29976         var minY = Math.ceil(this.thumbEl.getTop(true));
29977         
29978         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29979         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29980         
29981         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29982         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29983         
29984         x = x - this.mouseX;
29985         y = y - this.mouseY;
29986         
29987         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29988         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29989         
29990         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29991         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29992         
29993         this.previewEl.setLeft(bgX);
29994         this.previewEl.setTop(bgY);
29995         
29996         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29997         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29998     },
29999     
30000     onMouseUp : function(e)
30001     {   
30002         e.stopEvent();
30003         
30004         this.dragable = false;
30005     },
30006     
30007     onMouseWheel : function(e)
30008     {   
30009         e.stopEvent();
30010         
30011         this.startScale = this.scale;
30012         
30013         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30014         
30015         if(!this.zoomable()){
30016             this.scale = this.startScale;
30017             return;
30018         }
30019         
30020         this.draw();
30021         
30022         return;
30023     },
30024     
30025     zoomable : function()
30026     {
30027         var minScale = this.thumbEl.getWidth() / this.minWidth;
30028         
30029         if(this.minWidth < this.minHeight){
30030             minScale = this.thumbEl.getHeight() / this.minHeight;
30031         }
30032         
30033         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30034         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30035         
30036         if(
30037                 this.isDocument &&
30038                 (this.rotate == 0 || this.rotate == 180) && 
30039                 (
30040                     width > this.imageEl.OriginWidth || 
30041                     height > this.imageEl.OriginHeight ||
30042                     (width < this.minWidth && height < this.minHeight)
30043                 )
30044         ){
30045             return false;
30046         }
30047         
30048         if(
30049                 this.isDocument &&
30050                 (this.rotate == 90 || this.rotate == 270) && 
30051                 (
30052                     width > this.imageEl.OriginWidth || 
30053                     height > this.imageEl.OriginHeight ||
30054                     (width < this.minHeight && height < this.minWidth)
30055                 )
30056         ){
30057             return false;
30058         }
30059         
30060         if(
30061                 !this.isDocument &&
30062                 (this.rotate == 0 || this.rotate == 180) && 
30063                 (
30064                     width < this.minWidth || 
30065                     width > this.imageEl.OriginWidth || 
30066                     height < this.minHeight || 
30067                     height > this.imageEl.OriginHeight
30068                 )
30069         ){
30070             return false;
30071         }
30072         
30073         if(
30074                 !this.isDocument &&
30075                 (this.rotate == 90 || this.rotate == 270) && 
30076                 (
30077                     width < this.minHeight || 
30078                     width > this.imageEl.OriginWidth || 
30079                     height < this.minWidth || 
30080                     height > this.imageEl.OriginHeight
30081                 )
30082         ){
30083             return false;
30084         }
30085         
30086         return true;
30087         
30088     },
30089     
30090     onRotateLeft : function(e)
30091     {   
30092         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30093             
30094             var minScale = this.thumbEl.getWidth() / this.minWidth;
30095             
30096             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30097             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30098             
30099             this.startScale = this.scale;
30100             
30101             while (this.getScaleLevel() < minScale){
30102             
30103                 this.scale = this.scale + 1;
30104                 
30105                 if(!this.zoomable()){
30106                     break;
30107                 }
30108                 
30109                 if(
30110                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30111                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30112                 ){
30113                     continue;
30114                 }
30115                 
30116                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30117
30118                 this.draw();
30119                 
30120                 return;
30121             }
30122             
30123             this.scale = this.startScale;
30124             
30125             this.onRotateFail();
30126             
30127             return false;
30128         }
30129         
30130         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30131
30132         if(this.isDocument){
30133             this.setThumbBoxSize();
30134             this.setThumbBoxPosition();
30135             this.setCanvasPosition();
30136         }
30137         
30138         this.draw();
30139         
30140         this.fireEvent('rotate', this, 'left');
30141         
30142     },
30143     
30144     onRotateRight : function(e)
30145     {
30146         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30147             
30148             var minScale = this.thumbEl.getWidth() / this.minWidth;
30149         
30150             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30151             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30152             
30153             this.startScale = this.scale;
30154             
30155             while (this.getScaleLevel() < minScale){
30156             
30157                 this.scale = this.scale + 1;
30158                 
30159                 if(!this.zoomable()){
30160                     break;
30161                 }
30162                 
30163                 if(
30164                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30165                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30166                 ){
30167                     continue;
30168                 }
30169                 
30170                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30171
30172                 this.draw();
30173                 
30174                 return;
30175             }
30176             
30177             this.scale = this.startScale;
30178             
30179             this.onRotateFail();
30180             
30181             return false;
30182         }
30183         
30184         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30185
30186         if(this.isDocument){
30187             this.setThumbBoxSize();
30188             this.setThumbBoxPosition();
30189             this.setCanvasPosition();
30190         }
30191         
30192         this.draw();
30193         
30194         this.fireEvent('rotate', this, 'right');
30195     },
30196     
30197     onRotateFail : function()
30198     {
30199         this.errorEl.show(true);
30200         
30201         var _this = this;
30202         
30203         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30204     },
30205     
30206     draw : function()
30207     {
30208         this.previewEl.dom.innerHTML = '';
30209         
30210         var canvasEl = document.createElement("canvas");
30211         
30212         var contextEl = canvasEl.getContext("2d");
30213         
30214         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30215         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30216         var center = this.imageEl.OriginWidth / 2;
30217         
30218         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30219             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30220             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30221             center = this.imageEl.OriginHeight / 2;
30222         }
30223         
30224         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30225         
30226         contextEl.translate(center, center);
30227         contextEl.rotate(this.rotate * Math.PI / 180);
30228
30229         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30230         
30231         this.canvasEl = document.createElement("canvas");
30232         
30233         this.contextEl = this.canvasEl.getContext("2d");
30234         
30235         switch (this.rotate) {
30236             case 0 :
30237                 
30238                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30239                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30240                 
30241                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30242                 
30243                 break;
30244             case 90 : 
30245                 
30246                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30247                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30248                 
30249                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30250                     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);
30251                     break;
30252                 }
30253                 
30254                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30255                 
30256                 break;
30257             case 180 :
30258                 
30259                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30260                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30261                 
30262                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30263                     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);
30264                     break;
30265                 }
30266                 
30267                 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);
30268                 
30269                 break;
30270             case 270 :
30271                 
30272                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30273                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30274         
30275                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30276                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30277                     break;
30278                 }
30279                 
30280                 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);
30281                 
30282                 break;
30283             default : 
30284                 break;
30285         }
30286         
30287         this.previewEl.appendChild(this.canvasEl);
30288         
30289         this.setCanvasPosition();
30290     },
30291     
30292     crop : function()
30293     {
30294         if(!this.canvasLoaded){
30295             return;
30296         }
30297         
30298         var imageCanvas = document.createElement("canvas");
30299         
30300         var imageContext = imageCanvas.getContext("2d");
30301         
30302         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30303         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30304         
30305         var center = imageCanvas.width / 2;
30306         
30307         imageContext.translate(center, center);
30308         
30309         imageContext.rotate(this.rotate * Math.PI / 180);
30310         
30311         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30312         
30313         var canvas = document.createElement("canvas");
30314         
30315         var context = canvas.getContext("2d");
30316                 
30317         canvas.width = this.minWidth;
30318         canvas.height = this.minHeight;
30319
30320         switch (this.rotate) {
30321             case 0 :
30322                 
30323                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30324                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30325                 
30326                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30327                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30328                 
30329                 var targetWidth = this.minWidth - 2 * x;
30330                 var targetHeight = this.minHeight - 2 * y;
30331                 
30332                 var scale = 1;
30333                 
30334                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30335                     scale = targetWidth / width;
30336                 }
30337                 
30338                 if(x > 0 && y == 0){
30339                     scale = targetHeight / height;
30340                 }
30341                 
30342                 if(x > 0 && y > 0){
30343                     scale = targetWidth / width;
30344                     
30345                     if(width < height){
30346                         scale = targetHeight / height;
30347                     }
30348                 }
30349                 
30350                 context.scale(scale, scale);
30351                 
30352                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30353                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30354
30355                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30356                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30357
30358                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30359                 
30360                 break;
30361             case 90 : 
30362                 
30363                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30364                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30365                 
30366                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30367                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30368                 
30369                 var targetWidth = this.minWidth - 2 * x;
30370                 var targetHeight = this.minHeight - 2 * y;
30371                 
30372                 var scale = 1;
30373                 
30374                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30375                     scale = targetWidth / width;
30376                 }
30377                 
30378                 if(x > 0 && y == 0){
30379                     scale = targetHeight / height;
30380                 }
30381                 
30382                 if(x > 0 && y > 0){
30383                     scale = targetWidth / width;
30384                     
30385                     if(width < height){
30386                         scale = targetHeight / height;
30387                     }
30388                 }
30389                 
30390                 context.scale(scale, scale);
30391                 
30392                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30393                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30394
30395                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30396                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30397                 
30398                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30399                 
30400                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30401                 
30402                 break;
30403             case 180 :
30404                 
30405                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30406                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30407                 
30408                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30409                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30410                 
30411                 var targetWidth = this.minWidth - 2 * x;
30412                 var targetHeight = this.minHeight - 2 * y;
30413                 
30414                 var scale = 1;
30415                 
30416                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30417                     scale = targetWidth / width;
30418                 }
30419                 
30420                 if(x > 0 && y == 0){
30421                     scale = targetHeight / height;
30422                 }
30423                 
30424                 if(x > 0 && y > 0){
30425                     scale = targetWidth / width;
30426                     
30427                     if(width < height){
30428                         scale = targetHeight / height;
30429                     }
30430                 }
30431                 
30432                 context.scale(scale, scale);
30433                 
30434                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30435                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30436
30437                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30438                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30439
30440                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30441                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30442                 
30443                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30444                 
30445                 break;
30446             case 270 :
30447                 
30448                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30449                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30450                 
30451                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30452                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30453                 
30454                 var targetWidth = this.minWidth - 2 * x;
30455                 var targetHeight = this.minHeight - 2 * y;
30456                 
30457                 var scale = 1;
30458                 
30459                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30460                     scale = targetWidth / width;
30461                 }
30462                 
30463                 if(x > 0 && y == 0){
30464                     scale = targetHeight / height;
30465                 }
30466                 
30467                 if(x > 0 && y > 0){
30468                     scale = targetWidth / width;
30469                     
30470                     if(width < height){
30471                         scale = targetHeight / height;
30472                     }
30473                 }
30474                 
30475                 context.scale(scale, scale);
30476                 
30477                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30478                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30479
30480                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30481                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30482                 
30483                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30484                 
30485                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30486                 
30487                 break;
30488             default : 
30489                 break;
30490         }
30491         
30492         this.cropData = canvas.toDataURL(this.cropType);
30493         
30494         if(this.fireEvent('crop', this, this.cropData) !== false){
30495             this.process(this.file, this.cropData);
30496         }
30497         
30498         return;
30499         
30500     },
30501     
30502     setThumbBoxSize : function()
30503     {
30504         var width, height;
30505         
30506         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30507             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30508             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30509             
30510             this.minWidth = width;
30511             this.minHeight = height;
30512             
30513             if(this.rotate == 90 || this.rotate == 270){
30514                 this.minWidth = height;
30515                 this.minHeight = width;
30516             }
30517         }
30518         
30519         height = 300;
30520         width = Math.ceil(this.minWidth * height / this.minHeight);
30521         
30522         if(this.minWidth > this.minHeight){
30523             width = 300;
30524             height = Math.ceil(this.minHeight * width / this.minWidth);
30525         }
30526         
30527         this.thumbEl.setStyle({
30528             width : width + 'px',
30529             height : height + 'px'
30530         });
30531
30532         return;
30533             
30534     },
30535     
30536     setThumbBoxPosition : function()
30537     {
30538         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30539         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30540         
30541         this.thumbEl.setLeft(x);
30542         this.thumbEl.setTop(y);
30543         
30544     },
30545     
30546     baseRotateLevel : function()
30547     {
30548         this.baseRotate = 1;
30549         
30550         if(
30551                 typeof(this.exif) != 'undefined' &&
30552                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30553                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30554         ){
30555             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30556         }
30557         
30558         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30559         
30560     },
30561     
30562     baseScaleLevel : function()
30563     {
30564         var width, height;
30565         
30566         if(this.isDocument){
30567             
30568             if(this.baseRotate == 6 || this.baseRotate == 8){
30569             
30570                 height = this.thumbEl.getHeight();
30571                 this.baseScale = height / this.imageEl.OriginWidth;
30572
30573                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30574                     width = this.thumbEl.getWidth();
30575                     this.baseScale = width / this.imageEl.OriginHeight;
30576                 }
30577
30578                 return;
30579             }
30580
30581             height = this.thumbEl.getHeight();
30582             this.baseScale = height / this.imageEl.OriginHeight;
30583
30584             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30585                 width = this.thumbEl.getWidth();
30586                 this.baseScale = width / this.imageEl.OriginWidth;
30587             }
30588
30589             return;
30590         }
30591         
30592         if(this.baseRotate == 6 || this.baseRotate == 8){
30593             
30594             width = this.thumbEl.getHeight();
30595             this.baseScale = width / this.imageEl.OriginHeight;
30596             
30597             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30598                 height = this.thumbEl.getWidth();
30599                 this.baseScale = height / this.imageEl.OriginHeight;
30600             }
30601             
30602             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30603                 height = this.thumbEl.getWidth();
30604                 this.baseScale = height / this.imageEl.OriginHeight;
30605                 
30606                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30607                     width = this.thumbEl.getHeight();
30608                     this.baseScale = width / this.imageEl.OriginWidth;
30609                 }
30610             }
30611             
30612             return;
30613         }
30614         
30615         width = this.thumbEl.getWidth();
30616         this.baseScale = width / this.imageEl.OriginWidth;
30617         
30618         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30619             height = this.thumbEl.getHeight();
30620             this.baseScale = height / this.imageEl.OriginHeight;
30621         }
30622         
30623         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30624             
30625             height = this.thumbEl.getHeight();
30626             this.baseScale = height / this.imageEl.OriginHeight;
30627             
30628             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30629                 width = this.thumbEl.getWidth();
30630                 this.baseScale = width / this.imageEl.OriginWidth;
30631             }
30632             
30633         }
30634         
30635         return;
30636     },
30637     
30638     getScaleLevel : function()
30639     {
30640         return this.baseScale * Math.pow(1.1, this.scale);
30641     },
30642     
30643     onTouchStart : function(e)
30644     {
30645         if(!this.canvasLoaded){
30646             this.beforeSelectFile(e);
30647             return;
30648         }
30649         
30650         var touches = e.browserEvent.touches;
30651         
30652         if(!touches){
30653             return;
30654         }
30655         
30656         if(touches.length == 1){
30657             this.onMouseDown(e);
30658             return;
30659         }
30660         
30661         if(touches.length != 2){
30662             return;
30663         }
30664         
30665         var coords = [];
30666         
30667         for(var i = 0, finger; finger = touches[i]; i++){
30668             coords.push(finger.pageX, finger.pageY);
30669         }
30670         
30671         var x = Math.pow(coords[0] - coords[2], 2);
30672         var y = Math.pow(coords[1] - coords[3], 2);
30673         
30674         this.startDistance = Math.sqrt(x + y);
30675         
30676         this.startScale = this.scale;
30677         
30678         this.pinching = true;
30679         this.dragable = false;
30680         
30681     },
30682     
30683     onTouchMove : function(e)
30684     {
30685         if(!this.pinching && !this.dragable){
30686             return;
30687         }
30688         
30689         var touches = e.browserEvent.touches;
30690         
30691         if(!touches){
30692             return;
30693         }
30694         
30695         if(this.dragable){
30696             this.onMouseMove(e);
30697             return;
30698         }
30699         
30700         var coords = [];
30701         
30702         for(var i = 0, finger; finger = touches[i]; i++){
30703             coords.push(finger.pageX, finger.pageY);
30704         }
30705         
30706         var x = Math.pow(coords[0] - coords[2], 2);
30707         var y = Math.pow(coords[1] - coords[3], 2);
30708         
30709         this.endDistance = Math.sqrt(x + y);
30710         
30711         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30712         
30713         if(!this.zoomable()){
30714             this.scale = this.startScale;
30715             return;
30716         }
30717         
30718         this.draw();
30719         
30720     },
30721     
30722     onTouchEnd : function(e)
30723     {
30724         this.pinching = false;
30725         this.dragable = false;
30726         
30727     },
30728     
30729     process : function(file, crop)
30730     {
30731         if(this.loadMask){
30732             this.maskEl.mask(this.loadingText);
30733         }
30734         
30735         this.xhr = new XMLHttpRequest();
30736         
30737         file.xhr = this.xhr;
30738
30739         this.xhr.open(this.method, this.url, true);
30740         
30741         var headers = {
30742             "Accept": "application/json",
30743             "Cache-Control": "no-cache",
30744             "X-Requested-With": "XMLHttpRequest"
30745         };
30746         
30747         for (var headerName in headers) {
30748             var headerValue = headers[headerName];
30749             if (headerValue) {
30750                 this.xhr.setRequestHeader(headerName, headerValue);
30751             }
30752         }
30753         
30754         var _this = this;
30755         
30756         this.xhr.onload = function()
30757         {
30758             _this.xhrOnLoad(_this.xhr);
30759         }
30760         
30761         this.xhr.onerror = function()
30762         {
30763             _this.xhrOnError(_this.xhr);
30764         }
30765         
30766         var formData = new FormData();
30767
30768         formData.append('returnHTML', 'NO');
30769         
30770         if(crop){
30771             formData.append('crop', crop);
30772         }
30773         
30774         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30775             formData.append(this.paramName, file, file.name);
30776         }
30777         
30778         if(typeof(file.filename) != 'undefined'){
30779             formData.append('filename', file.filename);
30780         }
30781         
30782         if(typeof(file.mimetype) != 'undefined'){
30783             formData.append('mimetype', file.mimetype);
30784         }
30785         
30786         if(this.fireEvent('arrange', this, formData) != false){
30787             this.xhr.send(formData);
30788         };
30789     },
30790     
30791     xhrOnLoad : function(xhr)
30792     {
30793         if(this.loadMask){
30794             this.maskEl.unmask();
30795         }
30796         
30797         if (xhr.readyState !== 4) {
30798             this.fireEvent('exception', this, xhr);
30799             return;
30800         }
30801
30802         var response = Roo.decode(xhr.responseText);
30803         
30804         if(!response.success){
30805             this.fireEvent('exception', this, xhr);
30806             return;
30807         }
30808         
30809         var response = Roo.decode(xhr.responseText);
30810         
30811         this.fireEvent('upload', this, response);
30812         
30813     },
30814     
30815     xhrOnError : function()
30816     {
30817         if(this.loadMask){
30818             this.maskEl.unmask();
30819         }
30820         
30821         Roo.log('xhr on error');
30822         
30823         var response = Roo.decode(xhr.responseText);
30824           
30825         Roo.log(response);
30826         
30827     },
30828     
30829     prepare : function(file)
30830     {   
30831         if(this.loadMask){
30832             this.maskEl.mask(this.loadingText);
30833         }
30834         
30835         this.file = false;
30836         this.exif = {};
30837         
30838         if(typeof(file) === 'string'){
30839             this.loadCanvas(file);
30840             return;
30841         }
30842         
30843         if(!file || !this.urlAPI){
30844             return;
30845         }
30846         
30847         this.file = file;
30848         this.cropType = file.type;
30849         
30850         var _this = this;
30851         
30852         if(this.fireEvent('prepare', this, this.file) != false){
30853             
30854             var reader = new FileReader();
30855             
30856             reader.onload = function (e) {
30857                 if (e.target.error) {
30858                     Roo.log(e.target.error);
30859                     return;
30860                 }
30861                 
30862                 var buffer = e.target.result,
30863                     dataView = new DataView(buffer),
30864                     offset = 2,
30865                     maxOffset = dataView.byteLength - 4,
30866                     markerBytes,
30867                     markerLength;
30868                 
30869                 if (dataView.getUint16(0) === 0xffd8) {
30870                     while (offset < maxOffset) {
30871                         markerBytes = dataView.getUint16(offset);
30872                         
30873                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30874                             markerLength = dataView.getUint16(offset + 2) + 2;
30875                             if (offset + markerLength > dataView.byteLength) {
30876                                 Roo.log('Invalid meta data: Invalid segment size.');
30877                                 break;
30878                             }
30879                             
30880                             if(markerBytes == 0xffe1){
30881                                 _this.parseExifData(
30882                                     dataView,
30883                                     offset,
30884                                     markerLength
30885                                 );
30886                             }
30887                             
30888                             offset += markerLength;
30889                             
30890                             continue;
30891                         }
30892                         
30893                         break;
30894                     }
30895                     
30896                 }
30897                 
30898                 var url = _this.urlAPI.createObjectURL(_this.file);
30899                 
30900                 _this.loadCanvas(url);
30901                 
30902                 return;
30903             }
30904             
30905             reader.readAsArrayBuffer(this.file);
30906             
30907         }
30908         
30909     },
30910     
30911     parseExifData : function(dataView, offset, length)
30912     {
30913         var tiffOffset = offset + 10,
30914             littleEndian,
30915             dirOffset;
30916     
30917         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30918             // No Exif data, might be XMP data instead
30919             return;
30920         }
30921         
30922         // Check for the ASCII code for "Exif" (0x45786966):
30923         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30924             // No Exif data, might be XMP data instead
30925             return;
30926         }
30927         if (tiffOffset + 8 > dataView.byteLength) {
30928             Roo.log('Invalid Exif data: Invalid segment size.');
30929             return;
30930         }
30931         // Check for the two null bytes:
30932         if (dataView.getUint16(offset + 8) !== 0x0000) {
30933             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30934             return;
30935         }
30936         // Check the byte alignment:
30937         switch (dataView.getUint16(tiffOffset)) {
30938         case 0x4949:
30939             littleEndian = true;
30940             break;
30941         case 0x4D4D:
30942             littleEndian = false;
30943             break;
30944         default:
30945             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30946             return;
30947         }
30948         // Check for the TIFF tag marker (0x002A):
30949         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30950             Roo.log('Invalid Exif data: Missing TIFF marker.');
30951             return;
30952         }
30953         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30954         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30955         
30956         this.parseExifTags(
30957             dataView,
30958             tiffOffset,
30959             tiffOffset + dirOffset,
30960             littleEndian
30961         );
30962     },
30963     
30964     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30965     {
30966         var tagsNumber,
30967             dirEndOffset,
30968             i;
30969         if (dirOffset + 6 > dataView.byteLength) {
30970             Roo.log('Invalid Exif data: Invalid directory offset.');
30971             return;
30972         }
30973         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30974         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30975         if (dirEndOffset + 4 > dataView.byteLength) {
30976             Roo.log('Invalid Exif data: Invalid directory size.');
30977             return;
30978         }
30979         for (i = 0; i < tagsNumber; i += 1) {
30980             this.parseExifTag(
30981                 dataView,
30982                 tiffOffset,
30983                 dirOffset + 2 + 12 * i, // tag offset
30984                 littleEndian
30985             );
30986         }
30987         // Return the offset to the next directory:
30988         return dataView.getUint32(dirEndOffset, littleEndian);
30989     },
30990     
30991     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30992     {
30993         var tag = dataView.getUint16(offset, littleEndian);
30994         
30995         this.exif[tag] = this.getExifValue(
30996             dataView,
30997             tiffOffset,
30998             offset,
30999             dataView.getUint16(offset + 2, littleEndian), // tag type
31000             dataView.getUint32(offset + 4, littleEndian), // tag length
31001             littleEndian
31002         );
31003     },
31004     
31005     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31006     {
31007         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31008             tagSize,
31009             dataOffset,
31010             values,
31011             i,
31012             str,
31013             c;
31014     
31015         if (!tagType) {
31016             Roo.log('Invalid Exif data: Invalid tag type.');
31017             return;
31018         }
31019         
31020         tagSize = tagType.size * length;
31021         // Determine if the value is contained in the dataOffset bytes,
31022         // or if the value at the dataOffset is a pointer to the actual data:
31023         dataOffset = tagSize > 4 ?
31024                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31025         if (dataOffset + tagSize > dataView.byteLength) {
31026             Roo.log('Invalid Exif data: Invalid data offset.');
31027             return;
31028         }
31029         if (length === 1) {
31030             return tagType.getValue(dataView, dataOffset, littleEndian);
31031         }
31032         values = [];
31033         for (i = 0; i < length; i += 1) {
31034             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31035         }
31036         
31037         if (tagType.ascii) {
31038             str = '';
31039             // Concatenate the chars:
31040             for (i = 0; i < values.length; i += 1) {
31041                 c = values[i];
31042                 // Ignore the terminating NULL byte(s):
31043                 if (c === '\u0000') {
31044                     break;
31045                 }
31046                 str += c;
31047             }
31048             return str;
31049         }
31050         return values;
31051     }
31052     
31053 });
31054
31055 Roo.apply(Roo.bootstrap.UploadCropbox, {
31056     tags : {
31057         'Orientation': 0x0112
31058     },
31059     
31060     Orientation: {
31061             1: 0, //'top-left',
31062 //            2: 'top-right',
31063             3: 180, //'bottom-right',
31064 //            4: 'bottom-left',
31065 //            5: 'left-top',
31066             6: 90, //'right-top',
31067 //            7: 'right-bottom',
31068             8: 270 //'left-bottom'
31069     },
31070     
31071     exifTagTypes : {
31072         // byte, 8-bit unsigned int:
31073         1: {
31074             getValue: function (dataView, dataOffset) {
31075                 return dataView.getUint8(dataOffset);
31076             },
31077             size: 1
31078         },
31079         // ascii, 8-bit byte:
31080         2: {
31081             getValue: function (dataView, dataOffset) {
31082                 return String.fromCharCode(dataView.getUint8(dataOffset));
31083             },
31084             size: 1,
31085             ascii: true
31086         },
31087         // short, 16 bit int:
31088         3: {
31089             getValue: function (dataView, dataOffset, littleEndian) {
31090                 return dataView.getUint16(dataOffset, littleEndian);
31091             },
31092             size: 2
31093         },
31094         // long, 32 bit int:
31095         4: {
31096             getValue: function (dataView, dataOffset, littleEndian) {
31097                 return dataView.getUint32(dataOffset, littleEndian);
31098             },
31099             size: 4
31100         },
31101         // rational = two long values, first is numerator, second is denominator:
31102         5: {
31103             getValue: function (dataView, dataOffset, littleEndian) {
31104                 return dataView.getUint32(dataOffset, littleEndian) /
31105                     dataView.getUint32(dataOffset + 4, littleEndian);
31106             },
31107             size: 8
31108         },
31109         // slong, 32 bit signed int:
31110         9: {
31111             getValue: function (dataView, dataOffset, littleEndian) {
31112                 return dataView.getInt32(dataOffset, littleEndian);
31113             },
31114             size: 4
31115         },
31116         // srational, two slongs, first is numerator, second is denominator:
31117         10: {
31118             getValue: function (dataView, dataOffset, littleEndian) {
31119                 return dataView.getInt32(dataOffset, littleEndian) /
31120                     dataView.getInt32(dataOffset + 4, littleEndian);
31121             },
31122             size: 8
31123         }
31124     },
31125     
31126     footer : {
31127         STANDARD : [
31128             {
31129                 tag : 'div',
31130                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31131                 action : 'rotate-left',
31132                 cn : [
31133                     {
31134                         tag : 'button',
31135                         cls : 'btn btn-default',
31136                         html : '<i class="fa fa-undo"></i>'
31137                     }
31138                 ]
31139             },
31140             {
31141                 tag : 'div',
31142                 cls : 'btn-group roo-upload-cropbox-picture',
31143                 action : 'picture',
31144                 cn : [
31145                     {
31146                         tag : 'button',
31147                         cls : 'btn btn-default',
31148                         html : '<i class="fa fa-picture-o"></i>'
31149                     }
31150                 ]
31151             },
31152             {
31153                 tag : 'div',
31154                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31155                 action : 'rotate-right',
31156                 cn : [
31157                     {
31158                         tag : 'button',
31159                         cls : 'btn btn-default',
31160                         html : '<i class="fa fa-repeat"></i>'
31161                     }
31162                 ]
31163             }
31164         ],
31165         DOCUMENT : [
31166             {
31167                 tag : 'div',
31168                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31169                 action : 'rotate-left',
31170                 cn : [
31171                     {
31172                         tag : 'button',
31173                         cls : 'btn btn-default',
31174                         html : '<i class="fa fa-undo"></i>'
31175                     }
31176                 ]
31177             },
31178             {
31179                 tag : 'div',
31180                 cls : 'btn-group roo-upload-cropbox-download',
31181                 action : 'download',
31182                 cn : [
31183                     {
31184                         tag : 'button',
31185                         cls : 'btn btn-default',
31186                         html : '<i class="fa fa-download"></i>'
31187                     }
31188                 ]
31189             },
31190             {
31191                 tag : 'div',
31192                 cls : 'btn-group roo-upload-cropbox-crop',
31193                 action : 'crop',
31194                 cn : [
31195                     {
31196                         tag : 'button',
31197                         cls : 'btn btn-default',
31198                         html : '<i class="fa fa-crop"></i>'
31199                     }
31200                 ]
31201             },
31202             {
31203                 tag : 'div',
31204                 cls : 'btn-group roo-upload-cropbox-trash',
31205                 action : 'trash',
31206                 cn : [
31207                     {
31208                         tag : 'button',
31209                         cls : 'btn btn-default',
31210                         html : '<i class="fa fa-trash"></i>'
31211                     }
31212                 ]
31213             },
31214             {
31215                 tag : 'div',
31216                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31217                 action : 'rotate-right',
31218                 cn : [
31219                     {
31220                         tag : 'button',
31221                         cls : 'btn btn-default',
31222                         html : '<i class="fa fa-repeat"></i>'
31223                     }
31224                 ]
31225             }
31226         ],
31227         ROTATOR : [
31228             {
31229                 tag : 'div',
31230                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31231                 action : 'rotate-left',
31232                 cn : [
31233                     {
31234                         tag : 'button',
31235                         cls : 'btn btn-default',
31236                         html : '<i class="fa fa-undo"></i>'
31237                     }
31238                 ]
31239             },
31240             {
31241                 tag : 'div',
31242                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31243                 action : 'rotate-right',
31244                 cn : [
31245                     {
31246                         tag : 'button',
31247                         cls : 'btn btn-default',
31248                         html : '<i class="fa fa-repeat"></i>'
31249                     }
31250                 ]
31251             }
31252         ]
31253     }
31254 });
31255
31256 /*
31257 * Licence: LGPL
31258 */
31259
31260 /**
31261  * @class Roo.bootstrap.DocumentManager
31262  * @extends Roo.bootstrap.Component
31263  * Bootstrap DocumentManager class
31264  * @cfg {String} paramName default 'imageUpload'
31265  * @cfg {String} toolTipName default 'filename'
31266  * @cfg {String} method default POST
31267  * @cfg {String} url action url
31268  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31269  * @cfg {Boolean} multiple multiple upload default true
31270  * @cfg {Number} thumbSize default 300
31271  * @cfg {String} fieldLabel
31272  * @cfg {Number} labelWidth default 4
31273  * @cfg {String} labelAlign (left|top) default left
31274  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31275 * @cfg {Number} labellg set the width of label (1-12)
31276  * @cfg {Number} labelmd set the width of label (1-12)
31277  * @cfg {Number} labelsm set the width of label (1-12)
31278  * @cfg {Number} labelxs set the width of label (1-12)
31279  * 
31280  * @constructor
31281  * Create a new DocumentManager
31282  * @param {Object} config The config object
31283  */
31284
31285 Roo.bootstrap.DocumentManager = function(config){
31286     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31287     
31288     this.files = [];
31289     this.delegates = [];
31290     
31291     this.addEvents({
31292         /**
31293          * @event initial
31294          * Fire when initial the DocumentManager
31295          * @param {Roo.bootstrap.DocumentManager} this
31296          */
31297         "initial" : true,
31298         /**
31299          * @event inspect
31300          * inspect selected file
31301          * @param {Roo.bootstrap.DocumentManager} this
31302          * @param {File} file
31303          */
31304         "inspect" : true,
31305         /**
31306          * @event exception
31307          * Fire when xhr load exception
31308          * @param {Roo.bootstrap.DocumentManager} this
31309          * @param {XMLHttpRequest} xhr
31310          */
31311         "exception" : true,
31312         /**
31313          * @event afterupload
31314          * Fire when xhr load exception
31315          * @param {Roo.bootstrap.DocumentManager} this
31316          * @param {XMLHttpRequest} xhr
31317          */
31318         "afterupload" : true,
31319         /**
31320          * @event prepare
31321          * prepare the form data
31322          * @param {Roo.bootstrap.DocumentManager} this
31323          * @param {Object} formData
31324          */
31325         "prepare" : true,
31326         /**
31327          * @event remove
31328          * Fire when remove the file
31329          * @param {Roo.bootstrap.DocumentManager} this
31330          * @param {Object} file
31331          */
31332         "remove" : true,
31333         /**
31334          * @event refresh
31335          * Fire after refresh the file
31336          * @param {Roo.bootstrap.DocumentManager} this
31337          */
31338         "refresh" : true,
31339         /**
31340          * @event click
31341          * Fire after click the image
31342          * @param {Roo.bootstrap.DocumentManager} this
31343          * @param {Object} file
31344          */
31345         "click" : true,
31346         /**
31347          * @event edit
31348          * Fire when upload a image and editable set to true
31349          * @param {Roo.bootstrap.DocumentManager} this
31350          * @param {Object} file
31351          */
31352         "edit" : true,
31353         /**
31354          * @event beforeselectfile
31355          * Fire before select file
31356          * @param {Roo.bootstrap.DocumentManager} this
31357          */
31358         "beforeselectfile" : true,
31359         /**
31360          * @event process
31361          * Fire before process file
31362          * @param {Roo.bootstrap.DocumentManager} this
31363          * @param {Object} file
31364          */
31365         "process" : true,
31366         /**
31367          * @event previewrendered
31368          * Fire when preview rendered
31369          * @param {Roo.bootstrap.DocumentManager} this
31370          * @param {Object} file
31371          */
31372         "previewrendered" : true,
31373         /**
31374          */
31375         "previewResize" : true
31376         
31377     });
31378 };
31379
31380 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31381     
31382     boxes : 0,
31383     inputName : '',
31384     thumbSize : 300,
31385     multiple : true,
31386     files : false,
31387     method : 'POST',
31388     url : '',
31389     paramName : 'imageUpload',
31390     toolTipName : 'filename',
31391     fieldLabel : '',
31392     labelWidth : 4,
31393     labelAlign : 'left',
31394     editable : true,
31395     delegates : false,
31396     xhr : false, 
31397     
31398     labellg : 0,
31399     labelmd : 0,
31400     labelsm : 0,
31401     labelxs : 0,
31402     
31403     getAutoCreate : function()
31404     {   
31405         var managerWidget = {
31406             tag : 'div',
31407             cls : 'roo-document-manager',
31408             cn : [
31409                 {
31410                     tag : 'input',
31411                     cls : 'roo-document-manager-selector',
31412                     type : 'file'
31413                 },
31414                 {
31415                     tag : 'div',
31416                     cls : 'roo-document-manager-uploader',
31417                     cn : [
31418                         {
31419                             tag : 'div',
31420                             cls : 'roo-document-manager-upload-btn',
31421                             html : '<i class="fa fa-plus"></i>'
31422                         }
31423                     ]
31424                     
31425                 }
31426             ]
31427         };
31428         
31429         var content = [
31430             {
31431                 tag : 'div',
31432                 cls : 'column col-md-12',
31433                 cn : managerWidget
31434             }
31435         ];
31436         
31437         if(this.fieldLabel.length){
31438             
31439             content = [
31440                 {
31441                     tag : 'div',
31442                     cls : 'column col-md-12',
31443                     html : this.fieldLabel
31444                 },
31445                 {
31446                     tag : 'div',
31447                     cls : 'column col-md-12',
31448                     cn : managerWidget
31449                 }
31450             ];
31451
31452             if(this.labelAlign == 'left'){
31453                 content = [
31454                     {
31455                         tag : 'div',
31456                         cls : 'column',
31457                         html : this.fieldLabel
31458                     },
31459                     {
31460                         tag : 'div',
31461                         cls : 'column',
31462                         cn : managerWidget
31463                     }
31464                 ];
31465                 
31466                 if(this.labelWidth > 12){
31467                     content[0].style = "width: " + this.labelWidth + 'px';
31468                 }
31469
31470                 if(this.labelWidth < 13 && this.labelmd == 0){
31471                     this.labelmd = this.labelWidth;
31472                 }
31473
31474                 if(this.labellg > 0){
31475                     content[0].cls += ' col-lg-' + this.labellg;
31476                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31477                 }
31478
31479                 if(this.labelmd > 0){
31480                     content[0].cls += ' col-md-' + this.labelmd;
31481                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31482                 }
31483
31484                 if(this.labelsm > 0){
31485                     content[0].cls += ' col-sm-' + this.labelsm;
31486                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31487                 }
31488
31489                 if(this.labelxs > 0){
31490                     content[0].cls += ' col-xs-' + this.labelxs;
31491                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31492                 }
31493                 
31494             }
31495         }
31496         
31497         var cfg = {
31498             tag : 'div',
31499             cls : 'row clearfix',
31500             cn : content
31501         };
31502         
31503         return cfg;
31504         
31505     },
31506     
31507     initEvents : function()
31508     {
31509         this.managerEl = this.el.select('.roo-document-manager', true).first();
31510         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31511         
31512         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31513         this.selectorEl.hide();
31514         
31515         if(this.multiple){
31516             this.selectorEl.attr('multiple', 'multiple');
31517         }
31518         
31519         this.selectorEl.on('change', this.onFileSelected, this);
31520         
31521         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31522         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31523         
31524         this.uploader.on('click', this.onUploaderClick, this);
31525         
31526         this.renderProgressDialog();
31527         
31528         var _this = this;
31529         
31530         window.addEventListener("resize", function() { _this.refresh(); } );
31531         
31532         this.fireEvent('initial', this);
31533     },
31534     
31535     renderProgressDialog : function()
31536     {
31537         var _this = this;
31538         
31539         this.progressDialog = new Roo.bootstrap.Modal({
31540             cls : 'roo-document-manager-progress-dialog',
31541             allow_close : false,
31542             animate : false,
31543             title : '',
31544             buttons : [
31545                 {
31546                     name  :'cancel',
31547                     weight : 'danger',
31548                     html : 'Cancel'
31549                 }
31550             ], 
31551             listeners : { 
31552                 btnclick : function() {
31553                     _this.uploadCancel();
31554                     this.hide();
31555                 }
31556             }
31557         });
31558          
31559         this.progressDialog.render(Roo.get(document.body));
31560          
31561         this.progress = new Roo.bootstrap.Progress({
31562             cls : 'roo-document-manager-progress',
31563             active : true,
31564             striped : true
31565         });
31566         
31567         this.progress.render(this.progressDialog.getChildContainer());
31568         
31569         this.progressBar = new Roo.bootstrap.ProgressBar({
31570             cls : 'roo-document-manager-progress-bar',
31571             aria_valuenow : 0,
31572             aria_valuemin : 0,
31573             aria_valuemax : 12,
31574             panel : 'success'
31575         });
31576         
31577         this.progressBar.render(this.progress.getChildContainer());
31578     },
31579     
31580     onUploaderClick : function(e)
31581     {
31582         e.preventDefault();
31583      
31584         if(this.fireEvent('beforeselectfile', this) != false){
31585             this.selectorEl.dom.click();
31586         }
31587         
31588     },
31589     
31590     onFileSelected : function(e)
31591     {
31592         e.preventDefault();
31593         
31594         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31595             return;
31596         }
31597         
31598         Roo.each(this.selectorEl.dom.files, function(file){
31599             if(this.fireEvent('inspect', this, file) != false){
31600                 this.files.push(file);
31601             }
31602         }, this);
31603         
31604         this.queue();
31605         
31606     },
31607     
31608     queue : function()
31609     {
31610         this.selectorEl.dom.value = '';
31611         
31612         if(!this.files || !this.files.length){
31613             return;
31614         }
31615         
31616         if(this.boxes > 0 && this.files.length > this.boxes){
31617             this.files = this.files.slice(0, this.boxes);
31618         }
31619         
31620         this.uploader.show();
31621         
31622         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31623             this.uploader.hide();
31624         }
31625         
31626         var _this = this;
31627         
31628         var files = [];
31629         
31630         var docs = [];
31631         
31632         Roo.each(this.files, function(file){
31633             
31634             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31635                 var f = this.renderPreview(file);
31636                 files.push(f);
31637                 return;
31638             }
31639             
31640             if(file.type.indexOf('image') != -1){
31641                 this.delegates.push(
31642                     (function(){
31643                         _this.process(file);
31644                     }).createDelegate(this)
31645                 );
31646         
31647                 return;
31648             }
31649             
31650             docs.push(
31651                 (function(){
31652                     _this.process(file);
31653                 }).createDelegate(this)
31654             );
31655             
31656         }, this);
31657         
31658         this.files = files;
31659         
31660         this.delegates = this.delegates.concat(docs);
31661         
31662         if(!this.delegates.length){
31663             this.refresh();
31664             return;
31665         }
31666         
31667         this.progressBar.aria_valuemax = this.delegates.length;
31668         
31669         this.arrange();
31670         
31671         return;
31672     },
31673     
31674     arrange : function()
31675     {
31676         if(!this.delegates.length){
31677             this.progressDialog.hide();
31678             this.refresh();
31679             return;
31680         }
31681         
31682         var delegate = this.delegates.shift();
31683         
31684         this.progressDialog.show();
31685         
31686         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31687         
31688         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31689         
31690         delegate();
31691     },
31692     
31693     refresh : function()
31694     {
31695         this.uploader.show();
31696         
31697         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31698             this.uploader.hide();
31699         }
31700         
31701         Roo.isTouch ? this.closable(false) : this.closable(true);
31702         
31703         this.fireEvent('refresh', this);
31704     },
31705     
31706     onRemove : function(e, el, o)
31707     {
31708         e.preventDefault();
31709         
31710         this.fireEvent('remove', this, o);
31711         
31712     },
31713     
31714     remove : function(o)
31715     {
31716         var files = [];
31717         
31718         Roo.each(this.files, function(file){
31719             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31720                 files.push(file);
31721                 return;
31722             }
31723
31724             o.target.remove();
31725
31726         }, this);
31727         
31728         this.files = files;
31729         
31730         this.refresh();
31731     },
31732     
31733     clear : function()
31734     {
31735         Roo.each(this.files, function(file){
31736             if(!file.target){
31737                 return;
31738             }
31739             
31740             file.target.remove();
31741
31742         }, this);
31743         
31744         this.files = [];
31745         
31746         this.refresh();
31747     },
31748     
31749     onClick : function(e, el, o)
31750     {
31751         e.preventDefault();
31752         
31753         this.fireEvent('click', this, o);
31754         
31755     },
31756     
31757     closable : function(closable)
31758     {
31759         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31760             
31761             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31762             
31763             if(closable){
31764                 el.show();
31765                 return;
31766             }
31767             
31768             el.hide();
31769             
31770         }, this);
31771     },
31772     
31773     xhrOnLoad : function(xhr)
31774     {
31775         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31776             el.remove();
31777         }, this);
31778         
31779         if (xhr.readyState !== 4) {
31780             this.arrange();
31781             this.fireEvent('exception', this, xhr);
31782             return;
31783         }
31784
31785         var response = Roo.decode(xhr.responseText);
31786         
31787         if(!response.success){
31788             this.arrange();
31789             this.fireEvent('exception', this, xhr);
31790             return;
31791         }
31792         
31793         var file = this.renderPreview(response.data);
31794         
31795         this.files.push(file);
31796         
31797         this.arrange();
31798         
31799         this.fireEvent('afterupload', this, xhr);
31800         
31801     },
31802     
31803     xhrOnError : function(xhr)
31804     {
31805         Roo.log('xhr on error');
31806         
31807         var response = Roo.decode(xhr.responseText);
31808           
31809         Roo.log(response);
31810         
31811         this.arrange();
31812     },
31813     
31814     process : function(file)
31815     {
31816         if(this.fireEvent('process', this, file) !== false){
31817             if(this.editable && file.type.indexOf('image') != -1){
31818                 this.fireEvent('edit', this, file);
31819                 return;
31820             }
31821
31822             this.uploadStart(file, false);
31823
31824             return;
31825         }
31826         
31827     },
31828     
31829     uploadStart : function(file, crop)
31830     {
31831         this.xhr = new XMLHttpRequest();
31832         
31833         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31834             this.arrange();
31835             return;
31836         }
31837         
31838         file.xhr = this.xhr;
31839             
31840         this.managerEl.createChild({
31841             tag : 'div',
31842             cls : 'roo-document-manager-loading',
31843             cn : [
31844                 {
31845                     tag : 'div',
31846                     tooltip : file.name,
31847                     cls : 'roo-document-manager-thumb',
31848                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31849                 }
31850             ]
31851
31852         });
31853
31854         this.xhr.open(this.method, this.url, true);
31855         
31856         var headers = {
31857             "Accept": "application/json",
31858             "Cache-Control": "no-cache",
31859             "X-Requested-With": "XMLHttpRequest"
31860         };
31861         
31862         for (var headerName in headers) {
31863             var headerValue = headers[headerName];
31864             if (headerValue) {
31865                 this.xhr.setRequestHeader(headerName, headerValue);
31866             }
31867         }
31868         
31869         var _this = this;
31870         
31871         this.xhr.onload = function()
31872         {
31873             _this.xhrOnLoad(_this.xhr);
31874         }
31875         
31876         this.xhr.onerror = function()
31877         {
31878             _this.xhrOnError(_this.xhr);
31879         }
31880         
31881         var formData = new FormData();
31882
31883         formData.append('returnHTML', 'NO');
31884         
31885         if(crop){
31886             formData.append('crop', crop);
31887         }
31888         
31889         formData.append(this.paramName, file, file.name);
31890         
31891         var options = {
31892             file : file, 
31893             manually : false
31894         };
31895         
31896         if(this.fireEvent('prepare', this, formData, options) != false){
31897             
31898             if(options.manually){
31899                 return;
31900             }
31901             
31902             this.xhr.send(formData);
31903             return;
31904         };
31905         
31906         this.uploadCancel();
31907     },
31908     
31909     uploadCancel : function()
31910     {
31911         if (this.xhr) {
31912             this.xhr.abort();
31913         }
31914         
31915         this.delegates = [];
31916         
31917         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31918             el.remove();
31919         }, this);
31920         
31921         this.arrange();
31922     },
31923     
31924     renderPreview : function(file)
31925     {
31926         if(typeof(file.target) != 'undefined' && file.target){
31927             return file;
31928         }
31929         
31930         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31931         
31932         var previewEl = this.managerEl.createChild({
31933             tag : 'div',
31934             cls : 'roo-document-manager-preview',
31935             cn : [
31936                 {
31937                     tag : 'div',
31938                     tooltip : file[this.toolTipName],
31939                     cls : 'roo-document-manager-thumb',
31940                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31941                 },
31942                 {
31943                     tag : 'button',
31944                     cls : 'close',
31945                     html : '<i class="fa fa-times-circle"></i>'
31946                 }
31947             ]
31948         });
31949
31950         var close = previewEl.select('button.close', true).first();
31951
31952         close.on('click', this.onRemove, this, file);
31953
31954         file.target = previewEl;
31955
31956         var image = previewEl.select('img', true).first();
31957         
31958         var _this = this;
31959         
31960         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31961         
31962         image.on('click', this.onClick, this, file);
31963         
31964         this.fireEvent('previewrendered', this, file);
31965         
31966         return file;
31967         
31968     },
31969     
31970     onPreviewLoad : function(file, image)
31971     {
31972         if(typeof(file.target) == 'undefined' || !file.target){
31973             return;
31974         }
31975         
31976         var width = image.dom.naturalWidth || image.dom.width;
31977         var height = image.dom.naturalHeight || image.dom.height;
31978         
31979         if(!this.previewResize) {
31980             return;
31981         }
31982         
31983         if(width > height){
31984             file.target.addClass('wide');
31985             return;
31986         }
31987         
31988         file.target.addClass('tall');
31989         return;
31990         
31991     },
31992     
31993     uploadFromSource : function(file, crop)
31994     {
31995         this.xhr = new XMLHttpRequest();
31996         
31997         this.managerEl.createChild({
31998             tag : 'div',
31999             cls : 'roo-document-manager-loading',
32000             cn : [
32001                 {
32002                     tag : 'div',
32003                     tooltip : file.name,
32004                     cls : 'roo-document-manager-thumb',
32005                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32006                 }
32007             ]
32008
32009         });
32010
32011         this.xhr.open(this.method, this.url, true);
32012         
32013         var headers = {
32014             "Accept": "application/json",
32015             "Cache-Control": "no-cache",
32016             "X-Requested-With": "XMLHttpRequest"
32017         };
32018         
32019         for (var headerName in headers) {
32020             var headerValue = headers[headerName];
32021             if (headerValue) {
32022                 this.xhr.setRequestHeader(headerName, headerValue);
32023             }
32024         }
32025         
32026         var _this = this;
32027         
32028         this.xhr.onload = function()
32029         {
32030             _this.xhrOnLoad(_this.xhr);
32031         }
32032         
32033         this.xhr.onerror = function()
32034         {
32035             _this.xhrOnError(_this.xhr);
32036         }
32037         
32038         var formData = new FormData();
32039
32040         formData.append('returnHTML', 'NO');
32041         
32042         formData.append('crop', crop);
32043         
32044         if(typeof(file.filename) != 'undefined'){
32045             formData.append('filename', file.filename);
32046         }
32047         
32048         if(typeof(file.mimetype) != 'undefined'){
32049             formData.append('mimetype', file.mimetype);
32050         }
32051         
32052         Roo.log(formData);
32053         
32054         if(this.fireEvent('prepare', this, formData) != false){
32055             this.xhr.send(formData);
32056         };
32057     }
32058 });
32059
32060 /*
32061 * Licence: LGPL
32062 */
32063
32064 /**
32065  * @class Roo.bootstrap.DocumentViewer
32066  * @extends Roo.bootstrap.Component
32067  * Bootstrap DocumentViewer class
32068  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32069  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32070  * 
32071  * @constructor
32072  * Create a new DocumentViewer
32073  * @param {Object} config The config object
32074  */
32075
32076 Roo.bootstrap.DocumentViewer = function(config){
32077     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32078     
32079     this.addEvents({
32080         /**
32081          * @event initial
32082          * Fire after initEvent
32083          * @param {Roo.bootstrap.DocumentViewer} this
32084          */
32085         "initial" : true,
32086         /**
32087          * @event click
32088          * Fire after click
32089          * @param {Roo.bootstrap.DocumentViewer} this
32090          */
32091         "click" : true,
32092         /**
32093          * @event download
32094          * Fire after download button
32095          * @param {Roo.bootstrap.DocumentViewer} this
32096          */
32097         "download" : true,
32098         /**
32099          * @event trash
32100          * Fire after trash button
32101          * @param {Roo.bootstrap.DocumentViewer} this
32102          */
32103         "trash" : true
32104         
32105     });
32106 };
32107
32108 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32109     
32110     showDownload : true,
32111     
32112     showTrash : true,
32113     
32114     getAutoCreate : function()
32115     {
32116         var cfg = {
32117             tag : 'div',
32118             cls : 'roo-document-viewer',
32119             cn : [
32120                 {
32121                     tag : 'div',
32122                     cls : 'roo-document-viewer-body',
32123                     cn : [
32124                         {
32125                             tag : 'div',
32126                             cls : 'roo-document-viewer-thumb',
32127                             cn : [
32128                                 {
32129                                     tag : 'img',
32130                                     cls : 'roo-document-viewer-image'
32131                                 }
32132                             ]
32133                         }
32134                     ]
32135                 },
32136                 {
32137                     tag : 'div',
32138                     cls : 'roo-document-viewer-footer',
32139                     cn : {
32140                         tag : 'div',
32141                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32142                         cn : [
32143                             {
32144                                 tag : 'div',
32145                                 cls : 'btn-group roo-document-viewer-download',
32146                                 cn : [
32147                                     {
32148                                         tag : 'button',
32149                                         cls : 'btn btn-default',
32150                                         html : '<i class="fa fa-download"></i>'
32151                                     }
32152                                 ]
32153                             },
32154                             {
32155                                 tag : 'div',
32156                                 cls : 'btn-group roo-document-viewer-trash',
32157                                 cn : [
32158                                     {
32159                                         tag : 'button',
32160                                         cls : 'btn btn-default',
32161                                         html : '<i class="fa fa-trash"></i>'
32162                                     }
32163                                 ]
32164                             }
32165                         ]
32166                     }
32167                 }
32168             ]
32169         };
32170         
32171         return cfg;
32172     },
32173     
32174     initEvents : function()
32175     {
32176         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32177         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32178         
32179         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32180         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32181         
32182         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32183         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32184         
32185         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32186         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32187         
32188         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32189         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32190         
32191         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32192         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32193         
32194         this.bodyEl.on('click', this.onClick, this);
32195         this.downloadBtn.on('click', this.onDownload, this);
32196         this.trashBtn.on('click', this.onTrash, this);
32197         
32198         this.downloadBtn.hide();
32199         this.trashBtn.hide();
32200         
32201         if(this.showDownload){
32202             this.downloadBtn.show();
32203         }
32204         
32205         if(this.showTrash){
32206             this.trashBtn.show();
32207         }
32208         
32209         if(!this.showDownload && !this.showTrash) {
32210             this.footerEl.hide();
32211         }
32212         
32213     },
32214     
32215     initial : function()
32216     {
32217         this.fireEvent('initial', this);
32218         
32219     },
32220     
32221     onClick : function(e)
32222     {
32223         e.preventDefault();
32224         
32225         this.fireEvent('click', this);
32226     },
32227     
32228     onDownload : function(e)
32229     {
32230         e.preventDefault();
32231         
32232         this.fireEvent('download', this);
32233     },
32234     
32235     onTrash : function(e)
32236     {
32237         e.preventDefault();
32238         
32239         this.fireEvent('trash', this);
32240     }
32241     
32242 });
32243 /*
32244  * - LGPL
32245  *
32246  * nav progress bar
32247  * 
32248  */
32249
32250 /**
32251  * @class Roo.bootstrap.NavProgressBar
32252  * @extends Roo.bootstrap.Component
32253  * Bootstrap NavProgressBar class
32254  * 
32255  * @constructor
32256  * Create a new nav progress bar
32257  * @param {Object} config The config object
32258  */
32259
32260 Roo.bootstrap.NavProgressBar = function(config){
32261     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32262
32263     this.bullets = this.bullets || [];
32264    
32265 //    Roo.bootstrap.NavProgressBar.register(this);
32266      this.addEvents({
32267         /**
32268              * @event changed
32269              * Fires when the active item changes
32270              * @param {Roo.bootstrap.NavProgressBar} this
32271              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32272              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32273          */
32274         'changed': true
32275      });
32276     
32277 };
32278
32279 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32280     
32281     bullets : [],
32282     barItems : [],
32283     
32284     getAutoCreate : function()
32285     {
32286         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32287         
32288         cfg = {
32289             tag : 'div',
32290             cls : 'roo-navigation-bar-group',
32291             cn : [
32292                 {
32293                     tag : 'div',
32294                     cls : 'roo-navigation-top-bar'
32295                 },
32296                 {
32297                     tag : 'div',
32298                     cls : 'roo-navigation-bullets-bar',
32299                     cn : [
32300                         {
32301                             tag : 'ul',
32302                             cls : 'roo-navigation-bar'
32303                         }
32304                     ]
32305                 },
32306                 
32307                 {
32308                     tag : 'div',
32309                     cls : 'roo-navigation-bottom-bar'
32310                 }
32311             ]
32312             
32313         };
32314         
32315         return cfg;
32316         
32317     },
32318     
32319     initEvents: function() 
32320     {
32321         
32322     },
32323     
32324     onRender : function(ct, position) 
32325     {
32326         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32327         
32328         if(this.bullets.length){
32329             Roo.each(this.bullets, function(b){
32330                this.addItem(b);
32331             }, this);
32332         }
32333         
32334         this.format();
32335         
32336     },
32337     
32338     addItem : function(cfg)
32339     {
32340         var item = new Roo.bootstrap.NavProgressItem(cfg);
32341         
32342         item.parentId = this.id;
32343         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32344         
32345         if(cfg.html){
32346             var top = new Roo.bootstrap.Element({
32347                 tag : 'div',
32348                 cls : 'roo-navigation-bar-text'
32349             });
32350             
32351             var bottom = new Roo.bootstrap.Element({
32352                 tag : 'div',
32353                 cls : 'roo-navigation-bar-text'
32354             });
32355             
32356             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32357             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32358             
32359             var topText = new Roo.bootstrap.Element({
32360                 tag : 'span',
32361                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32362             });
32363             
32364             var bottomText = new Roo.bootstrap.Element({
32365                 tag : 'span',
32366                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32367             });
32368             
32369             topText.onRender(top.el, null);
32370             bottomText.onRender(bottom.el, null);
32371             
32372             item.topEl = top;
32373             item.bottomEl = bottom;
32374         }
32375         
32376         this.barItems.push(item);
32377         
32378         return item;
32379     },
32380     
32381     getActive : function()
32382     {
32383         var active = false;
32384         
32385         Roo.each(this.barItems, function(v){
32386             
32387             if (!v.isActive()) {
32388                 return;
32389             }
32390             
32391             active = v;
32392             return false;
32393             
32394         });
32395         
32396         return active;
32397     },
32398     
32399     setActiveItem : function(item)
32400     {
32401         var prev = false;
32402         
32403         Roo.each(this.barItems, function(v){
32404             if (v.rid == item.rid) {
32405                 return ;
32406             }
32407             
32408             if (v.isActive()) {
32409                 v.setActive(false);
32410                 prev = v;
32411             }
32412         });
32413
32414         item.setActive(true);
32415         
32416         this.fireEvent('changed', this, item, prev);
32417     },
32418     
32419     getBarItem: function(rid)
32420     {
32421         var ret = false;
32422         
32423         Roo.each(this.barItems, function(e) {
32424             if (e.rid != rid) {
32425                 return;
32426             }
32427             
32428             ret =  e;
32429             return false;
32430         });
32431         
32432         return ret;
32433     },
32434     
32435     indexOfItem : function(item)
32436     {
32437         var index = false;
32438         
32439         Roo.each(this.barItems, function(v, i){
32440             
32441             if (v.rid != item.rid) {
32442                 return;
32443             }
32444             
32445             index = i;
32446             return false
32447         });
32448         
32449         return index;
32450     },
32451     
32452     setActiveNext : function()
32453     {
32454         var i = this.indexOfItem(this.getActive());
32455         
32456         if (i > this.barItems.length) {
32457             return;
32458         }
32459         
32460         this.setActiveItem(this.barItems[i+1]);
32461     },
32462     
32463     setActivePrev : function()
32464     {
32465         var i = this.indexOfItem(this.getActive());
32466         
32467         if (i  < 1) {
32468             return;
32469         }
32470         
32471         this.setActiveItem(this.barItems[i-1]);
32472     },
32473     
32474     format : function()
32475     {
32476         if(!this.barItems.length){
32477             return;
32478         }
32479      
32480         var width = 100 / this.barItems.length;
32481         
32482         Roo.each(this.barItems, function(i){
32483             i.el.setStyle('width', width + '%');
32484             i.topEl.el.setStyle('width', width + '%');
32485             i.bottomEl.el.setStyle('width', width + '%');
32486         }, this);
32487         
32488     }
32489     
32490 });
32491 /*
32492  * - LGPL
32493  *
32494  * Nav Progress Item
32495  * 
32496  */
32497
32498 /**
32499  * @class Roo.bootstrap.NavProgressItem
32500  * @extends Roo.bootstrap.Component
32501  * Bootstrap NavProgressItem class
32502  * @cfg {String} rid the reference id
32503  * @cfg {Boolean} active (true|false) Is item active default false
32504  * @cfg {Boolean} disabled (true|false) Is item active default false
32505  * @cfg {String} html
32506  * @cfg {String} position (top|bottom) text position default bottom
32507  * @cfg {String} icon show icon instead of number
32508  * 
32509  * @constructor
32510  * Create a new NavProgressItem
32511  * @param {Object} config The config object
32512  */
32513 Roo.bootstrap.NavProgressItem = function(config){
32514     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32515     this.addEvents({
32516         // raw events
32517         /**
32518          * @event click
32519          * The raw click event for the entire grid.
32520          * @param {Roo.bootstrap.NavProgressItem} this
32521          * @param {Roo.EventObject} e
32522          */
32523         "click" : true
32524     });
32525    
32526 };
32527
32528 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32529     
32530     rid : '',
32531     active : false,
32532     disabled : false,
32533     html : '',
32534     position : 'bottom',
32535     icon : false,
32536     
32537     getAutoCreate : function()
32538     {
32539         var iconCls = 'roo-navigation-bar-item-icon';
32540         
32541         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32542         
32543         var cfg = {
32544             tag: 'li',
32545             cls: 'roo-navigation-bar-item',
32546             cn : [
32547                 {
32548                     tag : 'i',
32549                     cls : iconCls
32550                 }
32551             ]
32552         };
32553         
32554         if(this.active){
32555             cfg.cls += ' active';
32556         }
32557         if(this.disabled){
32558             cfg.cls += ' disabled';
32559         }
32560         
32561         return cfg;
32562     },
32563     
32564     disable : function()
32565     {
32566         this.setDisabled(true);
32567     },
32568     
32569     enable : function()
32570     {
32571         this.setDisabled(false);
32572     },
32573     
32574     initEvents: function() 
32575     {
32576         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32577         
32578         this.iconEl.on('click', this.onClick, this);
32579     },
32580     
32581     onClick : function(e)
32582     {
32583         e.preventDefault();
32584         
32585         if(this.disabled){
32586             return;
32587         }
32588         
32589         if(this.fireEvent('click', this, e) === false){
32590             return;
32591         };
32592         
32593         this.parent().setActiveItem(this);
32594     },
32595     
32596     isActive: function () 
32597     {
32598         return this.active;
32599     },
32600     
32601     setActive : function(state)
32602     {
32603         if(this.active == state){
32604             return;
32605         }
32606         
32607         this.active = state;
32608         
32609         if (state) {
32610             this.el.addClass('active');
32611             return;
32612         }
32613         
32614         this.el.removeClass('active');
32615         
32616         return;
32617     },
32618     
32619     setDisabled : function(state)
32620     {
32621         if(this.disabled == state){
32622             return;
32623         }
32624         
32625         this.disabled = state;
32626         
32627         if (state) {
32628             this.el.addClass('disabled');
32629             return;
32630         }
32631         
32632         this.el.removeClass('disabled');
32633     },
32634     
32635     tooltipEl : function()
32636     {
32637         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32638     }
32639 });
32640  
32641
32642  /*
32643  * - LGPL
32644  *
32645  * FieldLabel
32646  * 
32647  */
32648
32649 /**
32650  * @class Roo.bootstrap.FieldLabel
32651  * @extends Roo.bootstrap.Component
32652  * Bootstrap FieldLabel class
32653  * @cfg {String} html contents of the element
32654  * @cfg {String} tag tag of the element default label
32655  * @cfg {String} cls class of the element
32656  * @cfg {String} target label target 
32657  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32658  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32659  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32660  * @cfg {String} iconTooltip default "This field is required"
32661  * @cfg {String} indicatorpos (left|right) default left
32662  * 
32663  * @constructor
32664  * Create a new FieldLabel
32665  * @param {Object} config The config object
32666  */
32667
32668 Roo.bootstrap.FieldLabel = function(config){
32669     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32670     
32671     this.addEvents({
32672             /**
32673              * @event invalid
32674              * Fires after the field has been marked as invalid.
32675              * @param {Roo.form.FieldLabel} this
32676              * @param {String} msg The validation message
32677              */
32678             invalid : true,
32679             /**
32680              * @event valid
32681              * Fires after the field has been validated with no errors.
32682              * @param {Roo.form.FieldLabel} this
32683              */
32684             valid : true
32685         });
32686 };
32687
32688 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32689     
32690     tag: 'label',
32691     cls: '',
32692     html: '',
32693     target: '',
32694     allowBlank : true,
32695     invalidClass : 'has-warning',
32696     validClass : 'has-success',
32697     iconTooltip : 'This field is required',
32698     indicatorpos : 'left',
32699     
32700     getAutoCreate : function(){
32701         
32702         var cls = "";
32703         if (!this.allowBlank) {
32704             cls  = "visible";
32705         }
32706         
32707         var cfg = {
32708             tag : this.tag,
32709             cls : 'roo-bootstrap-field-label ' + this.cls,
32710             for : this.target,
32711             cn : [
32712                 {
32713                     tag : 'i',
32714                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32715                     tooltip : this.iconTooltip
32716                 },
32717                 {
32718                     tag : 'span',
32719                     html : this.html
32720                 }
32721             ] 
32722         };
32723         
32724         if(this.indicatorpos == 'right'){
32725             var cfg = {
32726                 tag : this.tag,
32727                 cls : 'roo-bootstrap-field-label ' + this.cls,
32728                 for : this.target,
32729                 cn : [
32730                     {
32731                         tag : 'span',
32732                         html : this.html
32733                     },
32734                     {
32735                         tag : 'i',
32736                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32737                         tooltip : this.iconTooltip
32738                     }
32739                 ] 
32740             };
32741         }
32742         
32743         return cfg;
32744     },
32745     
32746     initEvents: function() 
32747     {
32748         Roo.bootstrap.Element.superclass.initEvents.call(this);
32749         
32750         this.indicator = this.indicatorEl();
32751         
32752         if(this.indicator){
32753             this.indicator.removeClass('visible');
32754             this.indicator.addClass('invisible');
32755         }
32756         
32757         Roo.bootstrap.FieldLabel.register(this);
32758     },
32759     
32760     indicatorEl : function()
32761     {
32762         var indicator = this.el.select('i.roo-required-indicator',true).first();
32763         
32764         if(!indicator){
32765             return false;
32766         }
32767         
32768         return indicator;
32769         
32770     },
32771     
32772     /**
32773      * Mark this field as valid
32774      */
32775     markValid : function()
32776     {
32777         if(this.indicator){
32778             this.indicator.removeClass('visible');
32779             this.indicator.addClass('invisible');
32780         }
32781         if (Roo.bootstrap.version == 3) {
32782             this.el.removeClass(this.invalidClass);
32783             this.el.addClass(this.validClass);
32784         } else {
32785             this.el.removeClass('is-invalid');
32786             this.el.addClass('is-valid');
32787         }
32788         
32789         
32790         this.fireEvent('valid', this);
32791     },
32792     
32793     /**
32794      * Mark this field as invalid
32795      * @param {String} msg The validation message
32796      */
32797     markInvalid : function(msg)
32798     {
32799         if(this.indicator){
32800             this.indicator.removeClass('invisible');
32801             this.indicator.addClass('visible');
32802         }
32803           if (Roo.bootstrap.version == 3) {
32804             this.el.removeClass(this.validClass);
32805             this.el.addClass(this.invalidClass);
32806         } else {
32807             this.el.removeClass('is-valid');
32808             this.el.addClass('is-invalid');
32809         }
32810         
32811         
32812         this.fireEvent('invalid', this, msg);
32813     }
32814     
32815    
32816 });
32817
32818 Roo.apply(Roo.bootstrap.FieldLabel, {
32819     
32820     groups: {},
32821     
32822      /**
32823     * register a FieldLabel Group
32824     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32825     */
32826     register : function(label)
32827     {
32828         if(this.groups.hasOwnProperty(label.target)){
32829             return;
32830         }
32831      
32832         this.groups[label.target] = label;
32833         
32834     },
32835     /**
32836     * fetch a FieldLabel Group based on the target
32837     * @param {string} target
32838     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32839     */
32840     get: function(target) {
32841         if (typeof(this.groups[target]) == 'undefined') {
32842             return false;
32843         }
32844         
32845         return this.groups[target] ;
32846     }
32847 });
32848
32849  
32850
32851  /*
32852  * - LGPL
32853  *
32854  * page DateSplitField.
32855  * 
32856  */
32857
32858
32859 /**
32860  * @class Roo.bootstrap.DateSplitField
32861  * @extends Roo.bootstrap.Component
32862  * Bootstrap DateSplitField class
32863  * @cfg {string} fieldLabel - the label associated
32864  * @cfg {Number} labelWidth set the width of label (0-12)
32865  * @cfg {String} labelAlign (top|left)
32866  * @cfg {Boolean} dayAllowBlank (true|false) default false
32867  * @cfg {Boolean} monthAllowBlank (true|false) default false
32868  * @cfg {Boolean} yearAllowBlank (true|false) default false
32869  * @cfg {string} dayPlaceholder 
32870  * @cfg {string} monthPlaceholder
32871  * @cfg {string} yearPlaceholder
32872  * @cfg {string} dayFormat default 'd'
32873  * @cfg {string} monthFormat default 'm'
32874  * @cfg {string} yearFormat default 'Y'
32875  * @cfg {Number} labellg set the width of label (1-12)
32876  * @cfg {Number} labelmd set the width of label (1-12)
32877  * @cfg {Number} labelsm set the width of label (1-12)
32878  * @cfg {Number} labelxs set the width of label (1-12)
32879
32880  *     
32881  * @constructor
32882  * Create a new DateSplitField
32883  * @param {Object} config The config object
32884  */
32885
32886 Roo.bootstrap.DateSplitField = function(config){
32887     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32888     
32889     this.addEvents({
32890         // raw events
32891          /**
32892          * @event years
32893          * getting the data of years
32894          * @param {Roo.bootstrap.DateSplitField} this
32895          * @param {Object} years
32896          */
32897         "years" : true,
32898         /**
32899          * @event days
32900          * getting the data of days
32901          * @param {Roo.bootstrap.DateSplitField} this
32902          * @param {Object} days
32903          */
32904         "days" : true,
32905         /**
32906          * @event invalid
32907          * Fires after the field has been marked as invalid.
32908          * @param {Roo.form.Field} this
32909          * @param {String} msg The validation message
32910          */
32911         invalid : true,
32912        /**
32913          * @event valid
32914          * Fires after the field has been validated with no errors.
32915          * @param {Roo.form.Field} this
32916          */
32917         valid : true
32918     });
32919 };
32920
32921 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32922     
32923     fieldLabel : '',
32924     labelAlign : 'top',
32925     labelWidth : 3,
32926     dayAllowBlank : false,
32927     monthAllowBlank : false,
32928     yearAllowBlank : false,
32929     dayPlaceholder : '',
32930     monthPlaceholder : '',
32931     yearPlaceholder : '',
32932     dayFormat : 'd',
32933     monthFormat : 'm',
32934     yearFormat : 'Y',
32935     isFormField : true,
32936     labellg : 0,
32937     labelmd : 0,
32938     labelsm : 0,
32939     labelxs : 0,
32940     
32941     getAutoCreate : function()
32942     {
32943         var cfg = {
32944             tag : 'div',
32945             cls : 'row roo-date-split-field-group',
32946             cn : [
32947                 {
32948                     tag : 'input',
32949                     type : 'hidden',
32950                     cls : 'form-hidden-field roo-date-split-field-group-value',
32951                     name : this.name
32952                 }
32953             ]
32954         };
32955         
32956         var labelCls = 'col-md-12';
32957         var contentCls = 'col-md-4';
32958         
32959         if(this.fieldLabel){
32960             
32961             var label = {
32962                 tag : 'div',
32963                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32964                 cn : [
32965                     {
32966                         tag : 'label',
32967                         html : this.fieldLabel
32968                     }
32969                 ]
32970             };
32971             
32972             if(this.labelAlign == 'left'){
32973             
32974                 if(this.labelWidth > 12){
32975                     label.style = "width: " + this.labelWidth + 'px';
32976                 }
32977
32978                 if(this.labelWidth < 13 && this.labelmd == 0){
32979                     this.labelmd = this.labelWidth;
32980                 }
32981
32982                 if(this.labellg > 0){
32983                     labelCls = ' col-lg-' + this.labellg;
32984                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32985                 }
32986
32987                 if(this.labelmd > 0){
32988                     labelCls = ' col-md-' + this.labelmd;
32989                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32990                 }
32991
32992                 if(this.labelsm > 0){
32993                     labelCls = ' col-sm-' + this.labelsm;
32994                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32995                 }
32996
32997                 if(this.labelxs > 0){
32998                     labelCls = ' col-xs-' + this.labelxs;
32999                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33000                 }
33001             }
33002             
33003             label.cls += ' ' + labelCls;
33004             
33005             cfg.cn.push(label);
33006         }
33007         
33008         Roo.each(['day', 'month', 'year'], function(t){
33009             cfg.cn.push({
33010                 tag : 'div',
33011                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33012             });
33013         }, this);
33014         
33015         return cfg;
33016     },
33017     
33018     inputEl: function ()
33019     {
33020         return this.el.select('.roo-date-split-field-group-value', true).first();
33021     },
33022     
33023     onRender : function(ct, position) 
33024     {
33025         var _this = this;
33026         
33027         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33028         
33029         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33030         
33031         this.dayField = new Roo.bootstrap.ComboBox({
33032             allowBlank : this.dayAllowBlank,
33033             alwaysQuery : true,
33034             displayField : 'value',
33035             editable : false,
33036             fieldLabel : '',
33037             forceSelection : true,
33038             mode : 'local',
33039             placeholder : this.dayPlaceholder,
33040             selectOnFocus : true,
33041             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33042             triggerAction : 'all',
33043             typeAhead : true,
33044             valueField : 'value',
33045             store : new Roo.data.SimpleStore({
33046                 data : (function() {    
33047                     var days = [];
33048                     _this.fireEvent('days', _this, days);
33049                     return days;
33050                 })(),
33051                 fields : [ 'value' ]
33052             }),
33053             listeners : {
33054                 select : function (_self, record, index)
33055                 {
33056                     _this.setValue(_this.getValue());
33057                 }
33058             }
33059         });
33060
33061         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33062         
33063         this.monthField = new Roo.bootstrap.MonthField({
33064             after : '<i class=\"fa fa-calendar\"></i>',
33065             allowBlank : this.monthAllowBlank,
33066             placeholder : this.monthPlaceholder,
33067             readOnly : true,
33068             listeners : {
33069                 render : function (_self)
33070                 {
33071                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33072                         e.preventDefault();
33073                         _self.focus();
33074                     });
33075                 },
33076                 select : function (_self, oldvalue, newvalue)
33077                 {
33078                     _this.setValue(_this.getValue());
33079                 }
33080             }
33081         });
33082         
33083         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33084         
33085         this.yearField = new Roo.bootstrap.ComboBox({
33086             allowBlank : this.yearAllowBlank,
33087             alwaysQuery : true,
33088             displayField : 'value',
33089             editable : false,
33090             fieldLabel : '',
33091             forceSelection : true,
33092             mode : 'local',
33093             placeholder : this.yearPlaceholder,
33094             selectOnFocus : true,
33095             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33096             triggerAction : 'all',
33097             typeAhead : true,
33098             valueField : 'value',
33099             store : new Roo.data.SimpleStore({
33100                 data : (function() {
33101                     var years = [];
33102                     _this.fireEvent('years', _this, years);
33103                     return years;
33104                 })(),
33105                 fields : [ 'value' ]
33106             }),
33107             listeners : {
33108                 select : function (_self, record, index)
33109                 {
33110                     _this.setValue(_this.getValue());
33111                 }
33112             }
33113         });
33114
33115         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33116     },
33117     
33118     setValue : function(v, format)
33119     {
33120         this.inputEl.dom.value = v;
33121         
33122         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33123         
33124         var d = Date.parseDate(v, f);
33125         
33126         if(!d){
33127             this.validate();
33128             return;
33129         }
33130         
33131         this.setDay(d.format(this.dayFormat));
33132         this.setMonth(d.format(this.monthFormat));
33133         this.setYear(d.format(this.yearFormat));
33134         
33135         this.validate();
33136         
33137         return;
33138     },
33139     
33140     setDay : function(v)
33141     {
33142         this.dayField.setValue(v);
33143         this.inputEl.dom.value = this.getValue();
33144         this.validate();
33145         return;
33146     },
33147     
33148     setMonth : function(v)
33149     {
33150         this.monthField.setValue(v, true);
33151         this.inputEl.dom.value = this.getValue();
33152         this.validate();
33153         return;
33154     },
33155     
33156     setYear : function(v)
33157     {
33158         this.yearField.setValue(v);
33159         this.inputEl.dom.value = this.getValue();
33160         this.validate();
33161         return;
33162     },
33163     
33164     getDay : function()
33165     {
33166         return this.dayField.getValue();
33167     },
33168     
33169     getMonth : function()
33170     {
33171         return this.monthField.getValue();
33172     },
33173     
33174     getYear : function()
33175     {
33176         return this.yearField.getValue();
33177     },
33178     
33179     getValue : function()
33180     {
33181         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33182         
33183         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33184         
33185         return date;
33186     },
33187     
33188     reset : function()
33189     {
33190         this.setDay('');
33191         this.setMonth('');
33192         this.setYear('');
33193         this.inputEl.dom.value = '';
33194         this.validate();
33195         return;
33196     },
33197     
33198     validate : function()
33199     {
33200         var d = this.dayField.validate();
33201         var m = this.monthField.validate();
33202         var y = this.yearField.validate();
33203         
33204         var valid = true;
33205         
33206         if(
33207                 (!this.dayAllowBlank && !d) ||
33208                 (!this.monthAllowBlank && !m) ||
33209                 (!this.yearAllowBlank && !y)
33210         ){
33211             valid = false;
33212         }
33213         
33214         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33215             return valid;
33216         }
33217         
33218         if(valid){
33219             this.markValid();
33220             return valid;
33221         }
33222         
33223         this.markInvalid();
33224         
33225         return valid;
33226     },
33227     
33228     markValid : function()
33229     {
33230         
33231         var label = this.el.select('label', true).first();
33232         var icon = this.el.select('i.fa-star', true).first();
33233
33234         if(label && icon){
33235             icon.remove();
33236         }
33237         
33238         this.fireEvent('valid', this);
33239     },
33240     
33241      /**
33242      * Mark this field as invalid
33243      * @param {String} msg The validation message
33244      */
33245     markInvalid : function(msg)
33246     {
33247         
33248         var label = this.el.select('label', true).first();
33249         var icon = this.el.select('i.fa-star', true).first();
33250
33251         if(label && !icon){
33252             this.el.select('.roo-date-split-field-label', true).createChild({
33253                 tag : 'i',
33254                 cls : 'text-danger fa fa-lg fa-star',
33255                 tooltip : 'This field is required',
33256                 style : 'margin-right:5px;'
33257             }, label, true);
33258         }
33259         
33260         this.fireEvent('invalid', this, msg);
33261     },
33262     
33263     clearInvalid : function()
33264     {
33265         var label = this.el.select('label', true).first();
33266         var icon = this.el.select('i.fa-star', true).first();
33267
33268         if(label && icon){
33269             icon.remove();
33270         }
33271         
33272         this.fireEvent('valid', this);
33273     },
33274     
33275     getName: function()
33276     {
33277         return this.name;
33278     }
33279     
33280 });
33281
33282  /**
33283  *
33284  * This is based on 
33285  * http://masonry.desandro.com
33286  *
33287  * The idea is to render all the bricks based on vertical width...
33288  *
33289  * The original code extends 'outlayer' - we might need to use that....
33290  * 
33291  */
33292
33293
33294 /**
33295  * @class Roo.bootstrap.LayoutMasonry
33296  * @extends Roo.bootstrap.Component
33297  * Bootstrap Layout Masonry class
33298  * 
33299  * @constructor
33300  * Create a new Element
33301  * @param {Object} config The config object
33302  */
33303
33304 Roo.bootstrap.LayoutMasonry = function(config){
33305     
33306     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33307     
33308     this.bricks = [];
33309     
33310     Roo.bootstrap.LayoutMasonry.register(this);
33311     
33312     this.addEvents({
33313         // raw events
33314         /**
33315          * @event layout
33316          * Fire after layout the items
33317          * @param {Roo.bootstrap.LayoutMasonry} this
33318          * @param {Roo.EventObject} e
33319          */
33320         "layout" : true
33321     });
33322     
33323 };
33324
33325 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33326     
33327     /**
33328      * @cfg {Boolean} isLayoutInstant = no animation?
33329      */   
33330     isLayoutInstant : false, // needed?
33331    
33332     /**
33333      * @cfg {Number} boxWidth  width of the columns
33334      */   
33335     boxWidth : 450,
33336     
33337       /**
33338      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33339      */   
33340     boxHeight : 0,
33341     
33342     /**
33343      * @cfg {Number} padWidth padding below box..
33344      */   
33345     padWidth : 10, 
33346     
33347     /**
33348      * @cfg {Number} gutter gutter width..
33349      */   
33350     gutter : 10,
33351     
33352      /**
33353      * @cfg {Number} maxCols maximum number of columns
33354      */   
33355     
33356     maxCols: 0,
33357     
33358     /**
33359      * @cfg {Boolean} isAutoInitial defalut true
33360      */   
33361     isAutoInitial : true, 
33362     
33363     containerWidth: 0,
33364     
33365     /**
33366      * @cfg {Boolean} isHorizontal defalut false
33367      */   
33368     isHorizontal : false, 
33369
33370     currentSize : null,
33371     
33372     tag: 'div',
33373     
33374     cls: '',
33375     
33376     bricks: null, //CompositeElement
33377     
33378     cols : 1,
33379     
33380     _isLayoutInited : false,
33381     
33382 //    isAlternative : false, // only use for vertical layout...
33383     
33384     /**
33385      * @cfg {Number} alternativePadWidth padding below box..
33386      */   
33387     alternativePadWidth : 50,
33388     
33389     selectedBrick : [],
33390     
33391     getAutoCreate : function(){
33392         
33393         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33394         
33395         var cfg = {
33396             tag: this.tag,
33397             cls: 'blog-masonary-wrapper ' + this.cls,
33398             cn : {
33399                 cls : 'mas-boxes masonary'
33400             }
33401         };
33402         
33403         return cfg;
33404     },
33405     
33406     getChildContainer: function( )
33407     {
33408         if (this.boxesEl) {
33409             return this.boxesEl;
33410         }
33411         
33412         this.boxesEl = this.el.select('.mas-boxes').first();
33413         
33414         return this.boxesEl;
33415     },
33416     
33417     
33418     initEvents : function()
33419     {
33420         var _this = this;
33421         
33422         if(this.isAutoInitial){
33423             Roo.log('hook children rendered');
33424             this.on('childrenrendered', function() {
33425                 Roo.log('children rendered');
33426                 _this.initial();
33427             } ,this);
33428         }
33429     },
33430     
33431     initial : function()
33432     {
33433         this.selectedBrick = [];
33434         
33435         this.currentSize = this.el.getBox(true);
33436         
33437         Roo.EventManager.onWindowResize(this.resize, this); 
33438
33439         if(!this.isAutoInitial){
33440             this.layout();
33441             return;
33442         }
33443         
33444         this.layout();
33445         
33446         return;
33447         //this.layout.defer(500,this);
33448         
33449     },
33450     
33451     resize : function()
33452     {
33453         var cs = this.el.getBox(true);
33454         
33455         if (
33456                 this.currentSize.width == cs.width && 
33457                 this.currentSize.x == cs.x && 
33458                 this.currentSize.height == cs.height && 
33459                 this.currentSize.y == cs.y 
33460         ) {
33461             Roo.log("no change in with or X or Y");
33462             return;
33463         }
33464         
33465         this.currentSize = cs;
33466         
33467         this.layout();
33468         
33469     },
33470     
33471     layout : function()
33472     {   
33473         this._resetLayout();
33474         
33475         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33476         
33477         this.layoutItems( isInstant );
33478       
33479         this._isLayoutInited = true;
33480         
33481         this.fireEvent('layout', this);
33482         
33483     },
33484     
33485     _resetLayout : function()
33486     {
33487         if(this.isHorizontal){
33488             this.horizontalMeasureColumns();
33489             return;
33490         }
33491         
33492         this.verticalMeasureColumns();
33493         
33494     },
33495     
33496     verticalMeasureColumns : function()
33497     {
33498         this.getContainerWidth();
33499         
33500 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33501 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33502 //            return;
33503 //        }
33504         
33505         var boxWidth = this.boxWidth + this.padWidth;
33506         
33507         if(this.containerWidth < this.boxWidth){
33508             boxWidth = this.containerWidth
33509         }
33510         
33511         var containerWidth = this.containerWidth;
33512         
33513         var cols = Math.floor(containerWidth / boxWidth);
33514         
33515         this.cols = Math.max( cols, 1 );
33516         
33517         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33518         
33519         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33520         
33521         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33522         
33523         this.colWidth = boxWidth + avail - this.padWidth;
33524         
33525         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33526         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33527     },
33528     
33529     horizontalMeasureColumns : function()
33530     {
33531         this.getContainerWidth();
33532         
33533         var boxWidth = this.boxWidth;
33534         
33535         if(this.containerWidth < boxWidth){
33536             boxWidth = this.containerWidth;
33537         }
33538         
33539         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33540         
33541         this.el.setHeight(boxWidth);
33542         
33543     },
33544     
33545     getContainerWidth : function()
33546     {
33547         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33548     },
33549     
33550     layoutItems : function( isInstant )
33551     {
33552         Roo.log(this.bricks);
33553         
33554         var items = Roo.apply([], this.bricks);
33555         
33556         if(this.isHorizontal){
33557             this._horizontalLayoutItems( items , isInstant );
33558             return;
33559         }
33560         
33561 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33562 //            this._verticalAlternativeLayoutItems( items , isInstant );
33563 //            return;
33564 //        }
33565         
33566         this._verticalLayoutItems( items , isInstant );
33567         
33568     },
33569     
33570     _verticalLayoutItems : function ( items , isInstant)
33571     {
33572         if ( !items || !items.length ) {
33573             return;
33574         }
33575         
33576         var standard = [
33577             ['xs', 'xs', 'xs', 'tall'],
33578             ['xs', 'xs', 'tall'],
33579             ['xs', 'xs', 'sm'],
33580             ['xs', 'xs', 'xs'],
33581             ['xs', 'tall'],
33582             ['xs', 'sm'],
33583             ['xs', 'xs'],
33584             ['xs'],
33585             
33586             ['sm', 'xs', 'xs'],
33587             ['sm', 'xs'],
33588             ['sm'],
33589             
33590             ['tall', 'xs', 'xs', 'xs'],
33591             ['tall', 'xs', 'xs'],
33592             ['tall', 'xs'],
33593             ['tall']
33594             
33595         ];
33596         
33597         var queue = [];
33598         
33599         var boxes = [];
33600         
33601         var box = [];
33602         
33603         Roo.each(items, function(item, k){
33604             
33605             switch (item.size) {
33606                 // these layouts take up a full box,
33607                 case 'md' :
33608                 case 'md-left' :
33609                 case 'md-right' :
33610                 case 'wide' :
33611                     
33612                     if(box.length){
33613                         boxes.push(box);
33614                         box = [];
33615                     }
33616                     
33617                     boxes.push([item]);
33618                     
33619                     break;
33620                     
33621                 case 'xs' :
33622                 case 'sm' :
33623                 case 'tall' :
33624                     
33625                     box.push(item);
33626                     
33627                     break;
33628                 default :
33629                     break;
33630                     
33631             }
33632             
33633         }, this);
33634         
33635         if(box.length){
33636             boxes.push(box);
33637             box = [];
33638         }
33639         
33640         var filterPattern = function(box, length)
33641         {
33642             if(!box.length){
33643                 return;
33644             }
33645             
33646             var match = false;
33647             
33648             var pattern = box.slice(0, length);
33649             
33650             var format = [];
33651             
33652             Roo.each(pattern, function(i){
33653                 format.push(i.size);
33654             }, this);
33655             
33656             Roo.each(standard, function(s){
33657                 
33658                 if(String(s) != String(format)){
33659                     return;
33660                 }
33661                 
33662                 match = true;
33663                 return false;
33664                 
33665             }, this);
33666             
33667             if(!match && length == 1){
33668                 return;
33669             }
33670             
33671             if(!match){
33672                 filterPattern(box, length - 1);
33673                 return;
33674             }
33675                 
33676             queue.push(pattern);
33677
33678             box = box.slice(length, box.length);
33679
33680             filterPattern(box, 4);
33681
33682             return;
33683             
33684         }
33685         
33686         Roo.each(boxes, function(box, k){
33687             
33688             if(!box.length){
33689                 return;
33690             }
33691             
33692             if(box.length == 1){
33693                 queue.push(box);
33694                 return;
33695             }
33696             
33697             filterPattern(box, 4);
33698             
33699         }, this);
33700         
33701         this._processVerticalLayoutQueue( queue, isInstant );
33702         
33703     },
33704     
33705 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33706 //    {
33707 //        if ( !items || !items.length ) {
33708 //            return;
33709 //        }
33710 //
33711 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33712 //        
33713 //    },
33714     
33715     _horizontalLayoutItems : function ( items , isInstant)
33716     {
33717         if ( !items || !items.length || items.length < 3) {
33718             return;
33719         }
33720         
33721         items.reverse();
33722         
33723         var eItems = items.slice(0, 3);
33724         
33725         items = items.slice(3, items.length);
33726         
33727         var standard = [
33728             ['xs', 'xs', 'xs', 'wide'],
33729             ['xs', 'xs', 'wide'],
33730             ['xs', 'xs', 'sm'],
33731             ['xs', 'xs', 'xs'],
33732             ['xs', 'wide'],
33733             ['xs', 'sm'],
33734             ['xs', 'xs'],
33735             ['xs'],
33736             
33737             ['sm', 'xs', 'xs'],
33738             ['sm', 'xs'],
33739             ['sm'],
33740             
33741             ['wide', 'xs', 'xs', 'xs'],
33742             ['wide', 'xs', 'xs'],
33743             ['wide', 'xs'],
33744             ['wide'],
33745             
33746             ['wide-thin']
33747         ];
33748         
33749         var queue = [];
33750         
33751         var boxes = [];
33752         
33753         var box = [];
33754         
33755         Roo.each(items, function(item, k){
33756             
33757             switch (item.size) {
33758                 case 'md' :
33759                 case 'md-left' :
33760                 case 'md-right' :
33761                 case 'tall' :
33762                     
33763                     if(box.length){
33764                         boxes.push(box);
33765                         box = [];
33766                     }
33767                     
33768                     boxes.push([item]);
33769                     
33770                     break;
33771                     
33772                 case 'xs' :
33773                 case 'sm' :
33774                 case 'wide' :
33775                 case 'wide-thin' :
33776                     
33777                     box.push(item);
33778                     
33779                     break;
33780                 default :
33781                     break;
33782                     
33783             }
33784             
33785         }, this);
33786         
33787         if(box.length){
33788             boxes.push(box);
33789             box = [];
33790         }
33791         
33792         var filterPattern = function(box, length)
33793         {
33794             if(!box.length){
33795                 return;
33796             }
33797             
33798             var match = false;
33799             
33800             var pattern = box.slice(0, length);
33801             
33802             var format = [];
33803             
33804             Roo.each(pattern, function(i){
33805                 format.push(i.size);
33806             }, this);
33807             
33808             Roo.each(standard, function(s){
33809                 
33810                 if(String(s) != String(format)){
33811                     return;
33812                 }
33813                 
33814                 match = true;
33815                 return false;
33816                 
33817             }, this);
33818             
33819             if(!match && length == 1){
33820                 return;
33821             }
33822             
33823             if(!match){
33824                 filterPattern(box, length - 1);
33825                 return;
33826             }
33827                 
33828             queue.push(pattern);
33829
33830             box = box.slice(length, box.length);
33831
33832             filterPattern(box, 4);
33833
33834             return;
33835             
33836         }
33837         
33838         Roo.each(boxes, function(box, k){
33839             
33840             if(!box.length){
33841                 return;
33842             }
33843             
33844             if(box.length == 1){
33845                 queue.push(box);
33846                 return;
33847             }
33848             
33849             filterPattern(box, 4);
33850             
33851         }, this);
33852         
33853         
33854         var prune = [];
33855         
33856         var pos = this.el.getBox(true);
33857         
33858         var minX = pos.x;
33859         
33860         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33861         
33862         var hit_end = false;
33863         
33864         Roo.each(queue, function(box){
33865             
33866             if(hit_end){
33867                 
33868                 Roo.each(box, function(b){
33869                 
33870                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33871                     b.el.hide();
33872
33873                 }, this);
33874
33875                 return;
33876             }
33877             
33878             var mx = 0;
33879             
33880             Roo.each(box, function(b){
33881                 
33882                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33883                 b.el.show();
33884
33885                 mx = Math.max(mx, b.x);
33886                 
33887             }, this);
33888             
33889             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33890             
33891             if(maxX < minX){
33892                 
33893                 Roo.each(box, function(b){
33894                 
33895                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33896                     b.el.hide();
33897                     
33898                 }, this);
33899                 
33900                 hit_end = true;
33901                 
33902                 return;
33903             }
33904             
33905             prune.push(box);
33906             
33907         }, this);
33908         
33909         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33910     },
33911     
33912     /** Sets position of item in DOM
33913     * @param {Element} item
33914     * @param {Number} x - horizontal position
33915     * @param {Number} y - vertical position
33916     * @param {Boolean} isInstant - disables transitions
33917     */
33918     _processVerticalLayoutQueue : function( queue, isInstant )
33919     {
33920         var pos = this.el.getBox(true);
33921         var x = pos.x;
33922         var y = pos.y;
33923         var maxY = [];
33924         
33925         for (var i = 0; i < this.cols; i++){
33926             maxY[i] = pos.y;
33927         }
33928         
33929         Roo.each(queue, function(box, k){
33930             
33931             var col = k % this.cols;
33932             
33933             Roo.each(box, function(b,kk){
33934                 
33935                 b.el.position('absolute');
33936                 
33937                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33938                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33939                 
33940                 if(b.size == 'md-left' || b.size == 'md-right'){
33941                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33942                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33943                 }
33944                 
33945                 b.el.setWidth(width);
33946                 b.el.setHeight(height);
33947                 // iframe?
33948                 b.el.select('iframe',true).setSize(width,height);
33949                 
33950             }, this);
33951             
33952             for (var i = 0; i < this.cols; i++){
33953                 
33954                 if(maxY[i] < maxY[col]){
33955                     col = i;
33956                     continue;
33957                 }
33958                 
33959                 col = Math.min(col, i);
33960                 
33961             }
33962             
33963             x = pos.x + col * (this.colWidth + this.padWidth);
33964             
33965             y = maxY[col];
33966             
33967             var positions = [];
33968             
33969             switch (box.length){
33970                 case 1 :
33971                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33972                     break;
33973                 case 2 :
33974                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33975                     break;
33976                 case 3 :
33977                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33978                     break;
33979                 case 4 :
33980                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33981                     break;
33982                 default :
33983                     break;
33984             }
33985             
33986             Roo.each(box, function(b,kk){
33987                 
33988                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33989                 
33990                 var sz = b.el.getSize();
33991                 
33992                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33993                 
33994             }, this);
33995             
33996         }, this);
33997         
33998         var mY = 0;
33999         
34000         for (var i = 0; i < this.cols; i++){
34001             mY = Math.max(mY, maxY[i]);
34002         }
34003         
34004         this.el.setHeight(mY - pos.y);
34005         
34006     },
34007     
34008 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34009 //    {
34010 //        var pos = this.el.getBox(true);
34011 //        var x = pos.x;
34012 //        var y = pos.y;
34013 //        var maxX = pos.right;
34014 //        
34015 //        var maxHeight = 0;
34016 //        
34017 //        Roo.each(items, function(item, k){
34018 //            
34019 //            var c = k % 2;
34020 //            
34021 //            item.el.position('absolute');
34022 //                
34023 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34024 //
34025 //            item.el.setWidth(width);
34026 //
34027 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34028 //
34029 //            item.el.setHeight(height);
34030 //            
34031 //            if(c == 0){
34032 //                item.el.setXY([x, y], isInstant ? false : true);
34033 //            } else {
34034 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34035 //            }
34036 //            
34037 //            y = y + height + this.alternativePadWidth;
34038 //            
34039 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34040 //            
34041 //        }, this);
34042 //        
34043 //        this.el.setHeight(maxHeight);
34044 //        
34045 //    },
34046     
34047     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34048     {
34049         var pos = this.el.getBox(true);
34050         
34051         var minX = pos.x;
34052         var minY = pos.y;
34053         
34054         var maxX = pos.right;
34055         
34056         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34057         
34058         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34059         
34060         Roo.each(queue, function(box, k){
34061             
34062             Roo.each(box, function(b, kk){
34063                 
34064                 b.el.position('absolute');
34065                 
34066                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34067                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34068                 
34069                 if(b.size == 'md-left' || b.size == 'md-right'){
34070                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34071                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34072                 }
34073                 
34074                 b.el.setWidth(width);
34075                 b.el.setHeight(height);
34076                 
34077             }, this);
34078             
34079             if(!box.length){
34080                 return;
34081             }
34082             
34083             var positions = [];
34084             
34085             switch (box.length){
34086                 case 1 :
34087                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34088                     break;
34089                 case 2 :
34090                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34091                     break;
34092                 case 3 :
34093                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34094                     break;
34095                 case 4 :
34096                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34097                     break;
34098                 default :
34099                     break;
34100             }
34101             
34102             Roo.each(box, function(b,kk){
34103                 
34104                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34105                 
34106                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34107                 
34108             }, this);
34109             
34110         }, this);
34111         
34112     },
34113     
34114     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34115     {
34116         Roo.each(eItems, function(b,k){
34117             
34118             b.size = (k == 0) ? 'sm' : 'xs';
34119             b.x = (k == 0) ? 2 : 1;
34120             b.y = (k == 0) ? 2 : 1;
34121             
34122             b.el.position('absolute');
34123             
34124             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34125                 
34126             b.el.setWidth(width);
34127             
34128             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34129             
34130             b.el.setHeight(height);
34131             
34132         }, this);
34133
34134         var positions = [];
34135         
34136         positions.push({
34137             x : maxX - this.unitWidth * 2 - this.gutter,
34138             y : minY
34139         });
34140         
34141         positions.push({
34142             x : maxX - this.unitWidth,
34143             y : minY + (this.unitWidth + this.gutter) * 2
34144         });
34145         
34146         positions.push({
34147             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34148             y : minY
34149         });
34150         
34151         Roo.each(eItems, function(b,k){
34152             
34153             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34154
34155         }, this);
34156         
34157     },
34158     
34159     getVerticalOneBoxColPositions : function(x, y, box)
34160     {
34161         var pos = [];
34162         
34163         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34164         
34165         if(box[0].size == 'md-left'){
34166             rand = 0;
34167         }
34168         
34169         if(box[0].size == 'md-right'){
34170             rand = 1;
34171         }
34172         
34173         pos.push({
34174             x : x + (this.unitWidth + this.gutter) * rand,
34175             y : y
34176         });
34177         
34178         return pos;
34179     },
34180     
34181     getVerticalTwoBoxColPositions : function(x, y, box)
34182     {
34183         var pos = [];
34184         
34185         if(box[0].size == 'xs'){
34186             
34187             pos.push({
34188                 x : x,
34189                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34190             });
34191
34192             pos.push({
34193                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34194                 y : y
34195             });
34196             
34197             return pos;
34198             
34199         }
34200         
34201         pos.push({
34202             x : x,
34203             y : y
34204         });
34205
34206         pos.push({
34207             x : x + (this.unitWidth + this.gutter) * 2,
34208             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34209         });
34210         
34211         return pos;
34212         
34213     },
34214     
34215     getVerticalThreeBoxColPositions : function(x, y, box)
34216     {
34217         var pos = [];
34218         
34219         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34220             
34221             pos.push({
34222                 x : x,
34223                 y : y
34224             });
34225
34226             pos.push({
34227                 x : x + (this.unitWidth + this.gutter) * 1,
34228                 y : y
34229             });
34230             
34231             pos.push({
34232                 x : x + (this.unitWidth + this.gutter) * 2,
34233                 y : y
34234             });
34235             
34236             return pos;
34237             
34238         }
34239         
34240         if(box[0].size == 'xs' && box[1].size == 'xs'){
34241             
34242             pos.push({
34243                 x : x,
34244                 y : y
34245             });
34246
34247             pos.push({
34248                 x : x,
34249                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34250             });
34251             
34252             pos.push({
34253                 x : x + (this.unitWidth + this.gutter) * 1,
34254                 y : y
34255             });
34256             
34257             return pos;
34258             
34259         }
34260         
34261         pos.push({
34262             x : x,
34263             y : y
34264         });
34265
34266         pos.push({
34267             x : x + (this.unitWidth + this.gutter) * 2,
34268             y : y
34269         });
34270
34271         pos.push({
34272             x : x + (this.unitWidth + this.gutter) * 2,
34273             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34274         });
34275             
34276         return pos;
34277         
34278     },
34279     
34280     getVerticalFourBoxColPositions : function(x, y, box)
34281     {
34282         var pos = [];
34283         
34284         if(box[0].size == 'xs'){
34285             
34286             pos.push({
34287                 x : x,
34288                 y : y
34289             });
34290
34291             pos.push({
34292                 x : x,
34293                 y : y + (this.unitHeight + this.gutter) * 1
34294             });
34295             
34296             pos.push({
34297                 x : x,
34298                 y : y + (this.unitHeight + this.gutter) * 2
34299             });
34300             
34301             pos.push({
34302                 x : x + (this.unitWidth + this.gutter) * 1,
34303                 y : y
34304             });
34305             
34306             return pos;
34307             
34308         }
34309         
34310         pos.push({
34311             x : x,
34312             y : y
34313         });
34314
34315         pos.push({
34316             x : x + (this.unitWidth + this.gutter) * 2,
34317             y : y
34318         });
34319
34320         pos.push({
34321             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34322             y : y + (this.unitHeight + this.gutter) * 1
34323         });
34324
34325         pos.push({
34326             x : x + (this.unitWidth + this.gutter) * 2,
34327             y : y + (this.unitWidth + this.gutter) * 2
34328         });
34329
34330         return pos;
34331         
34332     },
34333     
34334     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34335     {
34336         var pos = [];
34337         
34338         if(box[0].size == 'md-left'){
34339             pos.push({
34340                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34341                 y : minY
34342             });
34343             
34344             return pos;
34345         }
34346         
34347         if(box[0].size == 'md-right'){
34348             pos.push({
34349                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34350                 y : minY + (this.unitWidth + this.gutter) * 1
34351             });
34352             
34353             return pos;
34354         }
34355         
34356         var rand = Math.floor(Math.random() * (4 - box[0].y));
34357         
34358         pos.push({
34359             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34360             y : minY + (this.unitWidth + this.gutter) * rand
34361         });
34362         
34363         return pos;
34364         
34365     },
34366     
34367     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34368     {
34369         var pos = [];
34370         
34371         if(box[0].size == 'xs'){
34372             
34373             pos.push({
34374                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34375                 y : minY
34376             });
34377
34378             pos.push({
34379                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34380                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34381             });
34382             
34383             return pos;
34384             
34385         }
34386         
34387         pos.push({
34388             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34389             y : minY
34390         });
34391
34392         pos.push({
34393             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34394             y : minY + (this.unitWidth + this.gutter) * 2
34395         });
34396         
34397         return pos;
34398         
34399     },
34400     
34401     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34402     {
34403         var pos = [];
34404         
34405         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34406             
34407             pos.push({
34408                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34409                 y : minY
34410             });
34411
34412             pos.push({
34413                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34414                 y : minY + (this.unitWidth + this.gutter) * 1
34415             });
34416             
34417             pos.push({
34418                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34419                 y : minY + (this.unitWidth + this.gutter) * 2
34420             });
34421             
34422             return pos;
34423             
34424         }
34425         
34426         if(box[0].size == 'xs' && box[1].size == 'xs'){
34427             
34428             pos.push({
34429                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34430                 y : minY
34431             });
34432
34433             pos.push({
34434                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34435                 y : minY
34436             });
34437             
34438             pos.push({
34439                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34440                 y : minY + (this.unitWidth + this.gutter) * 1
34441             });
34442             
34443             return pos;
34444             
34445         }
34446         
34447         pos.push({
34448             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34449             y : minY
34450         });
34451
34452         pos.push({
34453             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34454             y : minY + (this.unitWidth + this.gutter) * 2
34455         });
34456
34457         pos.push({
34458             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34459             y : minY + (this.unitWidth + this.gutter) * 2
34460         });
34461             
34462         return pos;
34463         
34464     },
34465     
34466     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34467     {
34468         var pos = [];
34469         
34470         if(box[0].size == 'xs'){
34471             
34472             pos.push({
34473                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34474                 y : minY
34475             });
34476
34477             pos.push({
34478                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34479                 y : minY
34480             });
34481             
34482             pos.push({
34483                 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),
34484                 y : minY
34485             });
34486             
34487             pos.push({
34488                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34489                 y : minY + (this.unitWidth + this.gutter) * 1
34490             });
34491             
34492             return pos;
34493             
34494         }
34495         
34496         pos.push({
34497             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34498             y : minY
34499         });
34500         
34501         pos.push({
34502             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34503             y : minY + (this.unitWidth + this.gutter) * 2
34504         });
34505         
34506         pos.push({
34507             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34508             y : minY + (this.unitWidth + this.gutter) * 2
34509         });
34510         
34511         pos.push({
34512             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),
34513             y : minY + (this.unitWidth + this.gutter) * 2
34514         });
34515
34516         return pos;
34517         
34518     },
34519     
34520     /**
34521     * remove a Masonry Brick
34522     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34523     */
34524     removeBrick : function(brick_id)
34525     {
34526         if (!brick_id) {
34527             return;
34528         }
34529         
34530         for (var i = 0; i<this.bricks.length; i++) {
34531             if (this.bricks[i].id == brick_id) {
34532                 this.bricks.splice(i,1);
34533                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34534                 this.initial();
34535             }
34536         }
34537     },
34538     
34539     /**
34540     * adds a Masonry Brick
34541     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34542     */
34543     addBrick : function(cfg)
34544     {
34545         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34546         //this.register(cn);
34547         cn.parentId = this.id;
34548         cn.render(this.el);
34549         return cn;
34550     },
34551     
34552     /**
34553     * register a Masonry Brick
34554     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34555     */
34556     
34557     register : function(brick)
34558     {
34559         this.bricks.push(brick);
34560         brick.masonryId = this.id;
34561     },
34562     
34563     /**
34564     * clear all the Masonry Brick
34565     */
34566     clearAll : function()
34567     {
34568         this.bricks = [];
34569         //this.getChildContainer().dom.innerHTML = "";
34570         this.el.dom.innerHTML = '';
34571     },
34572     
34573     getSelected : function()
34574     {
34575         if (!this.selectedBrick) {
34576             return false;
34577         }
34578         
34579         return this.selectedBrick;
34580     }
34581 });
34582
34583 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34584     
34585     groups: {},
34586      /**
34587     * register a Masonry Layout
34588     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34589     */
34590     
34591     register : function(layout)
34592     {
34593         this.groups[layout.id] = layout;
34594     },
34595     /**
34596     * fetch a  Masonry Layout based on the masonry layout ID
34597     * @param {string} the masonry layout to add
34598     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34599     */
34600     
34601     get: function(layout_id) {
34602         if (typeof(this.groups[layout_id]) == 'undefined') {
34603             return false;
34604         }
34605         return this.groups[layout_id] ;
34606     }
34607     
34608     
34609     
34610 });
34611
34612  
34613
34614  /**
34615  *
34616  * This is based on 
34617  * http://masonry.desandro.com
34618  *
34619  * The idea is to render all the bricks based on vertical width...
34620  *
34621  * The original code extends 'outlayer' - we might need to use that....
34622  * 
34623  */
34624
34625
34626 /**
34627  * @class Roo.bootstrap.LayoutMasonryAuto
34628  * @extends Roo.bootstrap.Component
34629  * Bootstrap Layout Masonry class
34630  * 
34631  * @constructor
34632  * Create a new Element
34633  * @param {Object} config The config object
34634  */
34635
34636 Roo.bootstrap.LayoutMasonryAuto = function(config){
34637     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34638 };
34639
34640 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34641     
34642       /**
34643      * @cfg {Boolean} isFitWidth  - resize the width..
34644      */   
34645     isFitWidth : false,  // options..
34646     /**
34647      * @cfg {Boolean} isOriginLeft = left align?
34648      */   
34649     isOriginLeft : true,
34650     /**
34651      * @cfg {Boolean} isOriginTop = top align?
34652      */   
34653     isOriginTop : false,
34654     /**
34655      * @cfg {Boolean} isLayoutInstant = no animation?
34656      */   
34657     isLayoutInstant : false, // needed?
34658     /**
34659      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34660      */   
34661     isResizingContainer : true,
34662     /**
34663      * @cfg {Number} columnWidth  width of the columns 
34664      */   
34665     
34666     columnWidth : 0,
34667     
34668     /**
34669      * @cfg {Number} maxCols maximum number of columns
34670      */   
34671     
34672     maxCols: 0,
34673     /**
34674      * @cfg {Number} padHeight padding below box..
34675      */   
34676     
34677     padHeight : 10, 
34678     
34679     /**
34680      * @cfg {Boolean} isAutoInitial defalut true
34681      */   
34682     
34683     isAutoInitial : true, 
34684     
34685     // private?
34686     gutter : 0,
34687     
34688     containerWidth: 0,
34689     initialColumnWidth : 0,
34690     currentSize : null,
34691     
34692     colYs : null, // array.
34693     maxY : 0,
34694     padWidth: 10,
34695     
34696     
34697     tag: 'div',
34698     cls: '',
34699     bricks: null, //CompositeElement
34700     cols : 0, // array?
34701     // element : null, // wrapped now this.el
34702     _isLayoutInited : null, 
34703     
34704     
34705     getAutoCreate : function(){
34706         
34707         var cfg = {
34708             tag: this.tag,
34709             cls: 'blog-masonary-wrapper ' + this.cls,
34710             cn : {
34711                 cls : 'mas-boxes masonary'
34712             }
34713         };
34714         
34715         return cfg;
34716     },
34717     
34718     getChildContainer: function( )
34719     {
34720         if (this.boxesEl) {
34721             return this.boxesEl;
34722         }
34723         
34724         this.boxesEl = this.el.select('.mas-boxes').first();
34725         
34726         return this.boxesEl;
34727     },
34728     
34729     
34730     initEvents : function()
34731     {
34732         var _this = this;
34733         
34734         if(this.isAutoInitial){
34735             Roo.log('hook children rendered');
34736             this.on('childrenrendered', function() {
34737                 Roo.log('children rendered');
34738                 _this.initial();
34739             } ,this);
34740         }
34741         
34742     },
34743     
34744     initial : function()
34745     {
34746         this.reloadItems();
34747
34748         this.currentSize = this.el.getBox(true);
34749
34750         /// was window resize... - let's see if this works..
34751         Roo.EventManager.onWindowResize(this.resize, this); 
34752
34753         if(!this.isAutoInitial){
34754             this.layout();
34755             return;
34756         }
34757         
34758         this.layout.defer(500,this);
34759     },
34760     
34761     reloadItems: function()
34762     {
34763         this.bricks = this.el.select('.masonry-brick', true);
34764         
34765         this.bricks.each(function(b) {
34766             //Roo.log(b.getSize());
34767             if (!b.attr('originalwidth')) {
34768                 b.attr('originalwidth',  b.getSize().width);
34769             }
34770             
34771         });
34772         
34773         Roo.log(this.bricks.elements.length);
34774     },
34775     
34776     resize : function()
34777     {
34778         Roo.log('resize');
34779         var cs = this.el.getBox(true);
34780         
34781         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34782             Roo.log("no change in with or X");
34783             return;
34784         }
34785         this.currentSize = cs;
34786         this.layout();
34787     },
34788     
34789     layout : function()
34790     {
34791          Roo.log('layout');
34792         this._resetLayout();
34793         //this._manageStamps();
34794       
34795         // don't animate first layout
34796         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34797         this.layoutItems( isInstant );
34798       
34799         // flag for initalized
34800         this._isLayoutInited = true;
34801     },
34802     
34803     layoutItems : function( isInstant )
34804     {
34805         //var items = this._getItemsForLayout( this.items );
34806         // original code supports filtering layout items.. we just ignore it..
34807         
34808         this._layoutItems( this.bricks , isInstant );
34809       
34810         this._postLayout();
34811     },
34812     _layoutItems : function ( items , isInstant)
34813     {
34814        //this.fireEvent( 'layout', this, items );
34815     
34816
34817         if ( !items || !items.elements.length ) {
34818           // no items, emit event with empty array
34819             return;
34820         }
34821
34822         var queue = [];
34823         items.each(function(item) {
34824             Roo.log("layout item");
34825             Roo.log(item);
34826             // get x/y object from method
34827             var position = this._getItemLayoutPosition( item );
34828             // enqueue
34829             position.item = item;
34830             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34831             queue.push( position );
34832         }, this);
34833       
34834         this._processLayoutQueue( queue );
34835     },
34836     /** Sets position of item in DOM
34837     * @param {Element} item
34838     * @param {Number} x - horizontal position
34839     * @param {Number} y - vertical position
34840     * @param {Boolean} isInstant - disables transitions
34841     */
34842     _processLayoutQueue : function( queue )
34843     {
34844         for ( var i=0, len = queue.length; i < len; i++ ) {
34845             var obj = queue[i];
34846             obj.item.position('absolute');
34847             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34848         }
34849     },
34850       
34851     
34852     /**
34853     * Any logic you want to do after each layout,
34854     * i.e. size the container
34855     */
34856     _postLayout : function()
34857     {
34858         this.resizeContainer();
34859     },
34860     
34861     resizeContainer : function()
34862     {
34863         if ( !this.isResizingContainer ) {
34864             return;
34865         }
34866         var size = this._getContainerSize();
34867         if ( size ) {
34868             this.el.setSize(size.width,size.height);
34869             this.boxesEl.setSize(size.width,size.height);
34870         }
34871     },
34872     
34873     
34874     
34875     _resetLayout : function()
34876     {
34877         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34878         this.colWidth = this.el.getWidth();
34879         //this.gutter = this.el.getWidth(); 
34880         
34881         this.measureColumns();
34882
34883         // reset column Y
34884         var i = this.cols;
34885         this.colYs = [];
34886         while (i--) {
34887             this.colYs.push( 0 );
34888         }
34889     
34890         this.maxY = 0;
34891     },
34892
34893     measureColumns : function()
34894     {
34895         this.getContainerWidth();
34896       // if columnWidth is 0, default to outerWidth of first item
34897         if ( !this.columnWidth ) {
34898             var firstItem = this.bricks.first();
34899             Roo.log(firstItem);
34900             this.columnWidth  = this.containerWidth;
34901             if (firstItem && firstItem.attr('originalwidth') ) {
34902                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34903             }
34904             // columnWidth fall back to item of first element
34905             Roo.log("set column width?");
34906                         this.initialColumnWidth = this.columnWidth  ;
34907
34908             // if first elem has no width, default to size of container
34909             
34910         }
34911         
34912         
34913         if (this.initialColumnWidth) {
34914             this.columnWidth = this.initialColumnWidth;
34915         }
34916         
34917         
34918             
34919         // column width is fixed at the top - however if container width get's smaller we should
34920         // reduce it...
34921         
34922         // this bit calcs how man columns..
34923             
34924         var columnWidth = this.columnWidth += this.gutter;
34925       
34926         // calculate columns
34927         var containerWidth = this.containerWidth + this.gutter;
34928         
34929         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34930         // fix rounding errors, typically with gutters
34931         var excess = columnWidth - containerWidth % columnWidth;
34932         
34933         
34934         // if overshoot is less than a pixel, round up, otherwise floor it
34935         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34936         cols = Math[ mathMethod ]( cols );
34937         this.cols = Math.max( cols, 1 );
34938         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34939         
34940          // padding positioning..
34941         var totalColWidth = this.cols * this.columnWidth;
34942         var padavail = this.containerWidth - totalColWidth;
34943         // so for 2 columns - we need 3 'pads'
34944         
34945         var padNeeded = (1+this.cols) * this.padWidth;
34946         
34947         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34948         
34949         this.columnWidth += padExtra
34950         //this.padWidth = Math.floor(padavail /  ( this.cols));
34951         
34952         // adjust colum width so that padding is fixed??
34953         
34954         // we have 3 columns ... total = width * 3
34955         // we have X left over... that should be used by 
34956         
34957         //if (this.expandC) {
34958             
34959         //}
34960         
34961         
34962         
34963     },
34964     
34965     getContainerWidth : function()
34966     {
34967        /* // container is parent if fit width
34968         var container = this.isFitWidth ? this.element.parentNode : this.element;
34969         // check that this.size and size are there
34970         // IE8 triggers resize on body size change, so they might not be
34971         
34972         var size = getSize( container );  //FIXME
34973         this.containerWidth = size && size.innerWidth; //FIXME
34974         */
34975          
34976         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34977         
34978     },
34979     
34980     _getItemLayoutPosition : function( item )  // what is item?
34981     {
34982         // we resize the item to our columnWidth..
34983       
34984         item.setWidth(this.columnWidth);
34985         item.autoBoxAdjust  = false;
34986         
34987         var sz = item.getSize();
34988  
34989         // how many columns does this brick span
34990         var remainder = this.containerWidth % this.columnWidth;
34991         
34992         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34993         // round if off by 1 pixel, otherwise use ceil
34994         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34995         colSpan = Math.min( colSpan, this.cols );
34996         
34997         // normally this should be '1' as we dont' currently allow multi width columns..
34998         
34999         var colGroup = this._getColGroup( colSpan );
35000         // get the minimum Y value from the columns
35001         var minimumY = Math.min.apply( Math, colGroup );
35002         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35003         
35004         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35005          
35006         // position the brick
35007         var position = {
35008             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35009             y: this.currentSize.y + minimumY + this.padHeight
35010         };
35011         
35012         Roo.log(position);
35013         // apply setHeight to necessary columns
35014         var setHeight = minimumY + sz.height + this.padHeight;
35015         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35016         
35017         var setSpan = this.cols + 1 - colGroup.length;
35018         for ( var i = 0; i < setSpan; i++ ) {
35019           this.colYs[ shortColIndex + i ] = setHeight ;
35020         }
35021       
35022         return position;
35023     },
35024     
35025     /**
35026      * @param {Number} colSpan - number of columns the element spans
35027      * @returns {Array} colGroup
35028      */
35029     _getColGroup : function( colSpan )
35030     {
35031         if ( colSpan < 2 ) {
35032           // if brick spans only one column, use all the column Ys
35033           return this.colYs;
35034         }
35035       
35036         var colGroup = [];
35037         // how many different places could this brick fit horizontally
35038         var groupCount = this.cols + 1 - colSpan;
35039         // for each group potential horizontal position
35040         for ( var i = 0; i < groupCount; i++ ) {
35041           // make an array of colY values for that one group
35042           var groupColYs = this.colYs.slice( i, i + colSpan );
35043           // and get the max value of the array
35044           colGroup[i] = Math.max.apply( Math, groupColYs );
35045         }
35046         return colGroup;
35047     },
35048     /*
35049     _manageStamp : function( stamp )
35050     {
35051         var stampSize =  stamp.getSize();
35052         var offset = stamp.getBox();
35053         // get the columns that this stamp affects
35054         var firstX = this.isOriginLeft ? offset.x : offset.right;
35055         var lastX = firstX + stampSize.width;
35056         var firstCol = Math.floor( firstX / this.columnWidth );
35057         firstCol = Math.max( 0, firstCol );
35058         
35059         var lastCol = Math.floor( lastX / this.columnWidth );
35060         // lastCol should not go over if multiple of columnWidth #425
35061         lastCol -= lastX % this.columnWidth ? 0 : 1;
35062         lastCol = Math.min( this.cols - 1, lastCol );
35063         
35064         // set colYs to bottom of the stamp
35065         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35066             stampSize.height;
35067             
35068         for ( var i = firstCol; i <= lastCol; i++ ) {
35069           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35070         }
35071     },
35072     */
35073     
35074     _getContainerSize : function()
35075     {
35076         this.maxY = Math.max.apply( Math, this.colYs );
35077         var size = {
35078             height: this.maxY
35079         };
35080       
35081         if ( this.isFitWidth ) {
35082             size.width = this._getContainerFitWidth();
35083         }
35084       
35085         return size;
35086     },
35087     
35088     _getContainerFitWidth : function()
35089     {
35090         var unusedCols = 0;
35091         // count unused columns
35092         var i = this.cols;
35093         while ( --i ) {
35094           if ( this.colYs[i] !== 0 ) {
35095             break;
35096           }
35097           unusedCols++;
35098         }
35099         // fit container to columns that have been used
35100         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35101     },
35102     
35103     needsResizeLayout : function()
35104     {
35105         var previousWidth = this.containerWidth;
35106         this.getContainerWidth();
35107         return previousWidth !== this.containerWidth;
35108     }
35109  
35110 });
35111
35112  
35113
35114  /*
35115  * - LGPL
35116  *
35117  * element
35118  * 
35119  */
35120
35121 /**
35122  * @class Roo.bootstrap.MasonryBrick
35123  * @extends Roo.bootstrap.Component
35124  * Bootstrap MasonryBrick class
35125  * 
35126  * @constructor
35127  * Create a new MasonryBrick
35128  * @param {Object} config The config object
35129  */
35130
35131 Roo.bootstrap.MasonryBrick = function(config){
35132     
35133     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35134     
35135     Roo.bootstrap.MasonryBrick.register(this);
35136     
35137     this.addEvents({
35138         // raw events
35139         /**
35140          * @event click
35141          * When a MasonryBrick is clcik
35142          * @param {Roo.bootstrap.MasonryBrick} this
35143          * @param {Roo.EventObject} e
35144          */
35145         "click" : true
35146     });
35147 };
35148
35149 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35150     
35151     /**
35152      * @cfg {String} title
35153      */   
35154     title : '',
35155     /**
35156      * @cfg {String} html
35157      */   
35158     html : '',
35159     /**
35160      * @cfg {String} bgimage
35161      */   
35162     bgimage : '',
35163     /**
35164      * @cfg {String} videourl
35165      */   
35166     videourl : '',
35167     /**
35168      * @cfg {String} cls
35169      */   
35170     cls : '',
35171     /**
35172      * @cfg {String} href
35173      */   
35174     href : '',
35175     /**
35176      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35177      */   
35178     size : 'xs',
35179     
35180     /**
35181      * @cfg {String} placetitle (center|bottom)
35182      */   
35183     placetitle : '',
35184     
35185     /**
35186      * @cfg {Boolean} isFitContainer defalut true
35187      */   
35188     isFitContainer : true, 
35189     
35190     /**
35191      * @cfg {Boolean} preventDefault defalut false
35192      */   
35193     preventDefault : false, 
35194     
35195     /**
35196      * @cfg {Boolean} inverse defalut false
35197      */   
35198     maskInverse : false, 
35199     
35200     getAutoCreate : function()
35201     {
35202         if(!this.isFitContainer){
35203             return this.getSplitAutoCreate();
35204         }
35205         
35206         var cls = 'masonry-brick masonry-brick-full';
35207         
35208         if(this.href.length){
35209             cls += ' masonry-brick-link';
35210         }
35211         
35212         if(this.bgimage.length){
35213             cls += ' masonry-brick-image';
35214         }
35215         
35216         if(this.maskInverse){
35217             cls += ' mask-inverse';
35218         }
35219         
35220         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35221             cls += ' enable-mask';
35222         }
35223         
35224         if(this.size){
35225             cls += ' masonry-' + this.size + '-brick';
35226         }
35227         
35228         if(this.placetitle.length){
35229             
35230             switch (this.placetitle) {
35231                 case 'center' :
35232                     cls += ' masonry-center-title';
35233                     break;
35234                 case 'bottom' :
35235                     cls += ' masonry-bottom-title';
35236                     break;
35237                 default:
35238                     break;
35239             }
35240             
35241         } else {
35242             if(!this.html.length && !this.bgimage.length){
35243                 cls += ' masonry-center-title';
35244             }
35245
35246             if(!this.html.length && this.bgimage.length){
35247                 cls += ' masonry-bottom-title';
35248             }
35249         }
35250         
35251         if(this.cls){
35252             cls += ' ' + this.cls;
35253         }
35254         
35255         var cfg = {
35256             tag: (this.href.length) ? 'a' : 'div',
35257             cls: cls,
35258             cn: [
35259                 {
35260                     tag: 'div',
35261                     cls: 'masonry-brick-mask'
35262                 },
35263                 {
35264                     tag: 'div',
35265                     cls: 'masonry-brick-paragraph',
35266                     cn: []
35267                 }
35268             ]
35269         };
35270         
35271         if(this.href.length){
35272             cfg.href = this.href;
35273         }
35274         
35275         var cn = cfg.cn[1].cn;
35276         
35277         if(this.title.length){
35278             cn.push({
35279                 tag: 'h4',
35280                 cls: 'masonry-brick-title',
35281                 html: this.title
35282             });
35283         }
35284         
35285         if(this.html.length){
35286             cn.push({
35287                 tag: 'p',
35288                 cls: 'masonry-brick-text',
35289                 html: this.html
35290             });
35291         }
35292         
35293         if (!this.title.length && !this.html.length) {
35294             cfg.cn[1].cls += ' hide';
35295         }
35296         
35297         if(this.bgimage.length){
35298             cfg.cn.push({
35299                 tag: 'img',
35300                 cls: 'masonry-brick-image-view',
35301                 src: this.bgimage
35302             });
35303         }
35304         
35305         if(this.videourl.length){
35306             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35307             // youtube support only?
35308             cfg.cn.push({
35309                 tag: 'iframe',
35310                 cls: 'masonry-brick-image-view',
35311                 src: vurl,
35312                 frameborder : 0,
35313                 allowfullscreen : true
35314             });
35315         }
35316         
35317         return cfg;
35318         
35319     },
35320     
35321     getSplitAutoCreate : function()
35322     {
35323         var cls = 'masonry-brick masonry-brick-split';
35324         
35325         if(this.href.length){
35326             cls += ' masonry-brick-link';
35327         }
35328         
35329         if(this.bgimage.length){
35330             cls += ' masonry-brick-image';
35331         }
35332         
35333         if(this.size){
35334             cls += ' masonry-' + this.size + '-brick';
35335         }
35336         
35337         switch (this.placetitle) {
35338             case 'center' :
35339                 cls += ' masonry-center-title';
35340                 break;
35341             case 'bottom' :
35342                 cls += ' masonry-bottom-title';
35343                 break;
35344             default:
35345                 if(!this.bgimage.length){
35346                     cls += ' masonry-center-title';
35347                 }
35348
35349                 if(this.bgimage.length){
35350                     cls += ' masonry-bottom-title';
35351                 }
35352                 break;
35353         }
35354         
35355         if(this.cls){
35356             cls += ' ' + this.cls;
35357         }
35358         
35359         var cfg = {
35360             tag: (this.href.length) ? 'a' : 'div',
35361             cls: cls,
35362             cn: [
35363                 {
35364                     tag: 'div',
35365                     cls: 'masonry-brick-split-head',
35366                     cn: [
35367                         {
35368                             tag: 'div',
35369                             cls: 'masonry-brick-paragraph',
35370                             cn: []
35371                         }
35372                     ]
35373                 },
35374                 {
35375                     tag: 'div',
35376                     cls: 'masonry-brick-split-body',
35377                     cn: []
35378                 }
35379             ]
35380         };
35381         
35382         if(this.href.length){
35383             cfg.href = this.href;
35384         }
35385         
35386         if(this.title.length){
35387             cfg.cn[0].cn[0].cn.push({
35388                 tag: 'h4',
35389                 cls: 'masonry-brick-title',
35390                 html: this.title
35391             });
35392         }
35393         
35394         if(this.html.length){
35395             cfg.cn[1].cn.push({
35396                 tag: 'p',
35397                 cls: 'masonry-brick-text',
35398                 html: this.html
35399             });
35400         }
35401
35402         if(this.bgimage.length){
35403             cfg.cn[0].cn.push({
35404                 tag: 'img',
35405                 cls: 'masonry-brick-image-view',
35406                 src: this.bgimage
35407             });
35408         }
35409         
35410         if(this.videourl.length){
35411             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35412             // youtube support only?
35413             cfg.cn[0].cn.cn.push({
35414                 tag: 'iframe',
35415                 cls: 'masonry-brick-image-view',
35416                 src: vurl,
35417                 frameborder : 0,
35418                 allowfullscreen : true
35419             });
35420         }
35421         
35422         return cfg;
35423     },
35424     
35425     initEvents: function() 
35426     {
35427         switch (this.size) {
35428             case 'xs' :
35429                 this.x = 1;
35430                 this.y = 1;
35431                 break;
35432             case 'sm' :
35433                 this.x = 2;
35434                 this.y = 2;
35435                 break;
35436             case 'md' :
35437             case 'md-left' :
35438             case 'md-right' :
35439                 this.x = 3;
35440                 this.y = 3;
35441                 break;
35442             case 'tall' :
35443                 this.x = 2;
35444                 this.y = 3;
35445                 break;
35446             case 'wide' :
35447                 this.x = 3;
35448                 this.y = 2;
35449                 break;
35450             case 'wide-thin' :
35451                 this.x = 3;
35452                 this.y = 1;
35453                 break;
35454                         
35455             default :
35456                 break;
35457         }
35458         
35459         if(Roo.isTouch){
35460             this.el.on('touchstart', this.onTouchStart, this);
35461             this.el.on('touchmove', this.onTouchMove, this);
35462             this.el.on('touchend', this.onTouchEnd, this);
35463             this.el.on('contextmenu', this.onContextMenu, this);
35464         } else {
35465             this.el.on('mouseenter'  ,this.enter, this);
35466             this.el.on('mouseleave', this.leave, this);
35467             this.el.on('click', this.onClick, this);
35468         }
35469         
35470         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35471             this.parent().bricks.push(this);   
35472         }
35473         
35474     },
35475     
35476     onClick: function(e, el)
35477     {
35478         var time = this.endTimer - this.startTimer;
35479         // Roo.log(e.preventDefault());
35480         if(Roo.isTouch){
35481             if(time > 1000){
35482                 e.preventDefault();
35483                 return;
35484             }
35485         }
35486         
35487         if(!this.preventDefault){
35488             return;
35489         }
35490         
35491         e.preventDefault();
35492         
35493         if (this.activeClass != '') {
35494             this.selectBrick();
35495         }
35496         
35497         this.fireEvent('click', this, e);
35498     },
35499     
35500     enter: function(e, el)
35501     {
35502         e.preventDefault();
35503         
35504         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35505             return;
35506         }
35507         
35508         if(this.bgimage.length && this.html.length){
35509             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35510         }
35511     },
35512     
35513     leave: function(e, el)
35514     {
35515         e.preventDefault();
35516         
35517         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35518             return;
35519         }
35520         
35521         if(this.bgimage.length && this.html.length){
35522             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35523         }
35524     },
35525     
35526     onTouchStart: function(e, el)
35527     {
35528 //        e.preventDefault();
35529         
35530         this.touchmoved = false;
35531         
35532         if(!this.isFitContainer){
35533             return;
35534         }
35535         
35536         if(!this.bgimage.length || !this.html.length){
35537             return;
35538         }
35539         
35540         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35541         
35542         this.timer = new Date().getTime();
35543         
35544     },
35545     
35546     onTouchMove: function(e, el)
35547     {
35548         this.touchmoved = true;
35549     },
35550     
35551     onContextMenu : function(e,el)
35552     {
35553         e.preventDefault();
35554         e.stopPropagation();
35555         return false;
35556     },
35557     
35558     onTouchEnd: function(e, el)
35559     {
35560 //        e.preventDefault();
35561         
35562         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35563         
35564             this.leave(e,el);
35565             
35566             return;
35567         }
35568         
35569         if(!this.bgimage.length || !this.html.length){
35570             
35571             if(this.href.length){
35572                 window.location.href = this.href;
35573             }
35574             
35575             return;
35576         }
35577         
35578         if(!this.isFitContainer){
35579             return;
35580         }
35581         
35582         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35583         
35584         window.location.href = this.href;
35585     },
35586     
35587     //selection on single brick only
35588     selectBrick : function() {
35589         
35590         if (!this.parentId) {
35591             return;
35592         }
35593         
35594         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35595         var index = m.selectedBrick.indexOf(this.id);
35596         
35597         if ( index > -1) {
35598             m.selectedBrick.splice(index,1);
35599             this.el.removeClass(this.activeClass);
35600             return;
35601         }
35602         
35603         for(var i = 0; i < m.selectedBrick.length; i++) {
35604             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35605             b.el.removeClass(b.activeClass);
35606         }
35607         
35608         m.selectedBrick = [];
35609         
35610         m.selectedBrick.push(this.id);
35611         this.el.addClass(this.activeClass);
35612         return;
35613     },
35614     
35615     isSelected : function(){
35616         return this.el.hasClass(this.activeClass);
35617         
35618     }
35619 });
35620
35621 Roo.apply(Roo.bootstrap.MasonryBrick, {
35622     
35623     //groups: {},
35624     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35625      /**
35626     * register a Masonry Brick
35627     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35628     */
35629     
35630     register : function(brick)
35631     {
35632         //this.groups[brick.id] = brick;
35633         this.groups.add(brick.id, brick);
35634     },
35635     /**
35636     * fetch a  masonry brick based on the masonry brick ID
35637     * @param {string} the masonry brick to add
35638     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35639     */
35640     
35641     get: function(brick_id) 
35642     {
35643         // if (typeof(this.groups[brick_id]) == 'undefined') {
35644         //     return false;
35645         // }
35646         // return this.groups[brick_id] ;
35647         
35648         if(this.groups.key(brick_id)) {
35649             return this.groups.key(brick_id);
35650         }
35651         
35652         return false;
35653     }
35654     
35655     
35656     
35657 });
35658
35659  /*
35660  * - LGPL
35661  *
35662  * element
35663  * 
35664  */
35665
35666 /**
35667  * @class Roo.bootstrap.Brick
35668  * @extends Roo.bootstrap.Component
35669  * Bootstrap Brick class
35670  * 
35671  * @constructor
35672  * Create a new Brick
35673  * @param {Object} config The config object
35674  */
35675
35676 Roo.bootstrap.Brick = function(config){
35677     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35678     
35679     this.addEvents({
35680         // raw events
35681         /**
35682          * @event click
35683          * When a Brick is click
35684          * @param {Roo.bootstrap.Brick} this
35685          * @param {Roo.EventObject} e
35686          */
35687         "click" : true
35688     });
35689 };
35690
35691 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35692     
35693     /**
35694      * @cfg {String} title
35695      */   
35696     title : '',
35697     /**
35698      * @cfg {String} html
35699      */   
35700     html : '',
35701     /**
35702      * @cfg {String} bgimage
35703      */   
35704     bgimage : '',
35705     /**
35706      * @cfg {String} cls
35707      */   
35708     cls : '',
35709     /**
35710      * @cfg {String} href
35711      */   
35712     href : '',
35713     /**
35714      * @cfg {String} video
35715      */   
35716     video : '',
35717     /**
35718      * @cfg {Boolean} square
35719      */   
35720     square : true,
35721     
35722     getAutoCreate : function()
35723     {
35724         var cls = 'roo-brick';
35725         
35726         if(this.href.length){
35727             cls += ' roo-brick-link';
35728         }
35729         
35730         if(this.bgimage.length){
35731             cls += ' roo-brick-image';
35732         }
35733         
35734         if(!this.html.length && !this.bgimage.length){
35735             cls += ' roo-brick-center-title';
35736         }
35737         
35738         if(!this.html.length && this.bgimage.length){
35739             cls += ' roo-brick-bottom-title';
35740         }
35741         
35742         if(this.cls){
35743             cls += ' ' + this.cls;
35744         }
35745         
35746         var cfg = {
35747             tag: (this.href.length) ? 'a' : 'div',
35748             cls: cls,
35749             cn: [
35750                 {
35751                     tag: 'div',
35752                     cls: 'roo-brick-paragraph',
35753                     cn: []
35754                 }
35755             ]
35756         };
35757         
35758         if(this.href.length){
35759             cfg.href = this.href;
35760         }
35761         
35762         var cn = cfg.cn[0].cn;
35763         
35764         if(this.title.length){
35765             cn.push({
35766                 tag: 'h4',
35767                 cls: 'roo-brick-title',
35768                 html: this.title
35769             });
35770         }
35771         
35772         if(this.html.length){
35773             cn.push({
35774                 tag: 'p',
35775                 cls: 'roo-brick-text',
35776                 html: this.html
35777             });
35778         } else {
35779             cn.cls += ' hide';
35780         }
35781         
35782         if(this.bgimage.length){
35783             cfg.cn.push({
35784                 tag: 'img',
35785                 cls: 'roo-brick-image-view',
35786                 src: this.bgimage
35787             });
35788         }
35789         
35790         return cfg;
35791     },
35792     
35793     initEvents: function() 
35794     {
35795         if(this.title.length || this.html.length){
35796             this.el.on('mouseenter'  ,this.enter, this);
35797             this.el.on('mouseleave', this.leave, this);
35798         }
35799         
35800         Roo.EventManager.onWindowResize(this.resize, this); 
35801         
35802         if(this.bgimage.length){
35803             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35804             this.imageEl.on('load', this.onImageLoad, this);
35805             return;
35806         }
35807         
35808         this.resize();
35809     },
35810     
35811     onImageLoad : function()
35812     {
35813         this.resize();
35814     },
35815     
35816     resize : function()
35817     {
35818         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35819         
35820         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35821         
35822         if(this.bgimage.length){
35823             var image = this.el.select('.roo-brick-image-view', true).first();
35824             
35825             image.setWidth(paragraph.getWidth());
35826             
35827             if(this.square){
35828                 image.setHeight(paragraph.getWidth());
35829             }
35830             
35831             this.el.setHeight(image.getHeight());
35832             paragraph.setHeight(image.getHeight());
35833             
35834         }
35835         
35836     },
35837     
35838     enter: function(e, el)
35839     {
35840         e.preventDefault();
35841         
35842         if(this.bgimage.length){
35843             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35844             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35845         }
35846     },
35847     
35848     leave: function(e, el)
35849     {
35850         e.preventDefault();
35851         
35852         if(this.bgimage.length){
35853             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35854             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35855         }
35856     }
35857     
35858 });
35859
35860  
35861
35862  /*
35863  * - LGPL
35864  *
35865  * Number field 
35866  */
35867
35868 /**
35869  * @class Roo.bootstrap.NumberField
35870  * @extends Roo.bootstrap.Input
35871  * Bootstrap NumberField class
35872  * 
35873  * 
35874  * 
35875  * 
35876  * @constructor
35877  * Create a new NumberField
35878  * @param {Object} config The config object
35879  */
35880
35881 Roo.bootstrap.NumberField = function(config){
35882     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35883 };
35884
35885 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35886     
35887     /**
35888      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35889      */
35890     allowDecimals : true,
35891     /**
35892      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35893      */
35894     decimalSeparator : ".",
35895     /**
35896      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35897      */
35898     decimalPrecision : 2,
35899     /**
35900      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35901      */
35902     allowNegative : true,
35903     
35904     /**
35905      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35906      */
35907     allowZero: true,
35908     /**
35909      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35910      */
35911     minValue : Number.NEGATIVE_INFINITY,
35912     /**
35913      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35914      */
35915     maxValue : Number.MAX_VALUE,
35916     /**
35917      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35918      */
35919     minText : "The minimum value for this field is {0}",
35920     /**
35921      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35922      */
35923     maxText : "The maximum value for this field is {0}",
35924     /**
35925      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35926      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35927      */
35928     nanText : "{0} is not a valid number",
35929     /**
35930      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35931      */
35932     thousandsDelimiter : false,
35933     /**
35934      * @cfg {String} valueAlign alignment of value
35935      */
35936     valueAlign : "left",
35937
35938     getAutoCreate : function()
35939     {
35940         var hiddenInput = {
35941             tag: 'input',
35942             type: 'hidden',
35943             id: Roo.id(),
35944             cls: 'hidden-number-input'
35945         };
35946         
35947         if (this.name) {
35948             hiddenInput.name = this.name;
35949         }
35950         
35951         this.name = '';
35952         
35953         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35954         
35955         this.name = hiddenInput.name;
35956         
35957         if(cfg.cn.length > 0) {
35958             cfg.cn.push(hiddenInput);
35959         }
35960         
35961         return cfg;
35962     },
35963
35964     // private
35965     initEvents : function()
35966     {   
35967         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35968         
35969         var allowed = "0123456789";
35970         
35971         if(this.allowDecimals){
35972             allowed += this.decimalSeparator;
35973         }
35974         
35975         if(this.allowNegative){
35976             allowed += "-";
35977         }
35978         
35979         if(this.thousandsDelimiter) {
35980             allowed += ",";
35981         }
35982         
35983         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35984         
35985         var keyPress = function(e){
35986             
35987             var k = e.getKey();
35988             
35989             var c = e.getCharCode();
35990             
35991             if(
35992                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35993                     allowed.indexOf(String.fromCharCode(c)) === -1
35994             ){
35995                 e.stopEvent();
35996                 return;
35997             }
35998             
35999             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36000                 return;
36001             }
36002             
36003             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36004                 e.stopEvent();
36005             }
36006         };
36007         
36008         this.el.on("keypress", keyPress, this);
36009     },
36010     
36011     validateValue : function(value)
36012     {
36013         
36014         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36015             return false;
36016         }
36017         
36018         var num = this.parseValue(value);
36019         
36020         if(isNaN(num)){
36021             this.markInvalid(String.format(this.nanText, value));
36022             return false;
36023         }
36024         
36025         if(num < this.minValue){
36026             this.markInvalid(String.format(this.minText, this.minValue));
36027             return false;
36028         }
36029         
36030         if(num > this.maxValue){
36031             this.markInvalid(String.format(this.maxText, this.maxValue));
36032             return false;
36033         }
36034         
36035         return true;
36036     },
36037
36038     getValue : function()
36039     {
36040         var v = this.hiddenEl().getValue();
36041         
36042         return this.fixPrecision(this.parseValue(v));
36043     },
36044
36045     parseValue : function(value)
36046     {
36047         if(this.thousandsDelimiter) {
36048             value += "";
36049             r = new RegExp(",", "g");
36050             value = value.replace(r, "");
36051         }
36052         
36053         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36054         return isNaN(value) ? '' : value;
36055     },
36056
36057     fixPrecision : function(value)
36058     {
36059         if(this.thousandsDelimiter) {
36060             value += "";
36061             r = new RegExp(",", "g");
36062             value = value.replace(r, "");
36063         }
36064         
36065         var nan = isNaN(value);
36066         
36067         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36068             return nan ? '' : value;
36069         }
36070         return parseFloat(value).toFixed(this.decimalPrecision);
36071     },
36072
36073     setValue : function(v)
36074     {
36075         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36076         
36077         this.value = v;
36078         
36079         if(this.rendered){
36080             
36081             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36082             
36083             this.inputEl().dom.value = (v == '') ? '' :
36084                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36085             
36086             if(!this.allowZero && v === '0') {
36087                 this.hiddenEl().dom.value = '';
36088                 this.inputEl().dom.value = '';
36089             }
36090             
36091             this.validate();
36092         }
36093     },
36094
36095     decimalPrecisionFcn : function(v)
36096     {
36097         return Math.floor(v);
36098     },
36099
36100     beforeBlur : function()
36101     {
36102         var v = this.parseValue(this.getRawValue());
36103         
36104         if(v || v === 0 || v === ''){
36105             this.setValue(v);
36106         }
36107     },
36108     
36109     hiddenEl : function()
36110     {
36111         return this.el.select('input.hidden-number-input',true).first();
36112     }
36113     
36114 });
36115
36116  
36117
36118 /*
36119 * Licence: LGPL
36120 */
36121
36122 /**
36123  * @class Roo.bootstrap.DocumentSlider
36124  * @extends Roo.bootstrap.Component
36125  * Bootstrap DocumentSlider class
36126  * 
36127  * @constructor
36128  * Create a new DocumentViewer
36129  * @param {Object} config The config object
36130  */
36131
36132 Roo.bootstrap.DocumentSlider = function(config){
36133     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36134     
36135     this.files = [];
36136     
36137     this.addEvents({
36138         /**
36139          * @event initial
36140          * Fire after initEvent
36141          * @param {Roo.bootstrap.DocumentSlider} this
36142          */
36143         "initial" : true,
36144         /**
36145          * @event update
36146          * Fire after update
36147          * @param {Roo.bootstrap.DocumentSlider} this
36148          */
36149         "update" : true,
36150         /**
36151          * @event click
36152          * Fire after click
36153          * @param {Roo.bootstrap.DocumentSlider} this
36154          */
36155         "click" : true
36156     });
36157 };
36158
36159 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36160     
36161     files : false,
36162     
36163     indicator : 0,
36164     
36165     getAutoCreate : function()
36166     {
36167         var cfg = {
36168             tag : 'div',
36169             cls : 'roo-document-slider',
36170             cn : [
36171                 {
36172                     tag : 'div',
36173                     cls : 'roo-document-slider-header',
36174                     cn : [
36175                         {
36176                             tag : 'div',
36177                             cls : 'roo-document-slider-header-title'
36178                         }
36179                     ]
36180                 },
36181                 {
36182                     tag : 'div',
36183                     cls : 'roo-document-slider-body',
36184                     cn : [
36185                         {
36186                             tag : 'div',
36187                             cls : 'roo-document-slider-prev',
36188                             cn : [
36189                                 {
36190                                     tag : 'i',
36191                                     cls : 'fa fa-chevron-left'
36192                                 }
36193                             ]
36194                         },
36195                         {
36196                             tag : 'div',
36197                             cls : 'roo-document-slider-thumb',
36198                             cn : [
36199                                 {
36200                                     tag : 'img',
36201                                     cls : 'roo-document-slider-image'
36202                                 }
36203                             ]
36204                         },
36205                         {
36206                             tag : 'div',
36207                             cls : 'roo-document-slider-next',
36208                             cn : [
36209                                 {
36210                                     tag : 'i',
36211                                     cls : 'fa fa-chevron-right'
36212                                 }
36213                             ]
36214                         }
36215                     ]
36216                 }
36217             ]
36218         };
36219         
36220         return cfg;
36221     },
36222     
36223     initEvents : function()
36224     {
36225         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36226         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36227         
36228         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36229         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36230         
36231         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36232         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36233         
36234         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36235         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36236         
36237         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36238         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36239         
36240         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36241         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36242         
36243         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36244         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36245         
36246         this.thumbEl.on('click', this.onClick, this);
36247         
36248         this.prevIndicator.on('click', this.prev, this);
36249         
36250         this.nextIndicator.on('click', this.next, this);
36251         
36252     },
36253     
36254     initial : function()
36255     {
36256         if(this.files.length){
36257             this.indicator = 1;
36258             this.update()
36259         }
36260         
36261         this.fireEvent('initial', this);
36262     },
36263     
36264     update : function()
36265     {
36266         this.imageEl.attr('src', this.files[this.indicator - 1]);
36267         
36268         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36269         
36270         this.prevIndicator.show();
36271         
36272         if(this.indicator == 1){
36273             this.prevIndicator.hide();
36274         }
36275         
36276         this.nextIndicator.show();
36277         
36278         if(this.indicator == this.files.length){
36279             this.nextIndicator.hide();
36280         }
36281         
36282         this.thumbEl.scrollTo('top');
36283         
36284         this.fireEvent('update', this);
36285     },
36286     
36287     onClick : function(e)
36288     {
36289         e.preventDefault();
36290         
36291         this.fireEvent('click', this);
36292     },
36293     
36294     prev : function(e)
36295     {
36296         e.preventDefault();
36297         
36298         this.indicator = Math.max(1, this.indicator - 1);
36299         
36300         this.update();
36301     },
36302     
36303     next : function(e)
36304     {
36305         e.preventDefault();
36306         
36307         this.indicator = Math.min(this.files.length, this.indicator + 1);
36308         
36309         this.update();
36310     }
36311 });
36312 /*
36313  * - LGPL
36314  *
36315  * RadioSet
36316  *
36317  *
36318  */
36319
36320 /**
36321  * @class Roo.bootstrap.RadioSet
36322  * @extends Roo.bootstrap.Input
36323  * Bootstrap RadioSet class
36324  * @cfg {String} indicatorpos (left|right) default left
36325  * @cfg {Boolean} inline (true|false) inline the element (default true)
36326  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36327  * @constructor
36328  * Create a new RadioSet
36329  * @param {Object} config The config object
36330  */
36331
36332 Roo.bootstrap.RadioSet = function(config){
36333     
36334     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36335     
36336     this.radioes = [];
36337     
36338     Roo.bootstrap.RadioSet.register(this);
36339     
36340     this.addEvents({
36341         /**
36342         * @event check
36343         * Fires when the element is checked or unchecked.
36344         * @param {Roo.bootstrap.RadioSet} this This radio
36345         * @param {Roo.bootstrap.Radio} item The checked item
36346         */
36347        check : true,
36348        /**
36349         * @event click
36350         * Fires when the element is click.
36351         * @param {Roo.bootstrap.RadioSet} this This radio set
36352         * @param {Roo.bootstrap.Radio} item The checked item
36353         * @param {Roo.EventObject} e The event object
36354         */
36355        click : true
36356     });
36357     
36358 };
36359
36360 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36361
36362     radioes : false,
36363     
36364     inline : true,
36365     
36366     weight : '',
36367     
36368     indicatorpos : 'left',
36369     
36370     getAutoCreate : function()
36371     {
36372         var label = {
36373             tag : 'label',
36374             cls : 'roo-radio-set-label',
36375             cn : [
36376                 {
36377                     tag : 'span',
36378                     html : this.fieldLabel
36379                 }
36380             ]
36381         };
36382         if (Roo.bootstrap.version == 3) {
36383             
36384             
36385             if(this.indicatorpos == 'left'){
36386                 label.cn.unshift({
36387                     tag : 'i',
36388                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36389                     tooltip : 'This field is required'
36390                 });
36391             } else {
36392                 label.cn.push({
36393                     tag : 'i',
36394                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36395                     tooltip : 'This field is required'
36396                 });
36397             }
36398         }
36399         var items = {
36400             tag : 'div',
36401             cls : 'roo-radio-set-items'
36402         };
36403         
36404         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36405         
36406         if (align === 'left' && this.fieldLabel.length) {
36407             
36408             items = {
36409                 cls : "roo-radio-set-right", 
36410                 cn: [
36411                     items
36412                 ]
36413             };
36414             
36415             if(this.labelWidth > 12){
36416                 label.style = "width: " + this.labelWidth + 'px';
36417             }
36418             
36419             if(this.labelWidth < 13 && this.labelmd == 0){
36420                 this.labelmd = this.labelWidth;
36421             }
36422             
36423             if(this.labellg > 0){
36424                 label.cls += ' col-lg-' + this.labellg;
36425                 items.cls += ' col-lg-' + (12 - this.labellg);
36426             }
36427             
36428             if(this.labelmd > 0){
36429                 label.cls += ' col-md-' + this.labelmd;
36430                 items.cls += ' col-md-' + (12 - this.labelmd);
36431             }
36432             
36433             if(this.labelsm > 0){
36434                 label.cls += ' col-sm-' + this.labelsm;
36435                 items.cls += ' col-sm-' + (12 - this.labelsm);
36436             }
36437             
36438             if(this.labelxs > 0){
36439                 label.cls += ' col-xs-' + this.labelxs;
36440                 items.cls += ' col-xs-' + (12 - this.labelxs);
36441             }
36442         }
36443         
36444         var cfg = {
36445             tag : 'div',
36446             cls : 'roo-radio-set',
36447             cn : [
36448                 {
36449                     tag : 'input',
36450                     cls : 'roo-radio-set-input',
36451                     type : 'hidden',
36452                     name : this.name,
36453                     value : this.value ? this.value :  ''
36454                 },
36455                 label,
36456                 items
36457             ]
36458         };
36459         
36460         if(this.weight.length){
36461             cfg.cls += ' roo-radio-' + this.weight;
36462         }
36463         
36464         if(this.inline) {
36465             cfg.cls += ' roo-radio-set-inline';
36466         }
36467         
36468         var settings=this;
36469         ['xs','sm','md','lg'].map(function(size){
36470             if (settings[size]) {
36471                 cfg.cls += ' col-' + size + '-' + settings[size];
36472             }
36473         });
36474         
36475         return cfg;
36476         
36477     },
36478
36479     initEvents : function()
36480     {
36481         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36482         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36483         
36484         if(!this.fieldLabel.length){
36485             this.labelEl.hide();
36486         }
36487         
36488         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36489         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36490         
36491         this.indicator = this.indicatorEl();
36492         
36493         if(this.indicator){
36494             this.indicator.addClass('invisible');
36495         }
36496         
36497         this.originalValue = this.getValue();
36498         
36499     },
36500     
36501     inputEl: function ()
36502     {
36503         return this.el.select('.roo-radio-set-input', true).first();
36504     },
36505     
36506     getChildContainer : function()
36507     {
36508         return this.itemsEl;
36509     },
36510     
36511     register : function(item)
36512     {
36513         this.radioes.push(item);
36514         
36515     },
36516     
36517     validate : function()
36518     {   
36519         if(this.getVisibilityEl().hasClass('hidden')){
36520             return true;
36521         }
36522         
36523         var valid = false;
36524         
36525         Roo.each(this.radioes, function(i){
36526             if(!i.checked){
36527                 return;
36528             }
36529             
36530             valid = true;
36531             return false;
36532         });
36533         
36534         if(this.allowBlank) {
36535             return true;
36536         }
36537         
36538         if(this.disabled || valid){
36539             this.markValid();
36540             return true;
36541         }
36542         
36543         this.markInvalid();
36544         return false;
36545         
36546     },
36547     
36548     markValid : function()
36549     {
36550         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36551             this.indicatorEl().removeClass('visible');
36552             this.indicatorEl().addClass('invisible');
36553         }
36554         
36555         
36556         if (Roo.bootstrap.version == 3) {
36557             this.el.removeClass([this.invalidClass, this.validClass]);
36558             this.el.addClass(this.validClass);
36559         } else {
36560             this.el.removeClass(['is-invalid','is-valid']);
36561             this.el.addClass(['is-valid']);
36562         }
36563         this.fireEvent('valid', this);
36564     },
36565     
36566     markInvalid : function(msg)
36567     {
36568         if(this.allowBlank || this.disabled){
36569             return;
36570         }
36571         
36572         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36573             this.indicatorEl().removeClass('invisible');
36574             this.indicatorEl().addClass('visible');
36575         }
36576         if (Roo.bootstrap.version == 3) {
36577             this.el.removeClass([this.invalidClass, this.validClass]);
36578             this.el.addClass(this.invalidClass);
36579         } else {
36580             this.el.removeClass(['is-invalid','is-valid']);
36581             this.el.addClass(['is-invalid']);
36582         }
36583         
36584         this.fireEvent('invalid', this, msg);
36585         
36586     },
36587     
36588     setValue : function(v, suppressEvent)
36589     {   
36590         if(this.value === v){
36591             return;
36592         }
36593         
36594         this.value = v;
36595         
36596         if(this.rendered){
36597             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36598         }
36599         
36600         Roo.each(this.radioes, function(i){
36601             i.checked = false;
36602             i.el.removeClass('checked');
36603         });
36604         
36605         Roo.each(this.radioes, function(i){
36606             
36607             if(i.value === v || i.value.toString() === v.toString()){
36608                 i.checked = true;
36609                 i.el.addClass('checked');
36610                 
36611                 if(suppressEvent !== true){
36612                     this.fireEvent('check', this, i);
36613                 }
36614                 
36615                 return false;
36616             }
36617             
36618         }, this);
36619         
36620         this.validate();
36621     },
36622     
36623     clearInvalid : function(){
36624         
36625         if(!this.el || this.preventMark){
36626             return;
36627         }
36628         
36629         this.el.removeClass([this.invalidClass]);
36630         
36631         this.fireEvent('valid', this);
36632     }
36633     
36634 });
36635
36636 Roo.apply(Roo.bootstrap.RadioSet, {
36637     
36638     groups: {},
36639     
36640     register : function(set)
36641     {
36642         this.groups[set.name] = set;
36643     },
36644     
36645     get: function(name) 
36646     {
36647         if (typeof(this.groups[name]) == 'undefined') {
36648             return false;
36649         }
36650         
36651         return this.groups[name] ;
36652     }
36653     
36654 });
36655 /*
36656  * Based on:
36657  * Ext JS Library 1.1.1
36658  * Copyright(c) 2006-2007, Ext JS, LLC.
36659  *
36660  * Originally Released Under LGPL - original licence link has changed is not relivant.
36661  *
36662  * Fork - LGPL
36663  * <script type="text/javascript">
36664  */
36665
36666
36667 /**
36668  * @class Roo.bootstrap.SplitBar
36669  * @extends Roo.util.Observable
36670  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36671  * <br><br>
36672  * Usage:
36673  * <pre><code>
36674 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36675                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36676 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36677 split.minSize = 100;
36678 split.maxSize = 600;
36679 split.animate = true;
36680 split.on('moved', splitterMoved);
36681 </code></pre>
36682  * @constructor
36683  * Create a new SplitBar
36684  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36685  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36686  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36687  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36688                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36689                         position of the SplitBar).
36690  */
36691 Roo.bootstrap.SplitBar = function(cfg){
36692     
36693     /** @private */
36694     
36695     //{
36696     //  dragElement : elm
36697     //  resizingElement: el,
36698         // optional..
36699     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36700     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36701         // existingProxy ???
36702     //}
36703     
36704     this.el = Roo.get(cfg.dragElement, true);
36705     this.el.dom.unselectable = "on";
36706     /** @private */
36707     this.resizingEl = Roo.get(cfg.resizingElement, true);
36708
36709     /**
36710      * @private
36711      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36712      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36713      * @type Number
36714      */
36715     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36716     
36717     /**
36718      * The minimum size of the resizing element. (Defaults to 0)
36719      * @type Number
36720      */
36721     this.minSize = 0;
36722     
36723     /**
36724      * The maximum size of the resizing element. (Defaults to 2000)
36725      * @type Number
36726      */
36727     this.maxSize = 2000;
36728     
36729     /**
36730      * Whether to animate the transition to the new size
36731      * @type Boolean
36732      */
36733     this.animate = false;
36734     
36735     /**
36736      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36737      * @type Boolean
36738      */
36739     this.useShim = false;
36740     
36741     /** @private */
36742     this.shim = null;
36743     
36744     if(!cfg.existingProxy){
36745         /** @private */
36746         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36747     }else{
36748         this.proxy = Roo.get(cfg.existingProxy).dom;
36749     }
36750     /** @private */
36751     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36752     
36753     /** @private */
36754     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36755     
36756     /** @private */
36757     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36758     
36759     /** @private */
36760     this.dragSpecs = {};
36761     
36762     /**
36763      * @private The adapter to use to positon and resize elements
36764      */
36765     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36766     this.adapter.init(this);
36767     
36768     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36769         /** @private */
36770         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36771         this.el.addClass("roo-splitbar-h");
36772     }else{
36773         /** @private */
36774         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36775         this.el.addClass("roo-splitbar-v");
36776     }
36777     
36778     this.addEvents({
36779         /**
36780          * @event resize
36781          * Fires when the splitter is moved (alias for {@link #event-moved})
36782          * @param {Roo.bootstrap.SplitBar} this
36783          * @param {Number} newSize the new width or height
36784          */
36785         "resize" : true,
36786         /**
36787          * @event moved
36788          * Fires when the splitter is moved
36789          * @param {Roo.bootstrap.SplitBar} this
36790          * @param {Number} newSize the new width or height
36791          */
36792         "moved" : true,
36793         /**
36794          * @event beforeresize
36795          * Fires before the splitter is dragged
36796          * @param {Roo.bootstrap.SplitBar} this
36797          */
36798         "beforeresize" : true,
36799
36800         "beforeapply" : true
36801     });
36802
36803     Roo.util.Observable.call(this);
36804 };
36805
36806 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36807     onStartProxyDrag : function(x, y){
36808         this.fireEvent("beforeresize", this);
36809         if(!this.overlay){
36810             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36811             o.unselectable();
36812             o.enableDisplayMode("block");
36813             // all splitbars share the same overlay
36814             Roo.bootstrap.SplitBar.prototype.overlay = o;
36815         }
36816         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36817         this.overlay.show();
36818         Roo.get(this.proxy).setDisplayed("block");
36819         var size = this.adapter.getElementSize(this);
36820         this.activeMinSize = this.getMinimumSize();;
36821         this.activeMaxSize = this.getMaximumSize();;
36822         var c1 = size - this.activeMinSize;
36823         var c2 = Math.max(this.activeMaxSize - size, 0);
36824         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36825             this.dd.resetConstraints();
36826             this.dd.setXConstraint(
36827                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36828                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36829             );
36830             this.dd.setYConstraint(0, 0);
36831         }else{
36832             this.dd.resetConstraints();
36833             this.dd.setXConstraint(0, 0);
36834             this.dd.setYConstraint(
36835                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36836                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36837             );
36838          }
36839         this.dragSpecs.startSize = size;
36840         this.dragSpecs.startPoint = [x, y];
36841         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36842     },
36843     
36844     /** 
36845      * @private Called after the drag operation by the DDProxy
36846      */
36847     onEndProxyDrag : function(e){
36848         Roo.get(this.proxy).setDisplayed(false);
36849         var endPoint = Roo.lib.Event.getXY(e);
36850         if(this.overlay){
36851             this.overlay.hide();
36852         }
36853         var newSize;
36854         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36855             newSize = this.dragSpecs.startSize + 
36856                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36857                     endPoint[0] - this.dragSpecs.startPoint[0] :
36858                     this.dragSpecs.startPoint[0] - endPoint[0]
36859                 );
36860         }else{
36861             newSize = this.dragSpecs.startSize + 
36862                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36863                     endPoint[1] - this.dragSpecs.startPoint[1] :
36864                     this.dragSpecs.startPoint[1] - endPoint[1]
36865                 );
36866         }
36867         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36868         if(newSize != this.dragSpecs.startSize){
36869             if(this.fireEvent('beforeapply', this, newSize) !== false){
36870                 this.adapter.setElementSize(this, newSize);
36871                 this.fireEvent("moved", this, newSize);
36872                 this.fireEvent("resize", this, newSize);
36873             }
36874         }
36875     },
36876     
36877     /**
36878      * Get the adapter this SplitBar uses
36879      * @return The adapter object
36880      */
36881     getAdapter : function(){
36882         return this.adapter;
36883     },
36884     
36885     /**
36886      * Set the adapter this SplitBar uses
36887      * @param {Object} adapter A SplitBar adapter object
36888      */
36889     setAdapter : function(adapter){
36890         this.adapter = adapter;
36891         this.adapter.init(this);
36892     },
36893     
36894     /**
36895      * Gets the minimum size for the resizing element
36896      * @return {Number} The minimum size
36897      */
36898     getMinimumSize : function(){
36899         return this.minSize;
36900     },
36901     
36902     /**
36903      * Sets the minimum size for the resizing element
36904      * @param {Number} minSize The minimum size
36905      */
36906     setMinimumSize : function(minSize){
36907         this.minSize = minSize;
36908     },
36909     
36910     /**
36911      * Gets the maximum size for the resizing element
36912      * @return {Number} The maximum size
36913      */
36914     getMaximumSize : function(){
36915         return this.maxSize;
36916     },
36917     
36918     /**
36919      * Sets the maximum size for the resizing element
36920      * @param {Number} maxSize The maximum size
36921      */
36922     setMaximumSize : function(maxSize){
36923         this.maxSize = maxSize;
36924     },
36925     
36926     /**
36927      * Sets the initialize size for the resizing element
36928      * @param {Number} size The initial size
36929      */
36930     setCurrentSize : function(size){
36931         var oldAnimate = this.animate;
36932         this.animate = false;
36933         this.adapter.setElementSize(this, size);
36934         this.animate = oldAnimate;
36935     },
36936     
36937     /**
36938      * Destroy this splitbar. 
36939      * @param {Boolean} removeEl True to remove the element
36940      */
36941     destroy : function(removeEl){
36942         if(this.shim){
36943             this.shim.remove();
36944         }
36945         this.dd.unreg();
36946         this.proxy.parentNode.removeChild(this.proxy);
36947         if(removeEl){
36948             this.el.remove();
36949         }
36950     }
36951 });
36952
36953 /**
36954  * @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.
36955  */
36956 Roo.bootstrap.SplitBar.createProxy = function(dir){
36957     var proxy = new Roo.Element(document.createElement("div"));
36958     proxy.unselectable();
36959     var cls = 'roo-splitbar-proxy';
36960     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36961     document.body.appendChild(proxy.dom);
36962     return proxy.dom;
36963 };
36964
36965 /** 
36966  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36967  * Default Adapter. It assumes the splitter and resizing element are not positioned
36968  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36969  */
36970 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36971 };
36972
36973 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36974     // do nothing for now
36975     init : function(s){
36976     
36977     },
36978     /**
36979      * Called before drag operations to get the current size of the resizing element. 
36980      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36981      */
36982      getElementSize : function(s){
36983         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36984             return s.resizingEl.getWidth();
36985         }else{
36986             return s.resizingEl.getHeight();
36987         }
36988     },
36989     
36990     /**
36991      * Called after drag operations to set the size of the resizing element.
36992      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36993      * @param {Number} newSize The new size to set
36994      * @param {Function} onComplete A function to be invoked when resizing is complete
36995      */
36996     setElementSize : function(s, newSize, onComplete){
36997         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36998             if(!s.animate){
36999                 s.resizingEl.setWidth(newSize);
37000                 if(onComplete){
37001                     onComplete(s, newSize);
37002                 }
37003             }else{
37004                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37005             }
37006         }else{
37007             
37008             if(!s.animate){
37009                 s.resizingEl.setHeight(newSize);
37010                 if(onComplete){
37011                     onComplete(s, newSize);
37012                 }
37013             }else{
37014                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37015             }
37016         }
37017     }
37018 };
37019
37020 /** 
37021  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37022  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37023  * Adapter that  moves the splitter element to align with the resized sizing element. 
37024  * Used with an absolute positioned SplitBar.
37025  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37026  * document.body, make sure you assign an id to the body element.
37027  */
37028 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37029     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37030     this.container = Roo.get(container);
37031 };
37032
37033 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37034     init : function(s){
37035         this.basic.init(s);
37036     },
37037     
37038     getElementSize : function(s){
37039         return this.basic.getElementSize(s);
37040     },
37041     
37042     setElementSize : function(s, newSize, onComplete){
37043         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37044     },
37045     
37046     moveSplitter : function(s){
37047         var yes = Roo.bootstrap.SplitBar;
37048         switch(s.placement){
37049             case yes.LEFT:
37050                 s.el.setX(s.resizingEl.getRight());
37051                 break;
37052             case yes.RIGHT:
37053                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37054                 break;
37055             case yes.TOP:
37056                 s.el.setY(s.resizingEl.getBottom());
37057                 break;
37058             case yes.BOTTOM:
37059                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37060                 break;
37061         }
37062     }
37063 };
37064
37065 /**
37066  * Orientation constant - Create a vertical SplitBar
37067  * @static
37068  * @type Number
37069  */
37070 Roo.bootstrap.SplitBar.VERTICAL = 1;
37071
37072 /**
37073  * Orientation constant - Create a horizontal SplitBar
37074  * @static
37075  * @type Number
37076  */
37077 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37078
37079 /**
37080  * Placement constant - The resizing element is to the left of the splitter element
37081  * @static
37082  * @type Number
37083  */
37084 Roo.bootstrap.SplitBar.LEFT = 1;
37085
37086 /**
37087  * Placement constant - The resizing element is to the right of the splitter element
37088  * @static
37089  * @type Number
37090  */
37091 Roo.bootstrap.SplitBar.RIGHT = 2;
37092
37093 /**
37094  * Placement constant - The resizing element is positioned above the splitter element
37095  * @static
37096  * @type Number
37097  */
37098 Roo.bootstrap.SplitBar.TOP = 3;
37099
37100 /**
37101  * Placement constant - The resizing element is positioned under splitter element
37102  * @static
37103  * @type Number
37104  */
37105 Roo.bootstrap.SplitBar.BOTTOM = 4;
37106 Roo.namespace("Roo.bootstrap.layout");/*
37107  * Based on:
37108  * Ext JS Library 1.1.1
37109  * Copyright(c) 2006-2007, Ext JS, LLC.
37110  *
37111  * Originally Released Under LGPL - original licence link has changed is not relivant.
37112  *
37113  * Fork - LGPL
37114  * <script type="text/javascript">
37115  */
37116
37117 /**
37118  * @class Roo.bootstrap.layout.Manager
37119  * @extends Roo.bootstrap.Component
37120  * Base class for layout managers.
37121  */
37122 Roo.bootstrap.layout.Manager = function(config)
37123 {
37124     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37125
37126
37127
37128
37129
37130     /** false to disable window resize monitoring @type Boolean */
37131     this.monitorWindowResize = true;
37132     this.regions = {};
37133     this.addEvents({
37134         /**
37135          * @event layout
37136          * Fires when a layout is performed.
37137          * @param {Roo.LayoutManager} this
37138          */
37139         "layout" : true,
37140         /**
37141          * @event regionresized
37142          * Fires when the user resizes a region.
37143          * @param {Roo.LayoutRegion} region The resized region
37144          * @param {Number} newSize The new size (width for east/west, height for north/south)
37145          */
37146         "regionresized" : true,
37147         /**
37148          * @event regioncollapsed
37149          * Fires when a region is collapsed.
37150          * @param {Roo.LayoutRegion} region The collapsed region
37151          */
37152         "regioncollapsed" : true,
37153         /**
37154          * @event regionexpanded
37155          * Fires when a region is expanded.
37156          * @param {Roo.LayoutRegion} region The expanded region
37157          */
37158         "regionexpanded" : true
37159     });
37160     this.updating = false;
37161
37162     if (config.el) {
37163         this.el = Roo.get(config.el);
37164         this.initEvents();
37165     }
37166
37167 };
37168
37169 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37170
37171
37172     regions : null,
37173
37174     monitorWindowResize : true,
37175
37176
37177     updating : false,
37178
37179
37180     onRender : function(ct, position)
37181     {
37182         if(!this.el){
37183             this.el = Roo.get(ct);
37184             this.initEvents();
37185         }
37186         //this.fireEvent('render',this);
37187     },
37188
37189
37190     initEvents: function()
37191     {
37192
37193
37194         // ie scrollbar fix
37195         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37196             document.body.scroll = "no";
37197         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37198             this.el.position('relative');
37199         }
37200         this.id = this.el.id;
37201         this.el.addClass("roo-layout-container");
37202         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37203         if(this.el.dom != document.body ) {
37204             this.el.on('resize', this.layout,this);
37205             this.el.on('show', this.layout,this);
37206         }
37207
37208     },
37209
37210     /**
37211      * Returns true if this layout is currently being updated
37212      * @return {Boolean}
37213      */
37214     isUpdating : function(){
37215         return this.updating;
37216     },
37217
37218     /**
37219      * Suspend the LayoutManager from doing auto-layouts while
37220      * making multiple add or remove calls
37221      */
37222     beginUpdate : function(){
37223         this.updating = true;
37224     },
37225
37226     /**
37227      * Restore auto-layouts and optionally disable the manager from performing a layout
37228      * @param {Boolean} noLayout true to disable a layout update
37229      */
37230     endUpdate : function(noLayout){
37231         this.updating = false;
37232         if(!noLayout){
37233             this.layout();
37234         }
37235     },
37236
37237     layout: function(){
37238         // abstract...
37239     },
37240
37241     onRegionResized : function(region, newSize){
37242         this.fireEvent("regionresized", region, newSize);
37243         this.layout();
37244     },
37245
37246     onRegionCollapsed : function(region){
37247         this.fireEvent("regioncollapsed", region);
37248     },
37249
37250     onRegionExpanded : function(region){
37251         this.fireEvent("regionexpanded", region);
37252     },
37253
37254     /**
37255      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37256      * performs box-model adjustments.
37257      * @return {Object} The size as an object {width: (the width), height: (the height)}
37258      */
37259     getViewSize : function()
37260     {
37261         var size;
37262         if(this.el.dom != document.body){
37263             size = this.el.getSize();
37264         }else{
37265             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37266         }
37267         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37268         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37269         return size;
37270     },
37271
37272     /**
37273      * Returns the Element this layout is bound to.
37274      * @return {Roo.Element}
37275      */
37276     getEl : function(){
37277         return this.el;
37278     },
37279
37280     /**
37281      * Returns the specified region.
37282      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37283      * @return {Roo.LayoutRegion}
37284      */
37285     getRegion : function(target){
37286         return this.regions[target.toLowerCase()];
37287     },
37288
37289     onWindowResize : function(){
37290         if(this.monitorWindowResize){
37291             this.layout();
37292         }
37293     }
37294 });
37295 /*
37296  * Based on:
37297  * Ext JS Library 1.1.1
37298  * Copyright(c) 2006-2007, Ext JS, LLC.
37299  *
37300  * Originally Released Under LGPL - original licence link has changed is not relivant.
37301  *
37302  * Fork - LGPL
37303  * <script type="text/javascript">
37304  */
37305 /**
37306  * @class Roo.bootstrap.layout.Border
37307  * @extends Roo.bootstrap.layout.Manager
37308  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37309  * please see: examples/bootstrap/nested.html<br><br>
37310  
37311 <b>The container the layout is rendered into can be either the body element or any other element.
37312 If it is not the body element, the container needs to either be an absolute positioned element,
37313 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37314 the container size if it is not the body element.</b>
37315
37316 * @constructor
37317 * Create a new Border
37318 * @param {Object} config Configuration options
37319  */
37320 Roo.bootstrap.layout.Border = function(config){
37321     config = config || {};
37322     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37323     
37324     
37325     
37326     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37327         if(config[region]){
37328             config[region].region = region;
37329             this.addRegion(config[region]);
37330         }
37331     },this);
37332     
37333 };
37334
37335 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37336
37337 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37338     
37339     parent : false, // this might point to a 'nest' or a ???
37340     
37341     /**
37342      * Creates and adds a new region if it doesn't already exist.
37343      * @param {String} target The target region key (north, south, east, west or center).
37344      * @param {Object} config The regions config object
37345      * @return {BorderLayoutRegion} The new region
37346      */
37347     addRegion : function(config)
37348     {
37349         if(!this.regions[config.region]){
37350             var r = this.factory(config);
37351             this.bindRegion(r);
37352         }
37353         return this.regions[config.region];
37354     },
37355
37356     // private (kinda)
37357     bindRegion : function(r){
37358         this.regions[r.config.region] = r;
37359         
37360         r.on("visibilitychange",    this.layout, this);
37361         r.on("paneladded",          this.layout, this);
37362         r.on("panelremoved",        this.layout, this);
37363         r.on("invalidated",         this.layout, this);
37364         r.on("resized",             this.onRegionResized, this);
37365         r.on("collapsed",           this.onRegionCollapsed, this);
37366         r.on("expanded",            this.onRegionExpanded, this);
37367     },
37368
37369     /**
37370      * Performs a layout update.
37371      */
37372     layout : function()
37373     {
37374         if(this.updating) {
37375             return;
37376         }
37377         
37378         // render all the rebions if they have not been done alreayd?
37379         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37380             if(this.regions[region] && !this.regions[region].bodyEl){
37381                 this.regions[region].onRender(this.el)
37382             }
37383         },this);
37384         
37385         var size = this.getViewSize();
37386         var w = size.width;
37387         var h = size.height;
37388         var centerW = w;
37389         var centerH = h;
37390         var centerY = 0;
37391         var centerX = 0;
37392         //var x = 0, y = 0;
37393
37394         var rs = this.regions;
37395         var north = rs["north"];
37396         var south = rs["south"]; 
37397         var west = rs["west"];
37398         var east = rs["east"];
37399         var center = rs["center"];
37400         //if(this.hideOnLayout){ // not supported anymore
37401             //c.el.setStyle("display", "none");
37402         //}
37403         if(north && north.isVisible()){
37404             var b = north.getBox();
37405             var m = north.getMargins();
37406             b.width = w - (m.left+m.right);
37407             b.x = m.left;
37408             b.y = m.top;
37409             centerY = b.height + b.y + m.bottom;
37410             centerH -= centerY;
37411             north.updateBox(this.safeBox(b));
37412         }
37413         if(south && south.isVisible()){
37414             var b = south.getBox();
37415             var m = south.getMargins();
37416             b.width = w - (m.left+m.right);
37417             b.x = m.left;
37418             var totalHeight = (b.height + m.top + m.bottom);
37419             b.y = h - totalHeight + m.top;
37420             centerH -= totalHeight;
37421             south.updateBox(this.safeBox(b));
37422         }
37423         if(west && west.isVisible()){
37424             var b = west.getBox();
37425             var m = west.getMargins();
37426             b.height = centerH - (m.top+m.bottom);
37427             b.x = m.left;
37428             b.y = centerY + m.top;
37429             var totalWidth = (b.width + m.left + m.right);
37430             centerX += totalWidth;
37431             centerW -= totalWidth;
37432             west.updateBox(this.safeBox(b));
37433         }
37434         if(east && east.isVisible()){
37435             var b = east.getBox();
37436             var m = east.getMargins();
37437             b.height = centerH - (m.top+m.bottom);
37438             var totalWidth = (b.width + m.left + m.right);
37439             b.x = w - totalWidth + m.left;
37440             b.y = centerY + m.top;
37441             centerW -= totalWidth;
37442             east.updateBox(this.safeBox(b));
37443         }
37444         if(center){
37445             var m = center.getMargins();
37446             var centerBox = {
37447                 x: centerX + m.left,
37448                 y: centerY + m.top,
37449                 width: centerW - (m.left+m.right),
37450                 height: centerH - (m.top+m.bottom)
37451             };
37452             //if(this.hideOnLayout){
37453                 //center.el.setStyle("display", "block");
37454             //}
37455             center.updateBox(this.safeBox(centerBox));
37456         }
37457         this.el.repaint();
37458         this.fireEvent("layout", this);
37459     },
37460
37461     // private
37462     safeBox : function(box){
37463         box.width = Math.max(0, box.width);
37464         box.height = Math.max(0, box.height);
37465         return box;
37466     },
37467
37468     /**
37469      * Adds a ContentPanel (or subclass) to this layout.
37470      * @param {String} target The target region key (north, south, east, west or center).
37471      * @param {Roo.ContentPanel} panel The panel to add
37472      * @return {Roo.ContentPanel} The added panel
37473      */
37474     add : function(target, panel){
37475          
37476         target = target.toLowerCase();
37477         return this.regions[target].add(panel);
37478     },
37479
37480     /**
37481      * Remove a ContentPanel (or subclass) to this layout.
37482      * @param {String} target The target region key (north, south, east, west or center).
37483      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37484      * @return {Roo.ContentPanel} The removed panel
37485      */
37486     remove : function(target, panel){
37487         target = target.toLowerCase();
37488         return this.regions[target].remove(panel);
37489     },
37490
37491     /**
37492      * Searches all regions for a panel with the specified id
37493      * @param {String} panelId
37494      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37495      */
37496     findPanel : function(panelId){
37497         var rs = this.regions;
37498         for(var target in rs){
37499             if(typeof rs[target] != "function"){
37500                 var p = rs[target].getPanel(panelId);
37501                 if(p){
37502                     return p;
37503                 }
37504             }
37505         }
37506         return null;
37507     },
37508
37509     /**
37510      * Searches all regions for a panel with the specified id and activates (shows) it.
37511      * @param {String/ContentPanel} panelId The panels id or the panel itself
37512      * @return {Roo.ContentPanel} The shown panel or null
37513      */
37514     showPanel : function(panelId) {
37515       var rs = this.regions;
37516       for(var target in rs){
37517          var r = rs[target];
37518          if(typeof r != "function"){
37519             if(r.hasPanel(panelId)){
37520                return r.showPanel(panelId);
37521             }
37522          }
37523       }
37524       return null;
37525    },
37526
37527    /**
37528      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37529      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37530      */
37531    /*
37532     restoreState : function(provider){
37533         if(!provider){
37534             provider = Roo.state.Manager;
37535         }
37536         var sm = new Roo.LayoutStateManager();
37537         sm.init(this, provider);
37538     },
37539 */
37540  
37541  
37542     /**
37543      * Adds a xtype elements to the layout.
37544      * <pre><code>
37545
37546 layout.addxtype({
37547        xtype : 'ContentPanel',
37548        region: 'west',
37549        items: [ .... ]
37550    }
37551 );
37552
37553 layout.addxtype({
37554         xtype : 'NestedLayoutPanel',
37555         region: 'west',
37556         layout: {
37557            center: { },
37558            west: { }   
37559         },
37560         items : [ ... list of content panels or nested layout panels.. ]
37561    }
37562 );
37563 </code></pre>
37564      * @param {Object} cfg Xtype definition of item to add.
37565      */
37566     addxtype : function(cfg)
37567     {
37568         // basically accepts a pannel...
37569         // can accept a layout region..!?!?
37570         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37571         
37572         
37573         // theory?  children can only be panels??
37574         
37575         //if (!cfg.xtype.match(/Panel$/)) {
37576         //    return false;
37577         //}
37578         var ret = false;
37579         
37580         if (typeof(cfg.region) == 'undefined') {
37581             Roo.log("Failed to add Panel, region was not set");
37582             Roo.log(cfg);
37583             return false;
37584         }
37585         var region = cfg.region;
37586         delete cfg.region;
37587         
37588           
37589         var xitems = [];
37590         if (cfg.items) {
37591             xitems = cfg.items;
37592             delete cfg.items;
37593         }
37594         var nb = false;
37595         
37596         if ( region == 'center') {
37597             Roo.log("Center: " + cfg.title);
37598         }
37599         
37600         
37601         switch(cfg.xtype) 
37602         {
37603             case 'Content':  // ContentPanel (el, cfg)
37604             case 'Scroll':  // ContentPanel (el, cfg)
37605             case 'View': 
37606                 cfg.autoCreate = cfg.autoCreate || true;
37607                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37608                 //} else {
37609                 //    var el = this.el.createChild();
37610                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37611                 //}
37612                 
37613                 this.add(region, ret);
37614                 break;
37615             
37616             /*
37617             case 'TreePanel': // our new panel!
37618                 cfg.el = this.el.createChild();
37619                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37620                 this.add(region, ret);
37621                 break;
37622             */
37623             
37624             case 'Nest': 
37625                 // create a new Layout (which is  a Border Layout...
37626                 
37627                 var clayout = cfg.layout;
37628                 clayout.el  = this.el.createChild();
37629                 clayout.items   = clayout.items  || [];
37630                 
37631                 delete cfg.layout;
37632                 
37633                 // replace this exitems with the clayout ones..
37634                 xitems = clayout.items;
37635                  
37636                 // force background off if it's in center...
37637                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37638                     cfg.background = false;
37639                 }
37640                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37641                 
37642                 
37643                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37644                 //console.log('adding nested layout panel '  + cfg.toSource());
37645                 this.add(region, ret);
37646                 nb = {}; /// find first...
37647                 break;
37648             
37649             case 'Grid':
37650                 
37651                 // needs grid and region
37652                 
37653                 //var el = this.getRegion(region).el.createChild();
37654                 /*
37655                  *var el = this.el.createChild();
37656                 // create the grid first...
37657                 cfg.grid.container = el;
37658                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37659                 */
37660                 
37661                 if (region == 'center' && this.active ) {
37662                     cfg.background = false;
37663                 }
37664                 
37665                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37666                 
37667                 this.add(region, ret);
37668                 /*
37669                 if (cfg.background) {
37670                     // render grid on panel activation (if panel background)
37671                     ret.on('activate', function(gp) {
37672                         if (!gp.grid.rendered) {
37673                     //        gp.grid.render(el);
37674                         }
37675                     });
37676                 } else {
37677                   //  cfg.grid.render(el);
37678                 }
37679                 */
37680                 break;
37681            
37682            
37683             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37684                 // it was the old xcomponent building that caused this before.
37685                 // espeically if border is the top element in the tree.
37686                 ret = this;
37687                 break; 
37688                 
37689                     
37690                 
37691                 
37692                 
37693             default:
37694                 /*
37695                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37696                     
37697                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37698                     this.add(region, ret);
37699                 } else {
37700                 */
37701                     Roo.log(cfg);
37702                     throw "Can not add '" + cfg.xtype + "' to Border";
37703                     return null;
37704              
37705                                 
37706              
37707         }
37708         this.beginUpdate();
37709         // add children..
37710         var region = '';
37711         var abn = {};
37712         Roo.each(xitems, function(i)  {
37713             region = nb && i.region ? i.region : false;
37714             
37715             var add = ret.addxtype(i);
37716            
37717             if (region) {
37718                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37719                 if (!i.background) {
37720                     abn[region] = nb[region] ;
37721                 }
37722             }
37723             
37724         });
37725         this.endUpdate();
37726
37727         // make the last non-background panel active..
37728         //if (nb) { Roo.log(abn); }
37729         if (nb) {
37730             
37731             for(var r in abn) {
37732                 region = this.getRegion(r);
37733                 if (region) {
37734                     // tried using nb[r], but it does not work..
37735                      
37736                     region.showPanel(abn[r]);
37737                    
37738                 }
37739             }
37740         }
37741         return ret;
37742         
37743     },
37744     
37745     
37746 // private
37747     factory : function(cfg)
37748     {
37749         
37750         var validRegions = Roo.bootstrap.layout.Border.regions;
37751
37752         var target = cfg.region;
37753         cfg.mgr = this;
37754         
37755         var r = Roo.bootstrap.layout;
37756         Roo.log(target);
37757         switch(target){
37758             case "north":
37759                 return new r.North(cfg);
37760             case "south":
37761                 return new r.South(cfg);
37762             case "east":
37763                 return new r.East(cfg);
37764             case "west":
37765                 return new r.West(cfg);
37766             case "center":
37767                 return new r.Center(cfg);
37768         }
37769         throw 'Layout region "'+target+'" not supported.';
37770     }
37771     
37772     
37773 });
37774  /*
37775  * Based on:
37776  * Ext JS Library 1.1.1
37777  * Copyright(c) 2006-2007, Ext JS, LLC.
37778  *
37779  * Originally Released Under LGPL - original licence link has changed is not relivant.
37780  *
37781  * Fork - LGPL
37782  * <script type="text/javascript">
37783  */
37784  
37785 /**
37786  * @class Roo.bootstrap.layout.Basic
37787  * @extends Roo.util.Observable
37788  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37789  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37790  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37791  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37792  * @cfg {string}   region  the region that it inhabits..
37793  * @cfg {bool}   skipConfig skip config?
37794  * 
37795
37796  */
37797 Roo.bootstrap.layout.Basic = function(config){
37798     
37799     this.mgr = config.mgr;
37800     
37801     this.position = config.region;
37802     
37803     var skipConfig = config.skipConfig;
37804     
37805     this.events = {
37806         /**
37807          * @scope Roo.BasicLayoutRegion
37808          */
37809         
37810         /**
37811          * @event beforeremove
37812          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37813          * @param {Roo.LayoutRegion} this
37814          * @param {Roo.ContentPanel} panel The panel
37815          * @param {Object} e The cancel event object
37816          */
37817         "beforeremove" : true,
37818         /**
37819          * @event invalidated
37820          * Fires when the layout for this region is changed.
37821          * @param {Roo.LayoutRegion} this
37822          */
37823         "invalidated" : true,
37824         /**
37825          * @event visibilitychange
37826          * Fires when this region is shown or hidden 
37827          * @param {Roo.LayoutRegion} this
37828          * @param {Boolean} visibility true or false
37829          */
37830         "visibilitychange" : true,
37831         /**
37832          * @event paneladded
37833          * Fires when a panel is added. 
37834          * @param {Roo.LayoutRegion} this
37835          * @param {Roo.ContentPanel} panel The panel
37836          */
37837         "paneladded" : true,
37838         /**
37839          * @event panelremoved
37840          * Fires when a panel is removed. 
37841          * @param {Roo.LayoutRegion} this
37842          * @param {Roo.ContentPanel} panel The panel
37843          */
37844         "panelremoved" : true,
37845         /**
37846          * @event beforecollapse
37847          * Fires when this region before collapse.
37848          * @param {Roo.LayoutRegion} this
37849          */
37850         "beforecollapse" : true,
37851         /**
37852          * @event collapsed
37853          * Fires when this region is collapsed.
37854          * @param {Roo.LayoutRegion} this
37855          */
37856         "collapsed" : true,
37857         /**
37858          * @event expanded
37859          * Fires when this region is expanded.
37860          * @param {Roo.LayoutRegion} this
37861          */
37862         "expanded" : true,
37863         /**
37864          * @event slideshow
37865          * Fires when this region is slid into view.
37866          * @param {Roo.LayoutRegion} this
37867          */
37868         "slideshow" : true,
37869         /**
37870          * @event slidehide
37871          * Fires when this region slides out of view. 
37872          * @param {Roo.LayoutRegion} this
37873          */
37874         "slidehide" : true,
37875         /**
37876          * @event panelactivated
37877          * Fires when a panel is activated. 
37878          * @param {Roo.LayoutRegion} this
37879          * @param {Roo.ContentPanel} panel The activated panel
37880          */
37881         "panelactivated" : true,
37882         /**
37883          * @event resized
37884          * Fires when the user resizes this region. 
37885          * @param {Roo.LayoutRegion} this
37886          * @param {Number} newSize The new size (width for east/west, height for north/south)
37887          */
37888         "resized" : true
37889     };
37890     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37891     this.panels = new Roo.util.MixedCollection();
37892     this.panels.getKey = this.getPanelId.createDelegate(this);
37893     this.box = null;
37894     this.activePanel = null;
37895     // ensure listeners are added...
37896     
37897     if (config.listeners || config.events) {
37898         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37899             listeners : config.listeners || {},
37900             events : config.events || {}
37901         });
37902     }
37903     
37904     if(skipConfig !== true){
37905         this.applyConfig(config);
37906     }
37907 };
37908
37909 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37910 {
37911     getPanelId : function(p){
37912         return p.getId();
37913     },
37914     
37915     applyConfig : function(config){
37916         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37917         this.config = config;
37918         
37919     },
37920     
37921     /**
37922      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37923      * the width, for horizontal (north, south) the height.
37924      * @param {Number} newSize The new width or height
37925      */
37926     resizeTo : function(newSize){
37927         var el = this.el ? this.el :
37928                  (this.activePanel ? this.activePanel.getEl() : null);
37929         if(el){
37930             switch(this.position){
37931                 case "east":
37932                 case "west":
37933                     el.setWidth(newSize);
37934                     this.fireEvent("resized", this, newSize);
37935                 break;
37936                 case "north":
37937                 case "south":
37938                     el.setHeight(newSize);
37939                     this.fireEvent("resized", this, newSize);
37940                 break;                
37941             }
37942         }
37943     },
37944     
37945     getBox : function(){
37946         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37947     },
37948     
37949     getMargins : function(){
37950         return this.margins;
37951     },
37952     
37953     updateBox : function(box){
37954         this.box = box;
37955         var el = this.activePanel.getEl();
37956         el.dom.style.left = box.x + "px";
37957         el.dom.style.top = box.y + "px";
37958         this.activePanel.setSize(box.width, box.height);
37959     },
37960     
37961     /**
37962      * Returns the container element for this region.
37963      * @return {Roo.Element}
37964      */
37965     getEl : function(){
37966         return this.activePanel;
37967     },
37968     
37969     /**
37970      * Returns true if this region is currently visible.
37971      * @return {Boolean}
37972      */
37973     isVisible : function(){
37974         return this.activePanel ? true : false;
37975     },
37976     
37977     setActivePanel : function(panel){
37978         panel = this.getPanel(panel);
37979         if(this.activePanel && this.activePanel != panel){
37980             this.activePanel.setActiveState(false);
37981             this.activePanel.getEl().setLeftTop(-10000,-10000);
37982         }
37983         this.activePanel = panel;
37984         panel.setActiveState(true);
37985         if(this.box){
37986             panel.setSize(this.box.width, this.box.height);
37987         }
37988         this.fireEvent("panelactivated", this, panel);
37989         this.fireEvent("invalidated");
37990     },
37991     
37992     /**
37993      * Show the specified panel.
37994      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37995      * @return {Roo.ContentPanel} The shown panel or null
37996      */
37997     showPanel : function(panel){
37998         panel = this.getPanel(panel);
37999         if(panel){
38000             this.setActivePanel(panel);
38001         }
38002         return panel;
38003     },
38004     
38005     /**
38006      * Get the active panel for this region.
38007      * @return {Roo.ContentPanel} The active panel or null
38008      */
38009     getActivePanel : function(){
38010         return this.activePanel;
38011     },
38012     
38013     /**
38014      * Add the passed ContentPanel(s)
38015      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38016      * @return {Roo.ContentPanel} The panel added (if only one was added)
38017      */
38018     add : function(panel){
38019         if(arguments.length > 1){
38020             for(var i = 0, len = arguments.length; i < len; i++) {
38021                 this.add(arguments[i]);
38022             }
38023             return null;
38024         }
38025         if(this.hasPanel(panel)){
38026             this.showPanel(panel);
38027             return panel;
38028         }
38029         var el = panel.getEl();
38030         if(el.dom.parentNode != this.mgr.el.dom){
38031             this.mgr.el.dom.appendChild(el.dom);
38032         }
38033         if(panel.setRegion){
38034             panel.setRegion(this);
38035         }
38036         this.panels.add(panel);
38037         el.setStyle("position", "absolute");
38038         if(!panel.background){
38039             this.setActivePanel(panel);
38040             if(this.config.initialSize && this.panels.getCount()==1){
38041                 this.resizeTo(this.config.initialSize);
38042             }
38043         }
38044         this.fireEvent("paneladded", this, panel);
38045         return panel;
38046     },
38047     
38048     /**
38049      * Returns true if the panel is in this region.
38050      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38051      * @return {Boolean}
38052      */
38053     hasPanel : function(panel){
38054         if(typeof panel == "object"){ // must be panel obj
38055             panel = panel.getId();
38056         }
38057         return this.getPanel(panel) ? true : false;
38058     },
38059     
38060     /**
38061      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38062      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38063      * @param {Boolean} preservePanel Overrides the config preservePanel option
38064      * @return {Roo.ContentPanel} The panel that was removed
38065      */
38066     remove : function(panel, preservePanel){
38067         panel = this.getPanel(panel);
38068         if(!panel){
38069             return null;
38070         }
38071         var e = {};
38072         this.fireEvent("beforeremove", this, panel, e);
38073         if(e.cancel === true){
38074             return null;
38075         }
38076         var panelId = panel.getId();
38077         this.panels.removeKey(panelId);
38078         return panel;
38079     },
38080     
38081     /**
38082      * Returns the panel specified or null if it's not in this region.
38083      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38084      * @return {Roo.ContentPanel}
38085      */
38086     getPanel : function(id){
38087         if(typeof id == "object"){ // must be panel obj
38088             return id;
38089         }
38090         return this.panels.get(id);
38091     },
38092     
38093     /**
38094      * Returns this regions position (north/south/east/west/center).
38095      * @return {String} 
38096      */
38097     getPosition: function(){
38098         return this.position;    
38099     }
38100 });/*
38101  * Based on:
38102  * Ext JS Library 1.1.1
38103  * Copyright(c) 2006-2007, Ext JS, LLC.
38104  *
38105  * Originally Released Under LGPL - original licence link has changed is not relivant.
38106  *
38107  * Fork - LGPL
38108  * <script type="text/javascript">
38109  */
38110  
38111 /**
38112  * @class Roo.bootstrap.layout.Region
38113  * @extends Roo.bootstrap.layout.Basic
38114  * This class represents a region in a layout manager.
38115  
38116  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38117  * @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})
38118  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38119  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38120  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38121  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38122  * @cfg {String}    title           The title for the region (overrides panel titles)
38123  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38124  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38125  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38126  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38127  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38128  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38129  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38130  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38131  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38132  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38133
38134  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38135  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38136  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38137  * @cfg {Number}    width           For East/West panels
38138  * @cfg {Number}    height          For North/South panels
38139  * @cfg {Boolean}   split           To show the splitter
38140  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38141  * 
38142  * @cfg {string}   cls             Extra CSS classes to add to region
38143  * 
38144  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38145  * @cfg {string}   region  the region that it inhabits..
38146  *
38147
38148  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38149  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38150
38151  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38152  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38153  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38154  */
38155 Roo.bootstrap.layout.Region = function(config)
38156 {
38157     this.applyConfig(config);
38158
38159     var mgr = config.mgr;
38160     var pos = config.region;
38161     config.skipConfig = true;
38162     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38163     
38164     if (mgr.el) {
38165         this.onRender(mgr.el);   
38166     }
38167      
38168     this.visible = true;
38169     this.collapsed = false;
38170     this.unrendered_panels = [];
38171 };
38172
38173 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38174
38175     position: '', // set by wrapper (eg. north/south etc..)
38176     unrendered_panels : null,  // unrendered panels.
38177     
38178     tabPosition : false,
38179     
38180     mgr: false, // points to 'Border'
38181     
38182     
38183     createBody : function(){
38184         /** This region's body element 
38185         * @type Roo.Element */
38186         this.bodyEl = this.el.createChild({
38187                 tag: "div",
38188                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38189         });
38190     },
38191
38192     onRender: function(ctr, pos)
38193     {
38194         var dh = Roo.DomHelper;
38195         /** This region's container element 
38196         * @type Roo.Element */
38197         this.el = dh.append(ctr.dom, {
38198                 tag: "div",
38199                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38200             }, true);
38201         /** This region's title element 
38202         * @type Roo.Element */
38203     
38204         this.titleEl = dh.append(this.el.dom,  {
38205                 tag: "div",
38206                 unselectable: "on",
38207                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38208                 children:[
38209                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38210                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38211                 ]
38212             }, true);
38213         
38214         this.titleEl.enableDisplayMode();
38215         /** This region's title text element 
38216         * @type HTMLElement */
38217         this.titleTextEl = this.titleEl.dom.firstChild;
38218         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38219         /*
38220         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38221         this.closeBtn.enableDisplayMode();
38222         this.closeBtn.on("click", this.closeClicked, this);
38223         this.closeBtn.hide();
38224     */
38225         this.createBody(this.config);
38226         if(this.config.hideWhenEmpty){
38227             this.hide();
38228             this.on("paneladded", this.validateVisibility, this);
38229             this.on("panelremoved", this.validateVisibility, this);
38230         }
38231         if(this.autoScroll){
38232             this.bodyEl.setStyle("overflow", "auto");
38233         }else{
38234             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38235         }
38236         //if(c.titlebar !== false){
38237             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38238                 this.titleEl.hide();
38239             }else{
38240                 this.titleEl.show();
38241                 if(this.config.title){
38242                     this.titleTextEl.innerHTML = this.config.title;
38243                 }
38244             }
38245         //}
38246         if(this.config.collapsed){
38247             this.collapse(true);
38248         }
38249         if(this.config.hidden){
38250             this.hide();
38251         }
38252         
38253         if (this.unrendered_panels && this.unrendered_panels.length) {
38254             for (var i =0;i< this.unrendered_panels.length; i++) {
38255                 this.add(this.unrendered_panels[i]);
38256             }
38257             this.unrendered_panels = null;
38258             
38259         }
38260         
38261     },
38262     
38263     applyConfig : function(c)
38264     {
38265         /*
38266          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38267             var dh = Roo.DomHelper;
38268             if(c.titlebar !== false){
38269                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38270                 this.collapseBtn.on("click", this.collapse, this);
38271                 this.collapseBtn.enableDisplayMode();
38272                 /*
38273                 if(c.showPin === true || this.showPin){
38274                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38275                     this.stickBtn.enableDisplayMode();
38276                     this.stickBtn.on("click", this.expand, this);
38277                     this.stickBtn.hide();
38278                 }
38279                 
38280             }
38281             */
38282             /** This region's collapsed element
38283             * @type Roo.Element */
38284             /*
38285              *
38286             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38287                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38288             ]}, true);
38289             
38290             if(c.floatable !== false){
38291                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38292                this.collapsedEl.on("click", this.collapseClick, this);
38293             }
38294
38295             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38296                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38297                    id: "message", unselectable: "on", style:{"float":"left"}});
38298                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38299              }
38300             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38301             this.expandBtn.on("click", this.expand, this);
38302             
38303         }
38304         
38305         if(this.collapseBtn){
38306             this.collapseBtn.setVisible(c.collapsible == true);
38307         }
38308         
38309         this.cmargins = c.cmargins || this.cmargins ||
38310                          (this.position == "west" || this.position == "east" ?
38311                              {top: 0, left: 2, right:2, bottom: 0} :
38312                              {top: 2, left: 0, right:0, bottom: 2});
38313         */
38314         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38315         
38316         
38317         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38318         
38319         this.autoScroll = c.autoScroll || false;
38320         
38321         
38322        
38323         
38324         this.duration = c.duration || .30;
38325         this.slideDuration = c.slideDuration || .45;
38326         this.config = c;
38327        
38328     },
38329     /**
38330      * Returns true if this region is currently visible.
38331      * @return {Boolean}
38332      */
38333     isVisible : function(){
38334         return this.visible;
38335     },
38336
38337     /**
38338      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38339      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38340      */
38341     //setCollapsedTitle : function(title){
38342     //    title = title || "&#160;";
38343      //   if(this.collapsedTitleTextEl){
38344       //      this.collapsedTitleTextEl.innerHTML = title;
38345        // }
38346     //},
38347
38348     getBox : function(){
38349         var b;
38350       //  if(!this.collapsed){
38351             b = this.el.getBox(false, true);
38352        // }else{
38353           //  b = this.collapsedEl.getBox(false, true);
38354         //}
38355         return b;
38356     },
38357
38358     getMargins : function(){
38359         return this.margins;
38360         //return this.collapsed ? this.cmargins : this.margins;
38361     },
38362 /*
38363     highlight : function(){
38364         this.el.addClass("x-layout-panel-dragover");
38365     },
38366
38367     unhighlight : function(){
38368         this.el.removeClass("x-layout-panel-dragover");
38369     },
38370 */
38371     updateBox : function(box)
38372     {
38373         if (!this.bodyEl) {
38374             return; // not rendered yet..
38375         }
38376         
38377         this.box = box;
38378         if(!this.collapsed){
38379             this.el.dom.style.left = box.x + "px";
38380             this.el.dom.style.top = box.y + "px";
38381             this.updateBody(box.width, box.height);
38382         }else{
38383             this.collapsedEl.dom.style.left = box.x + "px";
38384             this.collapsedEl.dom.style.top = box.y + "px";
38385             this.collapsedEl.setSize(box.width, box.height);
38386         }
38387         if(this.tabs){
38388             this.tabs.autoSizeTabs();
38389         }
38390     },
38391
38392     updateBody : function(w, h)
38393     {
38394         if(w !== null){
38395             this.el.setWidth(w);
38396             w -= this.el.getBorderWidth("rl");
38397             if(this.config.adjustments){
38398                 w += this.config.adjustments[0];
38399             }
38400         }
38401         if(h !== null && h > 0){
38402             this.el.setHeight(h);
38403             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38404             h -= this.el.getBorderWidth("tb");
38405             if(this.config.adjustments){
38406                 h += this.config.adjustments[1];
38407             }
38408             this.bodyEl.setHeight(h);
38409             if(this.tabs){
38410                 h = this.tabs.syncHeight(h);
38411             }
38412         }
38413         if(this.panelSize){
38414             w = w !== null ? w : this.panelSize.width;
38415             h = h !== null ? h : this.panelSize.height;
38416         }
38417         if(this.activePanel){
38418             var el = this.activePanel.getEl();
38419             w = w !== null ? w : el.getWidth();
38420             h = h !== null ? h : el.getHeight();
38421             this.panelSize = {width: w, height: h};
38422             this.activePanel.setSize(w, h);
38423         }
38424         if(Roo.isIE && this.tabs){
38425             this.tabs.el.repaint();
38426         }
38427     },
38428
38429     /**
38430      * Returns the container element for this region.
38431      * @return {Roo.Element}
38432      */
38433     getEl : function(){
38434         return this.el;
38435     },
38436
38437     /**
38438      * Hides this region.
38439      */
38440     hide : function(){
38441         //if(!this.collapsed){
38442             this.el.dom.style.left = "-2000px";
38443             this.el.hide();
38444         //}else{
38445          //   this.collapsedEl.dom.style.left = "-2000px";
38446          //   this.collapsedEl.hide();
38447        // }
38448         this.visible = false;
38449         this.fireEvent("visibilitychange", this, false);
38450     },
38451
38452     /**
38453      * Shows this region if it was previously hidden.
38454      */
38455     show : function(){
38456         //if(!this.collapsed){
38457             this.el.show();
38458         //}else{
38459         //    this.collapsedEl.show();
38460        // }
38461         this.visible = true;
38462         this.fireEvent("visibilitychange", this, true);
38463     },
38464 /*
38465     closeClicked : function(){
38466         if(this.activePanel){
38467             this.remove(this.activePanel);
38468         }
38469     },
38470
38471     collapseClick : function(e){
38472         if(this.isSlid){
38473            e.stopPropagation();
38474            this.slideIn();
38475         }else{
38476            e.stopPropagation();
38477            this.slideOut();
38478         }
38479     },
38480 */
38481     /**
38482      * Collapses this region.
38483      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38484      */
38485     /*
38486     collapse : function(skipAnim, skipCheck = false){
38487         if(this.collapsed) {
38488             return;
38489         }
38490         
38491         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38492             
38493             this.collapsed = true;
38494             if(this.split){
38495                 this.split.el.hide();
38496             }
38497             if(this.config.animate && skipAnim !== true){
38498                 this.fireEvent("invalidated", this);
38499                 this.animateCollapse();
38500             }else{
38501                 this.el.setLocation(-20000,-20000);
38502                 this.el.hide();
38503                 this.collapsedEl.show();
38504                 this.fireEvent("collapsed", this);
38505                 this.fireEvent("invalidated", this);
38506             }
38507         }
38508         
38509     },
38510 */
38511     animateCollapse : function(){
38512         // overridden
38513     },
38514
38515     /**
38516      * Expands this region if it was previously collapsed.
38517      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38518      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38519      */
38520     /*
38521     expand : function(e, skipAnim){
38522         if(e) {
38523             e.stopPropagation();
38524         }
38525         if(!this.collapsed || this.el.hasActiveFx()) {
38526             return;
38527         }
38528         if(this.isSlid){
38529             this.afterSlideIn();
38530             skipAnim = true;
38531         }
38532         this.collapsed = false;
38533         if(this.config.animate && skipAnim !== true){
38534             this.animateExpand();
38535         }else{
38536             this.el.show();
38537             if(this.split){
38538                 this.split.el.show();
38539             }
38540             this.collapsedEl.setLocation(-2000,-2000);
38541             this.collapsedEl.hide();
38542             this.fireEvent("invalidated", this);
38543             this.fireEvent("expanded", this);
38544         }
38545     },
38546 */
38547     animateExpand : function(){
38548         // overridden
38549     },
38550
38551     initTabs : function()
38552     {
38553         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38554         
38555         var ts = new Roo.bootstrap.panel.Tabs({
38556             el: this.bodyEl.dom,
38557             region : this,
38558             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38559             disableTooltips: this.config.disableTabTips,
38560             toolbar : this.config.toolbar
38561         });
38562         
38563         if(this.config.hideTabs){
38564             ts.stripWrap.setDisplayed(false);
38565         }
38566         this.tabs = ts;
38567         ts.resizeTabs = this.config.resizeTabs === true;
38568         ts.minTabWidth = this.config.minTabWidth || 40;
38569         ts.maxTabWidth = this.config.maxTabWidth || 250;
38570         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38571         ts.monitorResize = false;
38572         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38573         ts.bodyEl.addClass('roo-layout-tabs-body');
38574         this.panels.each(this.initPanelAsTab, this);
38575     },
38576
38577     initPanelAsTab : function(panel){
38578         var ti = this.tabs.addTab(
38579             panel.getEl().id,
38580             panel.getTitle(),
38581             null,
38582             this.config.closeOnTab && panel.isClosable(),
38583             panel.tpl
38584         );
38585         if(panel.tabTip !== undefined){
38586             ti.setTooltip(panel.tabTip);
38587         }
38588         ti.on("activate", function(){
38589               this.setActivePanel(panel);
38590         }, this);
38591         
38592         if(this.config.closeOnTab){
38593             ti.on("beforeclose", function(t, e){
38594                 e.cancel = true;
38595                 this.remove(panel);
38596             }, this);
38597         }
38598         
38599         panel.tabItem = ti;
38600         
38601         return ti;
38602     },
38603
38604     updatePanelTitle : function(panel, title)
38605     {
38606         if(this.activePanel == panel){
38607             this.updateTitle(title);
38608         }
38609         if(this.tabs){
38610             var ti = this.tabs.getTab(panel.getEl().id);
38611             ti.setText(title);
38612             if(panel.tabTip !== undefined){
38613                 ti.setTooltip(panel.tabTip);
38614             }
38615         }
38616     },
38617
38618     updateTitle : function(title){
38619         if(this.titleTextEl && !this.config.title){
38620             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38621         }
38622     },
38623
38624     setActivePanel : function(panel)
38625     {
38626         panel = this.getPanel(panel);
38627         if(this.activePanel && this.activePanel != panel){
38628             if(this.activePanel.setActiveState(false) === false){
38629                 return;
38630             }
38631         }
38632         this.activePanel = panel;
38633         panel.setActiveState(true);
38634         if(this.panelSize){
38635             panel.setSize(this.panelSize.width, this.panelSize.height);
38636         }
38637         if(this.closeBtn){
38638             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38639         }
38640         this.updateTitle(panel.getTitle());
38641         if(this.tabs){
38642             this.fireEvent("invalidated", this);
38643         }
38644         this.fireEvent("panelactivated", this, panel);
38645     },
38646
38647     /**
38648      * Shows the specified panel.
38649      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38650      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38651      */
38652     showPanel : function(panel)
38653     {
38654         panel = this.getPanel(panel);
38655         if(panel){
38656             if(this.tabs){
38657                 var tab = this.tabs.getTab(panel.getEl().id);
38658                 if(tab.isHidden()){
38659                     this.tabs.unhideTab(tab.id);
38660                 }
38661                 tab.activate();
38662             }else{
38663                 this.setActivePanel(panel);
38664             }
38665         }
38666         return panel;
38667     },
38668
38669     /**
38670      * Get the active panel for this region.
38671      * @return {Roo.ContentPanel} The active panel or null
38672      */
38673     getActivePanel : function(){
38674         return this.activePanel;
38675     },
38676
38677     validateVisibility : function(){
38678         if(this.panels.getCount() < 1){
38679             this.updateTitle("&#160;");
38680             this.closeBtn.hide();
38681             this.hide();
38682         }else{
38683             if(!this.isVisible()){
38684                 this.show();
38685             }
38686         }
38687     },
38688
38689     /**
38690      * Adds the passed ContentPanel(s) to this region.
38691      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38692      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38693      */
38694     add : function(panel)
38695     {
38696         if(arguments.length > 1){
38697             for(var i = 0, len = arguments.length; i < len; i++) {
38698                 this.add(arguments[i]);
38699             }
38700             return null;
38701         }
38702         
38703         // if we have not been rendered yet, then we can not really do much of this..
38704         if (!this.bodyEl) {
38705             this.unrendered_panels.push(panel);
38706             return panel;
38707         }
38708         
38709         
38710         
38711         
38712         if(this.hasPanel(panel)){
38713             this.showPanel(panel);
38714             return panel;
38715         }
38716         panel.setRegion(this);
38717         this.panels.add(panel);
38718        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38719             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38720             // and hide them... ???
38721             this.bodyEl.dom.appendChild(panel.getEl().dom);
38722             if(panel.background !== true){
38723                 this.setActivePanel(panel);
38724             }
38725             this.fireEvent("paneladded", this, panel);
38726             return panel;
38727         }
38728         */
38729         if(!this.tabs){
38730             this.initTabs();
38731         }else{
38732             this.initPanelAsTab(panel);
38733         }
38734         
38735         
38736         if(panel.background !== true){
38737             this.tabs.activate(panel.getEl().id);
38738         }
38739         this.fireEvent("paneladded", this, panel);
38740         return panel;
38741     },
38742
38743     /**
38744      * Hides the tab for the specified panel.
38745      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38746      */
38747     hidePanel : function(panel){
38748         if(this.tabs && (panel = this.getPanel(panel))){
38749             this.tabs.hideTab(panel.getEl().id);
38750         }
38751     },
38752
38753     /**
38754      * Unhides the tab for a previously hidden panel.
38755      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38756      */
38757     unhidePanel : function(panel){
38758         if(this.tabs && (panel = this.getPanel(panel))){
38759             this.tabs.unhideTab(panel.getEl().id);
38760         }
38761     },
38762
38763     clearPanels : function(){
38764         while(this.panels.getCount() > 0){
38765              this.remove(this.panels.first());
38766         }
38767     },
38768
38769     /**
38770      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38771      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38772      * @param {Boolean} preservePanel Overrides the config preservePanel option
38773      * @return {Roo.ContentPanel} The panel that was removed
38774      */
38775     remove : function(panel, preservePanel)
38776     {
38777         panel = this.getPanel(panel);
38778         if(!panel){
38779             return null;
38780         }
38781         var e = {};
38782         this.fireEvent("beforeremove", this, panel, e);
38783         if(e.cancel === true){
38784             return null;
38785         }
38786         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38787         var panelId = panel.getId();
38788         this.panels.removeKey(panelId);
38789         if(preservePanel){
38790             document.body.appendChild(panel.getEl().dom);
38791         }
38792         if(this.tabs){
38793             this.tabs.removeTab(panel.getEl().id);
38794         }else if (!preservePanel){
38795             this.bodyEl.dom.removeChild(panel.getEl().dom);
38796         }
38797         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38798             var p = this.panels.first();
38799             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38800             tempEl.appendChild(p.getEl().dom);
38801             this.bodyEl.update("");
38802             this.bodyEl.dom.appendChild(p.getEl().dom);
38803             tempEl = null;
38804             this.updateTitle(p.getTitle());
38805             this.tabs = null;
38806             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38807             this.setActivePanel(p);
38808         }
38809         panel.setRegion(null);
38810         if(this.activePanel == panel){
38811             this.activePanel = null;
38812         }
38813         if(this.config.autoDestroy !== false && preservePanel !== true){
38814             try{panel.destroy();}catch(e){}
38815         }
38816         this.fireEvent("panelremoved", this, panel);
38817         return panel;
38818     },
38819
38820     /**
38821      * Returns the TabPanel component used by this region
38822      * @return {Roo.TabPanel}
38823      */
38824     getTabs : function(){
38825         return this.tabs;
38826     },
38827
38828     createTool : function(parentEl, className){
38829         var btn = Roo.DomHelper.append(parentEl, {
38830             tag: "div",
38831             cls: "x-layout-tools-button",
38832             children: [ {
38833                 tag: "div",
38834                 cls: "roo-layout-tools-button-inner " + className,
38835                 html: "&#160;"
38836             }]
38837         }, true);
38838         btn.addClassOnOver("roo-layout-tools-button-over");
38839         return btn;
38840     }
38841 });/*
38842  * Based on:
38843  * Ext JS Library 1.1.1
38844  * Copyright(c) 2006-2007, Ext JS, LLC.
38845  *
38846  * Originally Released Under LGPL - original licence link has changed is not relivant.
38847  *
38848  * Fork - LGPL
38849  * <script type="text/javascript">
38850  */
38851  
38852
38853
38854 /**
38855  * @class Roo.SplitLayoutRegion
38856  * @extends Roo.LayoutRegion
38857  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38858  */
38859 Roo.bootstrap.layout.Split = function(config){
38860     this.cursor = config.cursor;
38861     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38862 };
38863
38864 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38865 {
38866     splitTip : "Drag to resize.",
38867     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38868     useSplitTips : false,
38869
38870     applyConfig : function(config){
38871         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38872     },
38873     
38874     onRender : function(ctr,pos) {
38875         
38876         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38877         if(!this.config.split){
38878             return;
38879         }
38880         if(!this.split){
38881             
38882             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38883                             tag: "div",
38884                             id: this.el.id + "-split",
38885                             cls: "roo-layout-split roo-layout-split-"+this.position,
38886                             html: "&#160;"
38887             });
38888             /** The SplitBar for this region 
38889             * @type Roo.SplitBar */
38890             // does not exist yet...
38891             Roo.log([this.position, this.orientation]);
38892             
38893             this.split = new Roo.bootstrap.SplitBar({
38894                 dragElement : splitEl,
38895                 resizingElement: this.el,
38896                 orientation : this.orientation
38897             });
38898             
38899             this.split.on("moved", this.onSplitMove, this);
38900             this.split.useShim = this.config.useShim === true;
38901             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38902             if(this.useSplitTips){
38903                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38904             }
38905             //if(config.collapsible){
38906             //    this.split.el.on("dblclick", this.collapse,  this);
38907             //}
38908         }
38909         if(typeof this.config.minSize != "undefined"){
38910             this.split.minSize = this.config.minSize;
38911         }
38912         if(typeof this.config.maxSize != "undefined"){
38913             this.split.maxSize = this.config.maxSize;
38914         }
38915         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38916             this.hideSplitter();
38917         }
38918         
38919     },
38920
38921     getHMaxSize : function(){
38922          var cmax = this.config.maxSize || 10000;
38923          var center = this.mgr.getRegion("center");
38924          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38925     },
38926
38927     getVMaxSize : function(){
38928          var cmax = this.config.maxSize || 10000;
38929          var center = this.mgr.getRegion("center");
38930          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38931     },
38932
38933     onSplitMove : function(split, newSize){
38934         this.fireEvent("resized", this, newSize);
38935     },
38936     
38937     /** 
38938      * Returns the {@link Roo.SplitBar} for this region.
38939      * @return {Roo.SplitBar}
38940      */
38941     getSplitBar : function(){
38942         return this.split;
38943     },
38944     
38945     hide : function(){
38946         this.hideSplitter();
38947         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38948     },
38949
38950     hideSplitter : function(){
38951         if(this.split){
38952             this.split.el.setLocation(-2000,-2000);
38953             this.split.el.hide();
38954         }
38955     },
38956
38957     show : function(){
38958         if(this.split){
38959             this.split.el.show();
38960         }
38961         Roo.bootstrap.layout.Split.superclass.show.call(this);
38962     },
38963     
38964     beforeSlide: function(){
38965         if(Roo.isGecko){// firefox overflow auto bug workaround
38966             this.bodyEl.clip();
38967             if(this.tabs) {
38968                 this.tabs.bodyEl.clip();
38969             }
38970             if(this.activePanel){
38971                 this.activePanel.getEl().clip();
38972                 
38973                 if(this.activePanel.beforeSlide){
38974                     this.activePanel.beforeSlide();
38975                 }
38976             }
38977         }
38978     },
38979     
38980     afterSlide : function(){
38981         if(Roo.isGecko){// firefox overflow auto bug workaround
38982             this.bodyEl.unclip();
38983             if(this.tabs) {
38984                 this.tabs.bodyEl.unclip();
38985             }
38986             if(this.activePanel){
38987                 this.activePanel.getEl().unclip();
38988                 if(this.activePanel.afterSlide){
38989                     this.activePanel.afterSlide();
38990                 }
38991             }
38992         }
38993     },
38994
38995     initAutoHide : function(){
38996         if(this.autoHide !== false){
38997             if(!this.autoHideHd){
38998                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38999                 this.autoHideHd = {
39000                     "mouseout": function(e){
39001                         if(!e.within(this.el, true)){
39002                             st.delay(500);
39003                         }
39004                     },
39005                     "mouseover" : function(e){
39006                         st.cancel();
39007                     },
39008                     scope : this
39009                 };
39010             }
39011             this.el.on(this.autoHideHd);
39012         }
39013     },
39014
39015     clearAutoHide : function(){
39016         if(this.autoHide !== false){
39017             this.el.un("mouseout", this.autoHideHd.mouseout);
39018             this.el.un("mouseover", this.autoHideHd.mouseover);
39019         }
39020     },
39021
39022     clearMonitor : function(){
39023         Roo.get(document).un("click", this.slideInIf, this);
39024     },
39025
39026     // these names are backwards but not changed for compat
39027     slideOut : function(){
39028         if(this.isSlid || this.el.hasActiveFx()){
39029             return;
39030         }
39031         this.isSlid = true;
39032         if(this.collapseBtn){
39033             this.collapseBtn.hide();
39034         }
39035         this.closeBtnState = this.closeBtn.getStyle('display');
39036         this.closeBtn.hide();
39037         if(this.stickBtn){
39038             this.stickBtn.show();
39039         }
39040         this.el.show();
39041         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39042         this.beforeSlide();
39043         this.el.setStyle("z-index", 10001);
39044         this.el.slideIn(this.getSlideAnchor(), {
39045             callback: function(){
39046                 this.afterSlide();
39047                 this.initAutoHide();
39048                 Roo.get(document).on("click", this.slideInIf, this);
39049                 this.fireEvent("slideshow", this);
39050             },
39051             scope: this,
39052             block: true
39053         });
39054     },
39055
39056     afterSlideIn : function(){
39057         this.clearAutoHide();
39058         this.isSlid = false;
39059         this.clearMonitor();
39060         this.el.setStyle("z-index", "");
39061         if(this.collapseBtn){
39062             this.collapseBtn.show();
39063         }
39064         this.closeBtn.setStyle('display', this.closeBtnState);
39065         if(this.stickBtn){
39066             this.stickBtn.hide();
39067         }
39068         this.fireEvent("slidehide", this);
39069     },
39070
39071     slideIn : function(cb){
39072         if(!this.isSlid || this.el.hasActiveFx()){
39073             Roo.callback(cb);
39074             return;
39075         }
39076         this.isSlid = false;
39077         this.beforeSlide();
39078         this.el.slideOut(this.getSlideAnchor(), {
39079             callback: function(){
39080                 this.el.setLeftTop(-10000, -10000);
39081                 this.afterSlide();
39082                 this.afterSlideIn();
39083                 Roo.callback(cb);
39084             },
39085             scope: this,
39086             block: true
39087         });
39088     },
39089     
39090     slideInIf : function(e){
39091         if(!e.within(this.el)){
39092             this.slideIn();
39093         }
39094     },
39095
39096     animateCollapse : function(){
39097         this.beforeSlide();
39098         this.el.setStyle("z-index", 20000);
39099         var anchor = this.getSlideAnchor();
39100         this.el.slideOut(anchor, {
39101             callback : function(){
39102                 this.el.setStyle("z-index", "");
39103                 this.collapsedEl.slideIn(anchor, {duration:.3});
39104                 this.afterSlide();
39105                 this.el.setLocation(-10000,-10000);
39106                 this.el.hide();
39107                 this.fireEvent("collapsed", this);
39108             },
39109             scope: this,
39110             block: true
39111         });
39112     },
39113
39114     animateExpand : function(){
39115         this.beforeSlide();
39116         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39117         this.el.setStyle("z-index", 20000);
39118         this.collapsedEl.hide({
39119             duration:.1
39120         });
39121         this.el.slideIn(this.getSlideAnchor(), {
39122             callback : function(){
39123                 this.el.setStyle("z-index", "");
39124                 this.afterSlide();
39125                 if(this.split){
39126                     this.split.el.show();
39127                 }
39128                 this.fireEvent("invalidated", this);
39129                 this.fireEvent("expanded", this);
39130             },
39131             scope: this,
39132             block: true
39133         });
39134     },
39135
39136     anchors : {
39137         "west" : "left",
39138         "east" : "right",
39139         "north" : "top",
39140         "south" : "bottom"
39141     },
39142
39143     sanchors : {
39144         "west" : "l",
39145         "east" : "r",
39146         "north" : "t",
39147         "south" : "b"
39148     },
39149
39150     canchors : {
39151         "west" : "tl-tr",
39152         "east" : "tr-tl",
39153         "north" : "tl-bl",
39154         "south" : "bl-tl"
39155     },
39156
39157     getAnchor : function(){
39158         return this.anchors[this.position];
39159     },
39160
39161     getCollapseAnchor : function(){
39162         return this.canchors[this.position];
39163     },
39164
39165     getSlideAnchor : function(){
39166         return this.sanchors[this.position];
39167     },
39168
39169     getAlignAdj : function(){
39170         var cm = this.cmargins;
39171         switch(this.position){
39172             case "west":
39173                 return [0, 0];
39174             break;
39175             case "east":
39176                 return [0, 0];
39177             break;
39178             case "north":
39179                 return [0, 0];
39180             break;
39181             case "south":
39182                 return [0, 0];
39183             break;
39184         }
39185     },
39186
39187     getExpandAdj : function(){
39188         var c = this.collapsedEl, cm = this.cmargins;
39189         switch(this.position){
39190             case "west":
39191                 return [-(cm.right+c.getWidth()+cm.left), 0];
39192             break;
39193             case "east":
39194                 return [cm.right+c.getWidth()+cm.left, 0];
39195             break;
39196             case "north":
39197                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39198             break;
39199             case "south":
39200                 return [0, cm.top+cm.bottom+c.getHeight()];
39201             break;
39202         }
39203     }
39204 });/*
39205  * Based on:
39206  * Ext JS Library 1.1.1
39207  * Copyright(c) 2006-2007, Ext JS, LLC.
39208  *
39209  * Originally Released Under LGPL - original licence link has changed is not relivant.
39210  *
39211  * Fork - LGPL
39212  * <script type="text/javascript">
39213  */
39214 /*
39215  * These classes are private internal classes
39216  */
39217 Roo.bootstrap.layout.Center = function(config){
39218     config.region = "center";
39219     Roo.bootstrap.layout.Region.call(this, config);
39220     this.visible = true;
39221     this.minWidth = config.minWidth || 20;
39222     this.minHeight = config.minHeight || 20;
39223 };
39224
39225 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39226     hide : function(){
39227         // center panel can't be hidden
39228     },
39229     
39230     show : function(){
39231         // center panel can't be hidden
39232     },
39233     
39234     getMinWidth: function(){
39235         return this.minWidth;
39236     },
39237     
39238     getMinHeight: function(){
39239         return this.minHeight;
39240     }
39241 });
39242
39243
39244
39245
39246  
39247
39248
39249
39250
39251
39252
39253 Roo.bootstrap.layout.North = function(config)
39254 {
39255     config.region = 'north';
39256     config.cursor = 'n-resize';
39257     
39258     Roo.bootstrap.layout.Split.call(this, config);
39259     
39260     
39261     if(this.split){
39262         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39263         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39264         this.split.el.addClass("roo-layout-split-v");
39265     }
39266     var size = config.initialSize || config.height;
39267     if(typeof size != "undefined"){
39268         this.el.setHeight(size);
39269     }
39270 };
39271 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39272 {
39273     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39274     
39275     
39276     
39277     getBox : function(){
39278         if(this.collapsed){
39279             return this.collapsedEl.getBox();
39280         }
39281         var box = this.el.getBox();
39282         if(this.split){
39283             box.height += this.split.el.getHeight();
39284         }
39285         return box;
39286     },
39287     
39288     updateBox : function(box){
39289         if(this.split && !this.collapsed){
39290             box.height -= this.split.el.getHeight();
39291             this.split.el.setLeft(box.x);
39292             this.split.el.setTop(box.y+box.height);
39293             this.split.el.setWidth(box.width);
39294         }
39295         if(this.collapsed){
39296             this.updateBody(box.width, null);
39297         }
39298         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39299     }
39300 });
39301
39302
39303
39304
39305
39306 Roo.bootstrap.layout.South = function(config){
39307     config.region = 'south';
39308     config.cursor = 's-resize';
39309     Roo.bootstrap.layout.Split.call(this, config);
39310     if(this.split){
39311         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39312         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39313         this.split.el.addClass("roo-layout-split-v");
39314     }
39315     var size = config.initialSize || config.height;
39316     if(typeof size != "undefined"){
39317         this.el.setHeight(size);
39318     }
39319 };
39320
39321 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39322     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39323     getBox : function(){
39324         if(this.collapsed){
39325             return this.collapsedEl.getBox();
39326         }
39327         var box = this.el.getBox();
39328         if(this.split){
39329             var sh = this.split.el.getHeight();
39330             box.height += sh;
39331             box.y -= sh;
39332         }
39333         return box;
39334     },
39335     
39336     updateBox : function(box){
39337         if(this.split && !this.collapsed){
39338             var sh = this.split.el.getHeight();
39339             box.height -= sh;
39340             box.y += sh;
39341             this.split.el.setLeft(box.x);
39342             this.split.el.setTop(box.y-sh);
39343             this.split.el.setWidth(box.width);
39344         }
39345         if(this.collapsed){
39346             this.updateBody(box.width, null);
39347         }
39348         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39349     }
39350 });
39351
39352 Roo.bootstrap.layout.East = function(config){
39353     config.region = "east";
39354     config.cursor = "e-resize";
39355     Roo.bootstrap.layout.Split.call(this, config);
39356     if(this.split){
39357         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39358         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39359         this.split.el.addClass("roo-layout-split-h");
39360     }
39361     var size = config.initialSize || config.width;
39362     if(typeof size != "undefined"){
39363         this.el.setWidth(size);
39364     }
39365 };
39366 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39367     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39368     getBox : function(){
39369         if(this.collapsed){
39370             return this.collapsedEl.getBox();
39371         }
39372         var box = this.el.getBox();
39373         if(this.split){
39374             var sw = this.split.el.getWidth();
39375             box.width += sw;
39376             box.x -= sw;
39377         }
39378         return box;
39379     },
39380
39381     updateBox : function(box){
39382         if(this.split && !this.collapsed){
39383             var sw = this.split.el.getWidth();
39384             box.width -= sw;
39385             this.split.el.setLeft(box.x);
39386             this.split.el.setTop(box.y);
39387             this.split.el.setHeight(box.height);
39388             box.x += sw;
39389         }
39390         if(this.collapsed){
39391             this.updateBody(null, box.height);
39392         }
39393         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39394     }
39395 });
39396
39397 Roo.bootstrap.layout.West = function(config){
39398     config.region = "west";
39399     config.cursor = "w-resize";
39400     
39401     Roo.bootstrap.layout.Split.call(this, config);
39402     if(this.split){
39403         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39404         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39405         this.split.el.addClass("roo-layout-split-h");
39406     }
39407     
39408 };
39409 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39410     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39411     
39412     onRender: function(ctr, pos)
39413     {
39414         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39415         var size = this.config.initialSize || this.config.width;
39416         if(typeof size != "undefined"){
39417             this.el.setWidth(size);
39418         }
39419     },
39420     
39421     getBox : function(){
39422         if(this.collapsed){
39423             return this.collapsedEl.getBox();
39424         }
39425         var box = this.el.getBox();
39426         if(this.split){
39427             box.width += this.split.el.getWidth();
39428         }
39429         return box;
39430     },
39431     
39432     updateBox : function(box){
39433         if(this.split && !this.collapsed){
39434             var sw = this.split.el.getWidth();
39435             box.width -= sw;
39436             this.split.el.setLeft(box.x+box.width);
39437             this.split.el.setTop(box.y);
39438             this.split.el.setHeight(box.height);
39439         }
39440         if(this.collapsed){
39441             this.updateBody(null, box.height);
39442         }
39443         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39444     }
39445 });Roo.namespace("Roo.bootstrap.panel");/*
39446  * Based on:
39447  * Ext JS Library 1.1.1
39448  * Copyright(c) 2006-2007, Ext JS, LLC.
39449  *
39450  * Originally Released Under LGPL - original licence link has changed is not relivant.
39451  *
39452  * Fork - LGPL
39453  * <script type="text/javascript">
39454  */
39455 /**
39456  * @class Roo.ContentPanel
39457  * @extends Roo.util.Observable
39458  * A basic ContentPanel element.
39459  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39460  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39461  * @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
39462  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39463  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39464  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39465  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39466  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39467  * @cfg {String} title          The title for this panel
39468  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39469  * @cfg {String} url            Calls {@link #setUrl} with this value
39470  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39471  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39472  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39473  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39474  * @cfg {Boolean} badges render the badges
39475  * @cfg {String} cls  extra classes to use  
39476  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39477
39478  * @constructor
39479  * Create a new ContentPanel.
39480  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39481  * @param {String/Object} config A string to set only the title or a config object
39482  * @param {String} content (optional) Set the HTML content for this panel
39483  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39484  */
39485 Roo.bootstrap.panel.Content = function( config){
39486     
39487     this.tpl = config.tpl || false;
39488     
39489     var el = config.el;
39490     var content = config.content;
39491
39492     if(config.autoCreate){ // xtype is available if this is called from factory
39493         el = Roo.id();
39494     }
39495     this.el = Roo.get(el);
39496     if(!this.el && config && config.autoCreate){
39497         if(typeof config.autoCreate == "object"){
39498             if(!config.autoCreate.id){
39499                 config.autoCreate.id = config.id||el;
39500             }
39501             this.el = Roo.DomHelper.append(document.body,
39502                         config.autoCreate, true);
39503         }else{
39504             var elcfg =  {
39505                 tag: "div",
39506                 cls: (config.cls || '') +
39507                     (config.background ? ' bg-' + config.background : '') +
39508                     " roo-layout-inactive-content",
39509                 id: config.id||el
39510             };
39511             if (config.html) {
39512                 elcfg.html = config.html;
39513                 
39514             }
39515                         
39516             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39517         }
39518     } 
39519     this.closable = false;
39520     this.loaded = false;
39521     this.active = false;
39522    
39523       
39524     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39525         
39526         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39527         
39528         this.wrapEl = this.el; //this.el.wrap();
39529         var ti = [];
39530         if (config.toolbar.items) {
39531             ti = config.toolbar.items ;
39532             delete config.toolbar.items ;
39533         }
39534         
39535         var nitems = [];
39536         this.toolbar.render(this.wrapEl, 'before');
39537         for(var i =0;i < ti.length;i++) {
39538           //  Roo.log(['add child', items[i]]);
39539             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39540         }
39541         this.toolbar.items = nitems;
39542         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39543         delete config.toolbar;
39544         
39545     }
39546     /*
39547     // xtype created footer. - not sure if will work as we normally have to render first..
39548     if (this.footer && !this.footer.el && this.footer.xtype) {
39549         if (!this.wrapEl) {
39550             this.wrapEl = this.el.wrap();
39551         }
39552     
39553         this.footer.container = this.wrapEl.createChild();
39554          
39555         this.footer = Roo.factory(this.footer, Roo);
39556         
39557     }
39558     */
39559     
39560      if(typeof config == "string"){
39561         this.title = config;
39562     }else{
39563         Roo.apply(this, config);
39564     }
39565     
39566     if(this.resizeEl){
39567         this.resizeEl = Roo.get(this.resizeEl, true);
39568     }else{
39569         this.resizeEl = this.el;
39570     }
39571     // handle view.xtype
39572     
39573  
39574     
39575     
39576     this.addEvents({
39577         /**
39578          * @event activate
39579          * Fires when this panel is activated. 
39580          * @param {Roo.ContentPanel} this
39581          */
39582         "activate" : true,
39583         /**
39584          * @event deactivate
39585          * Fires when this panel is activated. 
39586          * @param {Roo.ContentPanel} this
39587          */
39588         "deactivate" : true,
39589
39590         /**
39591          * @event resize
39592          * Fires when this panel is resized if fitToFrame is true.
39593          * @param {Roo.ContentPanel} this
39594          * @param {Number} width The width after any component adjustments
39595          * @param {Number} height The height after any component adjustments
39596          */
39597         "resize" : true,
39598         
39599          /**
39600          * @event render
39601          * Fires when this tab is created
39602          * @param {Roo.ContentPanel} this
39603          */
39604         "render" : true
39605         
39606         
39607         
39608     });
39609     
39610
39611     
39612     
39613     if(this.autoScroll){
39614         this.resizeEl.setStyle("overflow", "auto");
39615     } else {
39616         // fix randome scrolling
39617         //this.el.on('scroll', function() {
39618         //    Roo.log('fix random scolling');
39619         //    this.scrollTo('top',0); 
39620         //});
39621     }
39622     content = content || this.content;
39623     if(content){
39624         this.setContent(content);
39625     }
39626     if(config && config.url){
39627         this.setUrl(this.url, this.params, this.loadOnce);
39628     }
39629     
39630     
39631     
39632     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39633     
39634     if (this.view && typeof(this.view.xtype) != 'undefined') {
39635         this.view.el = this.el.appendChild(document.createElement("div"));
39636         this.view = Roo.factory(this.view); 
39637         this.view.render  &&  this.view.render(false, '');  
39638     }
39639     
39640     
39641     this.fireEvent('render', this);
39642 };
39643
39644 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39645     
39646     cls : '',
39647     background : '',
39648     
39649     tabTip : '',
39650     
39651     setRegion : function(region){
39652         this.region = region;
39653         this.setActiveClass(region && !this.background);
39654     },
39655     
39656     
39657     setActiveClass: function(state)
39658     {
39659         if(state){
39660            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39661            this.el.setStyle('position','relative');
39662         }else{
39663            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39664            this.el.setStyle('position', 'absolute');
39665         } 
39666     },
39667     
39668     /**
39669      * Returns the toolbar for this Panel if one was configured. 
39670      * @return {Roo.Toolbar} 
39671      */
39672     getToolbar : function(){
39673         return this.toolbar;
39674     },
39675     
39676     setActiveState : function(active)
39677     {
39678         this.active = active;
39679         this.setActiveClass(active);
39680         if(!active){
39681             if(this.fireEvent("deactivate", this) === false){
39682                 return false;
39683             }
39684             return true;
39685         }
39686         this.fireEvent("activate", this);
39687         return true;
39688     },
39689     /**
39690      * Updates this panel's element
39691      * @param {String} content The new content
39692      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39693     */
39694     setContent : function(content, loadScripts){
39695         this.el.update(content, loadScripts);
39696     },
39697
39698     ignoreResize : function(w, h){
39699         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39700             return true;
39701         }else{
39702             this.lastSize = {width: w, height: h};
39703             return false;
39704         }
39705     },
39706     /**
39707      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39708      * @return {Roo.UpdateManager} The UpdateManager
39709      */
39710     getUpdateManager : function(){
39711         return this.el.getUpdateManager();
39712     },
39713      /**
39714      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39715      * @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:
39716 <pre><code>
39717 panel.load({
39718     url: "your-url.php",
39719     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39720     callback: yourFunction,
39721     scope: yourObject, //(optional scope)
39722     discardUrl: false,
39723     nocache: false,
39724     text: "Loading...",
39725     timeout: 30,
39726     scripts: false
39727 });
39728 </code></pre>
39729      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39730      * 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.
39731      * @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}
39732      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39733      * @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.
39734      * @return {Roo.ContentPanel} this
39735      */
39736     load : function(){
39737         var um = this.el.getUpdateManager();
39738         um.update.apply(um, arguments);
39739         return this;
39740     },
39741
39742
39743     /**
39744      * 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.
39745      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39746      * @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)
39747      * @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)
39748      * @return {Roo.UpdateManager} The UpdateManager
39749      */
39750     setUrl : function(url, params, loadOnce){
39751         if(this.refreshDelegate){
39752             this.removeListener("activate", this.refreshDelegate);
39753         }
39754         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39755         this.on("activate", this.refreshDelegate);
39756         return this.el.getUpdateManager();
39757     },
39758     
39759     _handleRefresh : function(url, params, loadOnce){
39760         if(!loadOnce || !this.loaded){
39761             var updater = this.el.getUpdateManager();
39762             updater.update(url, params, this._setLoaded.createDelegate(this));
39763         }
39764     },
39765     
39766     _setLoaded : function(){
39767         this.loaded = true;
39768     }, 
39769     
39770     /**
39771      * Returns this panel's id
39772      * @return {String} 
39773      */
39774     getId : function(){
39775         return this.el.id;
39776     },
39777     
39778     /** 
39779      * Returns this panel's element - used by regiosn to add.
39780      * @return {Roo.Element} 
39781      */
39782     getEl : function(){
39783         return this.wrapEl || this.el;
39784     },
39785     
39786    
39787     
39788     adjustForComponents : function(width, height)
39789     {
39790         //Roo.log('adjustForComponents ');
39791         if(this.resizeEl != this.el){
39792             width -= this.el.getFrameWidth('lr');
39793             height -= this.el.getFrameWidth('tb');
39794         }
39795         if(this.toolbar){
39796             var te = this.toolbar.getEl();
39797             te.setWidth(width);
39798             height -= te.getHeight();
39799         }
39800         if(this.footer){
39801             var te = this.footer.getEl();
39802             te.setWidth(width);
39803             height -= te.getHeight();
39804         }
39805         
39806         
39807         if(this.adjustments){
39808             width += this.adjustments[0];
39809             height += this.adjustments[1];
39810         }
39811         return {"width": width, "height": height};
39812     },
39813     
39814     setSize : function(width, height){
39815         if(this.fitToFrame && !this.ignoreResize(width, height)){
39816             if(this.fitContainer && this.resizeEl != this.el){
39817                 this.el.setSize(width, height);
39818             }
39819             var size = this.adjustForComponents(width, height);
39820             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39821             this.fireEvent('resize', this, size.width, size.height);
39822         }
39823     },
39824     
39825     /**
39826      * Returns this panel's title
39827      * @return {String} 
39828      */
39829     getTitle : function(){
39830         
39831         if (typeof(this.title) != 'object') {
39832             return this.title;
39833         }
39834         
39835         var t = '';
39836         for (var k in this.title) {
39837             if (!this.title.hasOwnProperty(k)) {
39838                 continue;
39839             }
39840             
39841             if (k.indexOf('-') >= 0) {
39842                 var s = k.split('-');
39843                 for (var i = 0; i<s.length; i++) {
39844                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39845                 }
39846             } else {
39847                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39848             }
39849         }
39850         return t;
39851     },
39852     
39853     /**
39854      * Set this panel's title
39855      * @param {String} title
39856      */
39857     setTitle : function(title){
39858         this.title = title;
39859         if(this.region){
39860             this.region.updatePanelTitle(this, title);
39861         }
39862     },
39863     
39864     /**
39865      * Returns true is this panel was configured to be closable
39866      * @return {Boolean} 
39867      */
39868     isClosable : function(){
39869         return this.closable;
39870     },
39871     
39872     beforeSlide : function(){
39873         this.el.clip();
39874         this.resizeEl.clip();
39875     },
39876     
39877     afterSlide : function(){
39878         this.el.unclip();
39879         this.resizeEl.unclip();
39880     },
39881     
39882     /**
39883      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39884      *   Will fail silently if the {@link #setUrl} method has not been called.
39885      *   This does not activate the panel, just updates its content.
39886      */
39887     refresh : function(){
39888         if(this.refreshDelegate){
39889            this.loaded = false;
39890            this.refreshDelegate();
39891         }
39892     },
39893     
39894     /**
39895      * Destroys this panel
39896      */
39897     destroy : function(){
39898         this.el.removeAllListeners();
39899         var tempEl = document.createElement("span");
39900         tempEl.appendChild(this.el.dom);
39901         tempEl.innerHTML = "";
39902         this.el.remove();
39903         this.el = null;
39904     },
39905     
39906     /**
39907      * form - if the content panel contains a form - this is a reference to it.
39908      * @type {Roo.form.Form}
39909      */
39910     form : false,
39911     /**
39912      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39913      *    This contains a reference to it.
39914      * @type {Roo.View}
39915      */
39916     view : false,
39917     
39918       /**
39919      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39920      * <pre><code>
39921
39922 layout.addxtype({
39923        xtype : 'Form',
39924        items: [ .... ]
39925    }
39926 );
39927
39928 </code></pre>
39929      * @param {Object} cfg Xtype definition of item to add.
39930      */
39931     
39932     
39933     getChildContainer: function () {
39934         return this.getEl();
39935     }
39936     
39937     
39938     /*
39939         var  ret = new Roo.factory(cfg);
39940         return ret;
39941         
39942         
39943         // add form..
39944         if (cfg.xtype.match(/^Form$/)) {
39945             
39946             var el;
39947             //if (this.footer) {
39948             //    el = this.footer.container.insertSibling(false, 'before');
39949             //} else {
39950                 el = this.el.createChild();
39951             //}
39952
39953             this.form = new  Roo.form.Form(cfg);
39954             
39955             
39956             if ( this.form.allItems.length) {
39957                 this.form.render(el.dom);
39958             }
39959             return this.form;
39960         }
39961         // should only have one of theses..
39962         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39963             // views.. should not be just added - used named prop 'view''
39964             
39965             cfg.el = this.el.appendChild(document.createElement("div"));
39966             // factory?
39967             
39968             var ret = new Roo.factory(cfg);
39969              
39970              ret.render && ret.render(false, ''); // render blank..
39971             this.view = ret;
39972             return ret;
39973         }
39974         return false;
39975     }
39976     \*/
39977 });
39978  
39979 /**
39980  * @class Roo.bootstrap.panel.Grid
39981  * @extends Roo.bootstrap.panel.Content
39982  * @constructor
39983  * Create a new GridPanel.
39984  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39985  * @param {Object} config A the config object
39986   
39987  */
39988
39989
39990
39991 Roo.bootstrap.panel.Grid = function(config)
39992 {
39993     
39994       
39995     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39996         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39997
39998     config.el = this.wrapper;
39999     //this.el = this.wrapper;
40000     
40001       if (config.container) {
40002         // ctor'ed from a Border/panel.grid
40003         
40004         
40005         this.wrapper.setStyle("overflow", "hidden");
40006         this.wrapper.addClass('roo-grid-container');
40007
40008     }
40009     
40010     
40011     if(config.toolbar){
40012         var tool_el = this.wrapper.createChild();    
40013         this.toolbar = Roo.factory(config.toolbar);
40014         var ti = [];
40015         if (config.toolbar.items) {
40016             ti = config.toolbar.items ;
40017             delete config.toolbar.items ;
40018         }
40019         
40020         var nitems = [];
40021         this.toolbar.render(tool_el);
40022         for(var i =0;i < ti.length;i++) {
40023           //  Roo.log(['add child', items[i]]);
40024             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40025         }
40026         this.toolbar.items = nitems;
40027         
40028         delete config.toolbar;
40029     }
40030     
40031     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40032     config.grid.scrollBody = true;;
40033     config.grid.monitorWindowResize = false; // turn off autosizing
40034     config.grid.autoHeight = false;
40035     config.grid.autoWidth = false;
40036     
40037     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40038     
40039     if (config.background) {
40040         // render grid on panel activation (if panel background)
40041         this.on('activate', function(gp) {
40042             if (!gp.grid.rendered) {
40043                 gp.grid.render(this.wrapper);
40044                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40045             }
40046         });
40047             
40048     } else {
40049         this.grid.render(this.wrapper);
40050         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40051
40052     }
40053     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40054     // ??? needed ??? config.el = this.wrapper;
40055     
40056     
40057     
40058   
40059     // xtype created footer. - not sure if will work as we normally have to render first..
40060     if (this.footer && !this.footer.el && this.footer.xtype) {
40061         
40062         var ctr = this.grid.getView().getFooterPanel(true);
40063         this.footer.dataSource = this.grid.dataSource;
40064         this.footer = Roo.factory(this.footer, Roo);
40065         this.footer.render(ctr);
40066         
40067     }
40068     
40069     
40070     
40071     
40072      
40073 };
40074
40075 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40076     getId : function(){
40077         return this.grid.id;
40078     },
40079     
40080     /**
40081      * Returns the grid for this panel
40082      * @return {Roo.bootstrap.Table} 
40083      */
40084     getGrid : function(){
40085         return this.grid;    
40086     },
40087     
40088     setSize : function(width, height){
40089         if(!this.ignoreResize(width, height)){
40090             var grid = this.grid;
40091             var size = this.adjustForComponents(width, height);
40092             // tfoot is not a footer?
40093           
40094             
40095             var gridel = grid.getGridEl();
40096             gridel.setSize(size.width, size.height);
40097             
40098             var tbd = grid.getGridEl().select('tbody', true).first();
40099             var thd = grid.getGridEl().select('thead',true).first();
40100             var tbf= grid.getGridEl().select('tfoot', true).first();
40101
40102             if (tbf) {
40103                 size.height -= thd.getHeight();
40104             }
40105             if (thd) {
40106                 size.height -= thd.getHeight();
40107             }
40108             
40109             tbd.setSize(size.width, size.height );
40110             // this is for the account management tab -seems to work there.
40111             var thd = grid.getGridEl().select('thead',true).first();
40112             //if (tbd) {
40113             //    tbd.setSize(size.width, size.height - thd.getHeight());
40114             //}
40115              
40116             grid.autoSize();
40117         }
40118     },
40119      
40120     
40121     
40122     beforeSlide : function(){
40123         this.grid.getView().scroller.clip();
40124     },
40125     
40126     afterSlide : function(){
40127         this.grid.getView().scroller.unclip();
40128     },
40129     
40130     destroy : function(){
40131         this.grid.destroy();
40132         delete this.grid;
40133         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40134     }
40135 });
40136
40137 /**
40138  * @class Roo.bootstrap.panel.Nest
40139  * @extends Roo.bootstrap.panel.Content
40140  * @constructor
40141  * Create a new Panel, that can contain a layout.Border.
40142  * 
40143  * 
40144  * @param {Roo.BorderLayout} layout The layout for this panel
40145  * @param {String/Object} config A string to set only the title or a config object
40146  */
40147 Roo.bootstrap.panel.Nest = function(config)
40148 {
40149     // construct with only one argument..
40150     /* FIXME - implement nicer consturctors
40151     if (layout.layout) {
40152         config = layout;
40153         layout = config.layout;
40154         delete config.layout;
40155     }
40156     if (layout.xtype && !layout.getEl) {
40157         // then layout needs constructing..
40158         layout = Roo.factory(layout, Roo);
40159     }
40160     */
40161     
40162     config.el =  config.layout.getEl();
40163     
40164     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40165     
40166     config.layout.monitorWindowResize = false; // turn off autosizing
40167     this.layout = config.layout;
40168     this.layout.getEl().addClass("roo-layout-nested-layout");
40169     this.layout.parent = this;
40170     
40171     
40172     
40173     
40174 };
40175
40176 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40177
40178     setSize : function(width, height){
40179         if(!this.ignoreResize(width, height)){
40180             var size = this.adjustForComponents(width, height);
40181             var el = this.layout.getEl();
40182             if (size.height < 1) {
40183                 el.setWidth(size.width);   
40184             } else {
40185                 el.setSize(size.width, size.height);
40186             }
40187             var touch = el.dom.offsetWidth;
40188             this.layout.layout();
40189             // ie requires a double layout on the first pass
40190             if(Roo.isIE && !this.initialized){
40191                 this.initialized = true;
40192                 this.layout.layout();
40193             }
40194         }
40195     },
40196     
40197     // activate all subpanels if not currently active..
40198     
40199     setActiveState : function(active){
40200         this.active = active;
40201         this.setActiveClass(active);
40202         
40203         if(!active){
40204             this.fireEvent("deactivate", this);
40205             return;
40206         }
40207         
40208         this.fireEvent("activate", this);
40209         // not sure if this should happen before or after..
40210         if (!this.layout) {
40211             return; // should not happen..
40212         }
40213         var reg = false;
40214         for (var r in this.layout.regions) {
40215             reg = this.layout.getRegion(r);
40216             if (reg.getActivePanel()) {
40217                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40218                 reg.setActivePanel(reg.getActivePanel());
40219                 continue;
40220             }
40221             if (!reg.panels.length) {
40222                 continue;
40223             }
40224             reg.showPanel(reg.getPanel(0));
40225         }
40226         
40227         
40228         
40229         
40230     },
40231     
40232     /**
40233      * Returns the nested BorderLayout for this panel
40234      * @return {Roo.BorderLayout} 
40235      */
40236     getLayout : function(){
40237         return this.layout;
40238     },
40239     
40240      /**
40241      * Adds a xtype elements to the layout of the nested panel
40242      * <pre><code>
40243
40244 panel.addxtype({
40245        xtype : 'ContentPanel',
40246        region: 'west',
40247        items: [ .... ]
40248    }
40249 );
40250
40251 panel.addxtype({
40252         xtype : 'NestedLayoutPanel',
40253         region: 'west',
40254         layout: {
40255            center: { },
40256            west: { }   
40257         },
40258         items : [ ... list of content panels or nested layout panels.. ]
40259    }
40260 );
40261 </code></pre>
40262      * @param {Object} cfg Xtype definition of item to add.
40263      */
40264     addxtype : function(cfg) {
40265         return this.layout.addxtype(cfg);
40266     
40267     }
40268 });/*
40269  * Based on:
40270  * Ext JS Library 1.1.1
40271  * Copyright(c) 2006-2007, Ext JS, LLC.
40272  *
40273  * Originally Released Under LGPL - original licence link has changed is not relivant.
40274  *
40275  * Fork - LGPL
40276  * <script type="text/javascript">
40277  */
40278 /**
40279  * @class Roo.TabPanel
40280  * @extends Roo.util.Observable
40281  * A lightweight tab container.
40282  * <br><br>
40283  * Usage:
40284  * <pre><code>
40285 // basic tabs 1, built from existing content
40286 var tabs = new Roo.TabPanel("tabs1");
40287 tabs.addTab("script", "View Script");
40288 tabs.addTab("markup", "View Markup");
40289 tabs.activate("script");
40290
40291 // more advanced tabs, built from javascript
40292 var jtabs = new Roo.TabPanel("jtabs");
40293 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40294
40295 // set up the UpdateManager
40296 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40297 var updater = tab2.getUpdateManager();
40298 updater.setDefaultUrl("ajax1.htm");
40299 tab2.on('activate', updater.refresh, updater, true);
40300
40301 // Use setUrl for Ajax loading
40302 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40303 tab3.setUrl("ajax2.htm", null, true);
40304
40305 // Disabled tab
40306 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40307 tab4.disable();
40308
40309 jtabs.activate("jtabs-1");
40310  * </code></pre>
40311  * @constructor
40312  * Create a new TabPanel.
40313  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40314  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40315  */
40316 Roo.bootstrap.panel.Tabs = function(config){
40317     /**
40318     * The container element for this TabPanel.
40319     * @type Roo.Element
40320     */
40321     this.el = Roo.get(config.el);
40322     delete config.el;
40323     if(config){
40324         if(typeof config == "boolean"){
40325             this.tabPosition = config ? "bottom" : "top";
40326         }else{
40327             Roo.apply(this, config);
40328         }
40329     }
40330     
40331     if(this.tabPosition == "bottom"){
40332         // if tabs are at the bottom = create the body first.
40333         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40334         this.el.addClass("roo-tabs-bottom");
40335     }
40336     // next create the tabs holders
40337     
40338     if (this.tabPosition == "west"){
40339         
40340         var reg = this.region; // fake it..
40341         while (reg) {
40342             if (!reg.mgr.parent) {
40343                 break;
40344             }
40345             reg = reg.mgr.parent.region;
40346         }
40347         Roo.log("got nest?");
40348         Roo.log(reg);
40349         if (reg.mgr.getRegion('west')) {
40350             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40351             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40352             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40353             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40354             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40355         
40356             
40357         }
40358         
40359         
40360     } else {
40361      
40362         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40363         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40364         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40365         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40366     }
40367     
40368     
40369     if(Roo.isIE){
40370         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40371     }
40372     
40373     // finally - if tabs are at the top, then create the body last..
40374     if(this.tabPosition != "bottom"){
40375         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40376          * @type Roo.Element
40377          */
40378         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40379         this.el.addClass("roo-tabs-top");
40380     }
40381     this.items = [];
40382
40383     this.bodyEl.setStyle("position", "relative");
40384
40385     this.active = null;
40386     this.activateDelegate = this.activate.createDelegate(this);
40387
40388     this.addEvents({
40389         /**
40390          * @event tabchange
40391          * Fires when the active tab changes
40392          * @param {Roo.TabPanel} this
40393          * @param {Roo.TabPanelItem} activePanel The new active tab
40394          */
40395         "tabchange": true,
40396         /**
40397          * @event beforetabchange
40398          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40399          * @param {Roo.TabPanel} this
40400          * @param {Object} e Set cancel to true on this object to cancel the tab change
40401          * @param {Roo.TabPanelItem} tab The tab being changed to
40402          */
40403         "beforetabchange" : true
40404     });
40405
40406     Roo.EventManager.onWindowResize(this.onResize, this);
40407     this.cpad = this.el.getPadding("lr");
40408     this.hiddenCount = 0;
40409
40410
40411     // toolbar on the tabbar support...
40412     if (this.toolbar) {
40413         alert("no toolbar support yet");
40414         this.toolbar  = false;
40415         /*
40416         var tcfg = this.toolbar;
40417         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40418         this.toolbar = new Roo.Toolbar(tcfg);
40419         if (Roo.isSafari) {
40420             var tbl = tcfg.container.child('table', true);
40421             tbl.setAttribute('width', '100%');
40422         }
40423         */
40424         
40425     }
40426    
40427
40428
40429     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40430 };
40431
40432 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40433     /*
40434      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40435      */
40436     tabPosition : "top",
40437     /*
40438      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40439      */
40440     currentTabWidth : 0,
40441     /*
40442      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40443      */
40444     minTabWidth : 40,
40445     /*
40446      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40447      */
40448     maxTabWidth : 250,
40449     /*
40450      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40451      */
40452     preferredTabWidth : 175,
40453     /*
40454      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40455      */
40456     resizeTabs : false,
40457     /*
40458      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40459      */
40460     monitorResize : true,
40461     /*
40462      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40463      */
40464     toolbar : false,  // set by caller..
40465     
40466     region : false, /// set by caller
40467     
40468     disableTooltips : true, // not used yet...
40469
40470     /**
40471      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40472      * @param {String} id The id of the div to use <b>or create</b>
40473      * @param {String} text The text for the tab
40474      * @param {String} content (optional) Content to put in the TabPanelItem body
40475      * @param {Boolean} closable (optional) True to create a close icon on the tab
40476      * @return {Roo.TabPanelItem} The created TabPanelItem
40477      */
40478     addTab : function(id, text, content, closable, tpl)
40479     {
40480         var item = new Roo.bootstrap.panel.TabItem({
40481             panel: this,
40482             id : id,
40483             text : text,
40484             closable : closable,
40485             tpl : tpl
40486         });
40487         this.addTabItem(item);
40488         if(content){
40489             item.setContent(content);
40490         }
40491         return item;
40492     },
40493
40494     /**
40495      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40496      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40497      * @return {Roo.TabPanelItem}
40498      */
40499     getTab : function(id){
40500         return this.items[id];
40501     },
40502
40503     /**
40504      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40505      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40506      */
40507     hideTab : function(id){
40508         var t = this.items[id];
40509         if(!t.isHidden()){
40510            t.setHidden(true);
40511            this.hiddenCount++;
40512            this.autoSizeTabs();
40513         }
40514     },
40515
40516     /**
40517      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40518      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40519      */
40520     unhideTab : function(id){
40521         var t = this.items[id];
40522         if(t.isHidden()){
40523            t.setHidden(false);
40524            this.hiddenCount--;
40525            this.autoSizeTabs();
40526         }
40527     },
40528
40529     /**
40530      * Adds an existing {@link Roo.TabPanelItem}.
40531      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40532      */
40533     addTabItem : function(item)
40534     {
40535         this.items[item.id] = item;
40536         this.items.push(item);
40537         this.autoSizeTabs();
40538       //  if(this.resizeTabs){
40539     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40540   //         this.autoSizeTabs();
40541 //        }else{
40542 //            item.autoSize();
40543        // }
40544     },
40545
40546     /**
40547      * Removes a {@link Roo.TabPanelItem}.
40548      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40549      */
40550     removeTab : function(id){
40551         var items = this.items;
40552         var tab = items[id];
40553         if(!tab) { return; }
40554         var index = items.indexOf(tab);
40555         if(this.active == tab && items.length > 1){
40556             var newTab = this.getNextAvailable(index);
40557             if(newTab) {
40558                 newTab.activate();
40559             }
40560         }
40561         this.stripEl.dom.removeChild(tab.pnode.dom);
40562         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40563             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40564         }
40565         items.splice(index, 1);
40566         delete this.items[tab.id];
40567         tab.fireEvent("close", tab);
40568         tab.purgeListeners();
40569         this.autoSizeTabs();
40570     },
40571
40572     getNextAvailable : function(start){
40573         var items = this.items;
40574         var index = start;
40575         // look for a next tab that will slide over to
40576         // replace the one being removed
40577         while(index < items.length){
40578             var item = items[++index];
40579             if(item && !item.isHidden()){
40580                 return item;
40581             }
40582         }
40583         // if one isn't found select the previous tab (on the left)
40584         index = start;
40585         while(index >= 0){
40586             var item = items[--index];
40587             if(item && !item.isHidden()){
40588                 return item;
40589             }
40590         }
40591         return null;
40592     },
40593
40594     /**
40595      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40596      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40597      */
40598     disableTab : function(id){
40599         var tab = this.items[id];
40600         if(tab && this.active != tab){
40601             tab.disable();
40602         }
40603     },
40604
40605     /**
40606      * Enables a {@link Roo.TabPanelItem} that is disabled.
40607      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40608      */
40609     enableTab : function(id){
40610         var tab = this.items[id];
40611         tab.enable();
40612     },
40613
40614     /**
40615      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40616      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40617      * @return {Roo.TabPanelItem} The TabPanelItem.
40618      */
40619     activate : function(id)
40620     {
40621         //Roo.log('activite:'  + id);
40622         
40623         var tab = this.items[id];
40624         if(!tab){
40625             return null;
40626         }
40627         if(tab == this.active || tab.disabled){
40628             return tab;
40629         }
40630         var e = {};
40631         this.fireEvent("beforetabchange", this, e, tab);
40632         if(e.cancel !== true && !tab.disabled){
40633             if(this.active){
40634                 this.active.hide();
40635             }
40636             this.active = this.items[id];
40637             this.active.show();
40638             this.fireEvent("tabchange", this, this.active);
40639         }
40640         return tab;
40641     },
40642
40643     /**
40644      * Gets the active {@link Roo.TabPanelItem}.
40645      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40646      */
40647     getActiveTab : function(){
40648         return this.active;
40649     },
40650
40651     /**
40652      * Updates the tab body element to fit the height of the container element
40653      * for overflow scrolling
40654      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40655      */
40656     syncHeight : function(targetHeight){
40657         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40658         var bm = this.bodyEl.getMargins();
40659         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40660         this.bodyEl.setHeight(newHeight);
40661         return newHeight;
40662     },
40663
40664     onResize : function(){
40665         if(this.monitorResize){
40666             this.autoSizeTabs();
40667         }
40668     },
40669
40670     /**
40671      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40672      */
40673     beginUpdate : function(){
40674         this.updating = true;
40675     },
40676
40677     /**
40678      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40679      */
40680     endUpdate : function(){
40681         this.updating = false;
40682         this.autoSizeTabs();
40683     },
40684
40685     /**
40686      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40687      */
40688     autoSizeTabs : function()
40689     {
40690         var count = this.items.length;
40691         var vcount = count - this.hiddenCount;
40692         
40693         if (vcount < 2) {
40694             this.stripEl.hide();
40695         } else {
40696             this.stripEl.show();
40697         }
40698         
40699         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40700             return;
40701         }
40702         
40703         
40704         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40705         var availWidth = Math.floor(w / vcount);
40706         var b = this.stripBody;
40707         if(b.getWidth() > w){
40708             var tabs = this.items;
40709             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40710             if(availWidth < this.minTabWidth){
40711                 /*if(!this.sleft){    // incomplete scrolling code
40712                     this.createScrollButtons();
40713                 }
40714                 this.showScroll();
40715                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40716             }
40717         }else{
40718             if(this.currentTabWidth < this.preferredTabWidth){
40719                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40720             }
40721         }
40722     },
40723
40724     /**
40725      * Returns the number of tabs in this TabPanel.
40726      * @return {Number}
40727      */
40728      getCount : function(){
40729          return this.items.length;
40730      },
40731
40732     /**
40733      * Resizes all the tabs to the passed width
40734      * @param {Number} The new width
40735      */
40736     setTabWidth : function(width){
40737         this.currentTabWidth = width;
40738         for(var i = 0, len = this.items.length; i < len; i++) {
40739                 if(!this.items[i].isHidden()) {
40740                 this.items[i].setWidth(width);
40741             }
40742         }
40743     },
40744
40745     /**
40746      * Destroys this TabPanel
40747      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40748      */
40749     destroy : function(removeEl){
40750         Roo.EventManager.removeResizeListener(this.onResize, this);
40751         for(var i = 0, len = this.items.length; i < len; i++){
40752             this.items[i].purgeListeners();
40753         }
40754         if(removeEl === true){
40755             this.el.update("");
40756             this.el.remove();
40757         }
40758     },
40759     
40760     createStrip : function(container)
40761     {
40762         var strip = document.createElement("nav");
40763         strip.className = Roo.bootstrap.version == 4 ?
40764             "navbar-light bg-light" : 
40765             "navbar navbar-default"; //"x-tabs-wrap";
40766         container.appendChild(strip);
40767         return strip;
40768     },
40769     
40770     createStripList : function(strip)
40771     {
40772         // div wrapper for retard IE
40773         // returns the "tr" element.
40774         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40775         //'<div class="x-tabs-strip-wrap">'+
40776           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40777           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40778         return strip.firstChild; //.firstChild.firstChild.firstChild;
40779     },
40780     createBody : function(container)
40781     {
40782         var body = document.createElement("div");
40783         Roo.id(body, "tab-body");
40784         //Roo.fly(body).addClass("x-tabs-body");
40785         Roo.fly(body).addClass("tab-content");
40786         container.appendChild(body);
40787         return body;
40788     },
40789     createItemBody :function(bodyEl, id){
40790         var body = Roo.getDom(id);
40791         if(!body){
40792             body = document.createElement("div");
40793             body.id = id;
40794         }
40795         //Roo.fly(body).addClass("x-tabs-item-body");
40796         Roo.fly(body).addClass("tab-pane");
40797          bodyEl.insertBefore(body, bodyEl.firstChild);
40798         return body;
40799     },
40800     /** @private */
40801     createStripElements :  function(stripEl, text, closable, tpl)
40802     {
40803         var td = document.createElement("li"); // was td..
40804         td.className = 'nav-item';
40805         
40806         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40807         
40808         
40809         stripEl.appendChild(td);
40810         /*if(closable){
40811             td.className = "x-tabs-closable";
40812             if(!this.closeTpl){
40813                 this.closeTpl = new Roo.Template(
40814                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40815                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40816                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40817                 );
40818             }
40819             var el = this.closeTpl.overwrite(td, {"text": text});
40820             var close = el.getElementsByTagName("div")[0];
40821             var inner = el.getElementsByTagName("em")[0];
40822             return {"el": el, "close": close, "inner": inner};
40823         } else {
40824         */
40825         // not sure what this is..
40826 //            if(!this.tabTpl){
40827                 //this.tabTpl = new Roo.Template(
40828                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40829                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40830                 //);
40831 //                this.tabTpl = new Roo.Template(
40832 //                   '<a href="#">' +
40833 //                   '<span unselectable="on"' +
40834 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40835 //                            ' >{text}</span></a>'
40836 //                );
40837 //                
40838 //            }
40839
40840
40841             var template = tpl || this.tabTpl || false;
40842             
40843             if(!template){
40844                 template =  new Roo.Template(
40845                         Roo.bootstrap.version == 4 ? 
40846                             (
40847                                 '<a class="nav-link" href="#" unselectable="on"' +
40848                                      (this.disableTooltips ? '' : ' title="{text}"') +
40849                                      ' >{text}</a>'
40850                             ) : (
40851                                 '<a class="nav-link" href="#">' +
40852                                 '<span unselectable="on"' +
40853                                          (this.disableTooltips ? '' : ' title="{text}"') +
40854                                     ' >{text}</span></a>'
40855                             )
40856                 );
40857             }
40858             
40859             switch (typeof(template)) {
40860                 case 'object' :
40861                     break;
40862                 case 'string' :
40863                     template = new Roo.Template(template);
40864                     break;
40865                 default :
40866                     break;
40867             }
40868             
40869             var el = template.overwrite(td, {"text": text});
40870             
40871             var inner = el.getElementsByTagName("span")[0];
40872             
40873             return {"el": el, "inner": inner};
40874             
40875     }
40876         
40877     
40878 });
40879
40880 /**
40881  * @class Roo.TabPanelItem
40882  * @extends Roo.util.Observable
40883  * Represents an individual item (tab plus body) in a TabPanel.
40884  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40885  * @param {String} id The id of this TabPanelItem
40886  * @param {String} text The text for the tab of this TabPanelItem
40887  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40888  */
40889 Roo.bootstrap.panel.TabItem = function(config){
40890     /**
40891      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40892      * @type Roo.TabPanel
40893      */
40894     this.tabPanel = config.panel;
40895     /**
40896      * The id for this TabPanelItem
40897      * @type String
40898      */
40899     this.id = config.id;
40900     /** @private */
40901     this.disabled = false;
40902     /** @private */
40903     this.text = config.text;
40904     /** @private */
40905     this.loaded = false;
40906     this.closable = config.closable;
40907
40908     /**
40909      * The body element for this TabPanelItem.
40910      * @type Roo.Element
40911      */
40912     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40913     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40914     this.bodyEl.setStyle("display", "block");
40915     this.bodyEl.setStyle("zoom", "1");
40916     //this.hideAction();
40917
40918     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40919     /** @private */
40920     this.el = Roo.get(els.el);
40921     this.inner = Roo.get(els.inner, true);
40922      this.textEl = Roo.bootstrap.version == 4 ?
40923         this.el : Roo.get(this.el.dom.firstChild, true);
40924
40925     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40926     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40927
40928     
40929 //    this.el.on("mousedown", this.onTabMouseDown, this);
40930     this.el.on("click", this.onTabClick, this);
40931     /** @private */
40932     if(config.closable){
40933         var c = Roo.get(els.close, true);
40934         c.dom.title = this.closeText;
40935         c.addClassOnOver("close-over");
40936         c.on("click", this.closeClick, this);
40937      }
40938
40939     this.addEvents({
40940          /**
40941          * @event activate
40942          * Fires when this tab becomes the active tab.
40943          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40944          * @param {Roo.TabPanelItem} this
40945          */
40946         "activate": true,
40947         /**
40948          * @event beforeclose
40949          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40950          * @param {Roo.TabPanelItem} this
40951          * @param {Object} e Set cancel to true on this object to cancel the close.
40952          */
40953         "beforeclose": true,
40954         /**
40955          * @event close
40956          * Fires when this tab is closed.
40957          * @param {Roo.TabPanelItem} this
40958          */
40959          "close": true,
40960         /**
40961          * @event deactivate
40962          * Fires when this tab is no longer the active tab.
40963          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40964          * @param {Roo.TabPanelItem} this
40965          */
40966          "deactivate" : true
40967     });
40968     this.hidden = false;
40969
40970     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40971 };
40972
40973 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40974            {
40975     purgeListeners : function(){
40976        Roo.util.Observable.prototype.purgeListeners.call(this);
40977        this.el.removeAllListeners();
40978     },
40979     /**
40980      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40981      */
40982     show : function(){
40983         this.status_node.addClass("active");
40984         this.showAction();
40985         if(Roo.isOpera){
40986             this.tabPanel.stripWrap.repaint();
40987         }
40988         this.fireEvent("activate", this.tabPanel, this);
40989     },
40990
40991     /**
40992      * Returns true if this tab is the active tab.
40993      * @return {Boolean}
40994      */
40995     isActive : function(){
40996         return this.tabPanel.getActiveTab() == this;
40997     },
40998
40999     /**
41000      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41001      */
41002     hide : function(){
41003         this.status_node.removeClass("active");
41004         this.hideAction();
41005         this.fireEvent("deactivate", this.tabPanel, this);
41006     },
41007
41008     hideAction : function(){
41009         this.bodyEl.hide();
41010         this.bodyEl.setStyle("position", "absolute");
41011         this.bodyEl.setLeft("-20000px");
41012         this.bodyEl.setTop("-20000px");
41013     },
41014
41015     showAction : function(){
41016         this.bodyEl.setStyle("position", "relative");
41017         this.bodyEl.setTop("");
41018         this.bodyEl.setLeft("");
41019         this.bodyEl.show();
41020     },
41021
41022     /**
41023      * Set the tooltip for the tab.
41024      * @param {String} tooltip The tab's tooltip
41025      */
41026     setTooltip : function(text){
41027         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41028             this.textEl.dom.qtip = text;
41029             this.textEl.dom.removeAttribute('title');
41030         }else{
41031             this.textEl.dom.title = text;
41032         }
41033     },
41034
41035     onTabClick : function(e){
41036         e.preventDefault();
41037         this.tabPanel.activate(this.id);
41038     },
41039
41040     onTabMouseDown : function(e){
41041         e.preventDefault();
41042         this.tabPanel.activate(this.id);
41043     },
41044 /*
41045     getWidth : function(){
41046         return this.inner.getWidth();
41047     },
41048
41049     setWidth : function(width){
41050         var iwidth = width - this.linode.getPadding("lr");
41051         this.inner.setWidth(iwidth);
41052         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41053         this.linode.setWidth(width);
41054     },
41055 */
41056     /**
41057      * Show or hide the tab
41058      * @param {Boolean} hidden True to hide or false to show.
41059      */
41060     setHidden : function(hidden){
41061         this.hidden = hidden;
41062         this.linode.setStyle("display", hidden ? "none" : "");
41063     },
41064
41065     /**
41066      * Returns true if this tab is "hidden"
41067      * @return {Boolean}
41068      */
41069     isHidden : function(){
41070         return this.hidden;
41071     },
41072
41073     /**
41074      * Returns the text for this tab
41075      * @return {String}
41076      */
41077     getText : function(){
41078         return this.text;
41079     },
41080     /*
41081     autoSize : function(){
41082         //this.el.beginMeasure();
41083         this.textEl.setWidth(1);
41084         /*
41085          *  #2804 [new] Tabs in Roojs
41086          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41087          */
41088         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41089         //this.el.endMeasure();
41090     //},
41091
41092     /**
41093      * Sets the text for the tab (Note: this also sets the tooltip text)
41094      * @param {String} text The tab's text and tooltip
41095      */
41096     setText : function(text){
41097         this.text = text;
41098         this.textEl.update(text);
41099         this.setTooltip(text);
41100         //if(!this.tabPanel.resizeTabs){
41101         //    this.autoSize();
41102         //}
41103     },
41104     /**
41105      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41106      */
41107     activate : function(){
41108         this.tabPanel.activate(this.id);
41109     },
41110
41111     /**
41112      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41113      */
41114     disable : function(){
41115         if(this.tabPanel.active != this){
41116             this.disabled = true;
41117             this.status_node.addClass("disabled");
41118         }
41119     },
41120
41121     /**
41122      * Enables this TabPanelItem if it was previously disabled.
41123      */
41124     enable : function(){
41125         this.disabled = false;
41126         this.status_node.removeClass("disabled");
41127     },
41128
41129     /**
41130      * Sets the content for this TabPanelItem.
41131      * @param {String} content The content
41132      * @param {Boolean} loadScripts true to look for and load scripts
41133      */
41134     setContent : function(content, loadScripts){
41135         this.bodyEl.update(content, loadScripts);
41136     },
41137
41138     /**
41139      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41140      * @return {Roo.UpdateManager} The UpdateManager
41141      */
41142     getUpdateManager : function(){
41143         return this.bodyEl.getUpdateManager();
41144     },
41145
41146     /**
41147      * Set a URL to be used to load the content for this TabPanelItem.
41148      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41149      * @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)
41150      * @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)
41151      * @return {Roo.UpdateManager} The UpdateManager
41152      */
41153     setUrl : function(url, params, loadOnce){
41154         if(this.refreshDelegate){
41155             this.un('activate', this.refreshDelegate);
41156         }
41157         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41158         this.on("activate", this.refreshDelegate);
41159         return this.bodyEl.getUpdateManager();
41160     },
41161
41162     /** @private */
41163     _handleRefresh : function(url, params, loadOnce){
41164         if(!loadOnce || !this.loaded){
41165             var updater = this.bodyEl.getUpdateManager();
41166             updater.update(url, params, this._setLoaded.createDelegate(this));
41167         }
41168     },
41169
41170     /**
41171      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41172      *   Will fail silently if the setUrl method has not been called.
41173      *   This does not activate the panel, just updates its content.
41174      */
41175     refresh : function(){
41176         if(this.refreshDelegate){
41177            this.loaded = false;
41178            this.refreshDelegate();
41179         }
41180     },
41181
41182     /** @private */
41183     _setLoaded : function(){
41184         this.loaded = true;
41185     },
41186
41187     /** @private */
41188     closeClick : function(e){
41189         var o = {};
41190         e.stopEvent();
41191         this.fireEvent("beforeclose", this, o);
41192         if(o.cancel !== true){
41193             this.tabPanel.removeTab(this.id);
41194         }
41195     },
41196     /**
41197      * The text displayed in the tooltip for the close icon.
41198      * @type String
41199      */
41200     closeText : "Close this tab"
41201 });
41202 /**
41203 *    This script refer to:
41204 *    Title: International Telephone Input
41205 *    Author: Jack O'Connor
41206 *    Code version:  v12.1.12
41207 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41208 **/
41209
41210 Roo.bootstrap.PhoneInputData = function() {
41211     var d = [
41212       [
41213         "Afghanistan (‫افغانستان‬‎)",
41214         "af",
41215         "93"
41216       ],
41217       [
41218         "Albania (Shqipëri)",
41219         "al",
41220         "355"
41221       ],
41222       [
41223         "Algeria (‫الجزائر‬‎)",
41224         "dz",
41225         "213"
41226       ],
41227       [
41228         "American Samoa",
41229         "as",
41230         "1684"
41231       ],
41232       [
41233         "Andorra",
41234         "ad",
41235         "376"
41236       ],
41237       [
41238         "Angola",
41239         "ao",
41240         "244"
41241       ],
41242       [
41243         "Anguilla",
41244         "ai",
41245         "1264"
41246       ],
41247       [
41248         "Antigua and Barbuda",
41249         "ag",
41250         "1268"
41251       ],
41252       [
41253         "Argentina",
41254         "ar",
41255         "54"
41256       ],
41257       [
41258         "Armenia (Հայաստան)",
41259         "am",
41260         "374"
41261       ],
41262       [
41263         "Aruba",
41264         "aw",
41265         "297"
41266       ],
41267       [
41268         "Australia",
41269         "au",
41270         "61",
41271         0
41272       ],
41273       [
41274         "Austria (Österreich)",
41275         "at",
41276         "43"
41277       ],
41278       [
41279         "Azerbaijan (Azərbaycan)",
41280         "az",
41281         "994"
41282       ],
41283       [
41284         "Bahamas",
41285         "bs",
41286         "1242"
41287       ],
41288       [
41289         "Bahrain (‫البحرين‬‎)",
41290         "bh",
41291         "973"
41292       ],
41293       [
41294         "Bangladesh (বাংলাদেশ)",
41295         "bd",
41296         "880"
41297       ],
41298       [
41299         "Barbados",
41300         "bb",
41301         "1246"
41302       ],
41303       [
41304         "Belarus (Беларусь)",
41305         "by",
41306         "375"
41307       ],
41308       [
41309         "Belgium (België)",
41310         "be",
41311         "32"
41312       ],
41313       [
41314         "Belize",
41315         "bz",
41316         "501"
41317       ],
41318       [
41319         "Benin (Bénin)",
41320         "bj",
41321         "229"
41322       ],
41323       [
41324         "Bermuda",
41325         "bm",
41326         "1441"
41327       ],
41328       [
41329         "Bhutan (འབྲུག)",
41330         "bt",
41331         "975"
41332       ],
41333       [
41334         "Bolivia",
41335         "bo",
41336         "591"
41337       ],
41338       [
41339         "Bosnia and Herzegovina (Босна и Херцеговина)",
41340         "ba",
41341         "387"
41342       ],
41343       [
41344         "Botswana",
41345         "bw",
41346         "267"
41347       ],
41348       [
41349         "Brazil (Brasil)",
41350         "br",
41351         "55"
41352       ],
41353       [
41354         "British Indian Ocean Territory",
41355         "io",
41356         "246"
41357       ],
41358       [
41359         "British Virgin Islands",
41360         "vg",
41361         "1284"
41362       ],
41363       [
41364         "Brunei",
41365         "bn",
41366         "673"
41367       ],
41368       [
41369         "Bulgaria (България)",
41370         "bg",
41371         "359"
41372       ],
41373       [
41374         "Burkina Faso",
41375         "bf",
41376         "226"
41377       ],
41378       [
41379         "Burundi (Uburundi)",
41380         "bi",
41381         "257"
41382       ],
41383       [
41384         "Cambodia (កម្ពុជា)",
41385         "kh",
41386         "855"
41387       ],
41388       [
41389         "Cameroon (Cameroun)",
41390         "cm",
41391         "237"
41392       ],
41393       [
41394         "Canada",
41395         "ca",
41396         "1",
41397         1,
41398         ["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"]
41399       ],
41400       [
41401         "Cape Verde (Kabu Verdi)",
41402         "cv",
41403         "238"
41404       ],
41405       [
41406         "Caribbean Netherlands",
41407         "bq",
41408         "599",
41409         1
41410       ],
41411       [
41412         "Cayman Islands",
41413         "ky",
41414         "1345"
41415       ],
41416       [
41417         "Central African Republic (République centrafricaine)",
41418         "cf",
41419         "236"
41420       ],
41421       [
41422         "Chad (Tchad)",
41423         "td",
41424         "235"
41425       ],
41426       [
41427         "Chile",
41428         "cl",
41429         "56"
41430       ],
41431       [
41432         "China (中国)",
41433         "cn",
41434         "86"
41435       ],
41436       [
41437         "Christmas Island",
41438         "cx",
41439         "61",
41440         2
41441       ],
41442       [
41443         "Cocos (Keeling) Islands",
41444         "cc",
41445         "61",
41446         1
41447       ],
41448       [
41449         "Colombia",
41450         "co",
41451         "57"
41452       ],
41453       [
41454         "Comoros (‫جزر القمر‬‎)",
41455         "km",
41456         "269"
41457       ],
41458       [
41459         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41460         "cd",
41461         "243"
41462       ],
41463       [
41464         "Congo (Republic) (Congo-Brazzaville)",
41465         "cg",
41466         "242"
41467       ],
41468       [
41469         "Cook Islands",
41470         "ck",
41471         "682"
41472       ],
41473       [
41474         "Costa Rica",
41475         "cr",
41476         "506"
41477       ],
41478       [
41479         "Côte d’Ivoire",
41480         "ci",
41481         "225"
41482       ],
41483       [
41484         "Croatia (Hrvatska)",
41485         "hr",
41486         "385"
41487       ],
41488       [
41489         "Cuba",
41490         "cu",
41491         "53"
41492       ],
41493       [
41494         "Curaçao",
41495         "cw",
41496         "599",
41497         0
41498       ],
41499       [
41500         "Cyprus (Κύπρος)",
41501         "cy",
41502         "357"
41503       ],
41504       [
41505         "Czech Republic (Česká republika)",
41506         "cz",
41507         "420"
41508       ],
41509       [
41510         "Denmark (Danmark)",
41511         "dk",
41512         "45"
41513       ],
41514       [
41515         "Djibouti",
41516         "dj",
41517         "253"
41518       ],
41519       [
41520         "Dominica",
41521         "dm",
41522         "1767"
41523       ],
41524       [
41525         "Dominican Republic (República Dominicana)",
41526         "do",
41527         "1",
41528         2,
41529         ["809", "829", "849"]
41530       ],
41531       [
41532         "Ecuador",
41533         "ec",
41534         "593"
41535       ],
41536       [
41537         "Egypt (‫مصر‬‎)",
41538         "eg",
41539         "20"
41540       ],
41541       [
41542         "El Salvador",
41543         "sv",
41544         "503"
41545       ],
41546       [
41547         "Equatorial Guinea (Guinea Ecuatorial)",
41548         "gq",
41549         "240"
41550       ],
41551       [
41552         "Eritrea",
41553         "er",
41554         "291"
41555       ],
41556       [
41557         "Estonia (Eesti)",
41558         "ee",
41559         "372"
41560       ],
41561       [
41562         "Ethiopia",
41563         "et",
41564         "251"
41565       ],
41566       [
41567         "Falkland Islands (Islas Malvinas)",
41568         "fk",
41569         "500"
41570       ],
41571       [
41572         "Faroe Islands (Føroyar)",
41573         "fo",
41574         "298"
41575       ],
41576       [
41577         "Fiji",
41578         "fj",
41579         "679"
41580       ],
41581       [
41582         "Finland (Suomi)",
41583         "fi",
41584         "358",
41585         0
41586       ],
41587       [
41588         "France",
41589         "fr",
41590         "33"
41591       ],
41592       [
41593         "French Guiana (Guyane française)",
41594         "gf",
41595         "594"
41596       ],
41597       [
41598         "French Polynesia (Polynésie française)",
41599         "pf",
41600         "689"
41601       ],
41602       [
41603         "Gabon",
41604         "ga",
41605         "241"
41606       ],
41607       [
41608         "Gambia",
41609         "gm",
41610         "220"
41611       ],
41612       [
41613         "Georgia (საქართველო)",
41614         "ge",
41615         "995"
41616       ],
41617       [
41618         "Germany (Deutschland)",
41619         "de",
41620         "49"
41621       ],
41622       [
41623         "Ghana (Gaana)",
41624         "gh",
41625         "233"
41626       ],
41627       [
41628         "Gibraltar",
41629         "gi",
41630         "350"
41631       ],
41632       [
41633         "Greece (Ελλάδα)",
41634         "gr",
41635         "30"
41636       ],
41637       [
41638         "Greenland (Kalaallit Nunaat)",
41639         "gl",
41640         "299"
41641       ],
41642       [
41643         "Grenada",
41644         "gd",
41645         "1473"
41646       ],
41647       [
41648         "Guadeloupe",
41649         "gp",
41650         "590",
41651         0
41652       ],
41653       [
41654         "Guam",
41655         "gu",
41656         "1671"
41657       ],
41658       [
41659         "Guatemala",
41660         "gt",
41661         "502"
41662       ],
41663       [
41664         "Guernsey",
41665         "gg",
41666         "44",
41667         1
41668       ],
41669       [
41670         "Guinea (Guinée)",
41671         "gn",
41672         "224"
41673       ],
41674       [
41675         "Guinea-Bissau (Guiné Bissau)",
41676         "gw",
41677         "245"
41678       ],
41679       [
41680         "Guyana",
41681         "gy",
41682         "592"
41683       ],
41684       [
41685         "Haiti",
41686         "ht",
41687         "509"
41688       ],
41689       [
41690         "Honduras",
41691         "hn",
41692         "504"
41693       ],
41694       [
41695         "Hong Kong (香港)",
41696         "hk",
41697         "852"
41698       ],
41699       [
41700         "Hungary (Magyarország)",
41701         "hu",
41702         "36"
41703       ],
41704       [
41705         "Iceland (Ísland)",
41706         "is",
41707         "354"
41708       ],
41709       [
41710         "India (भारत)",
41711         "in",
41712         "91"
41713       ],
41714       [
41715         "Indonesia",
41716         "id",
41717         "62"
41718       ],
41719       [
41720         "Iran (‫ایران‬‎)",
41721         "ir",
41722         "98"
41723       ],
41724       [
41725         "Iraq (‫العراق‬‎)",
41726         "iq",
41727         "964"
41728       ],
41729       [
41730         "Ireland",
41731         "ie",
41732         "353"
41733       ],
41734       [
41735         "Isle of Man",
41736         "im",
41737         "44",
41738         2
41739       ],
41740       [
41741         "Israel (‫ישראל‬‎)",
41742         "il",
41743         "972"
41744       ],
41745       [
41746         "Italy (Italia)",
41747         "it",
41748         "39",
41749         0
41750       ],
41751       [
41752         "Jamaica",
41753         "jm",
41754         "1876"
41755       ],
41756       [
41757         "Japan (日本)",
41758         "jp",
41759         "81"
41760       ],
41761       [
41762         "Jersey",
41763         "je",
41764         "44",
41765         3
41766       ],
41767       [
41768         "Jordan (‫الأردن‬‎)",
41769         "jo",
41770         "962"
41771       ],
41772       [
41773         "Kazakhstan (Казахстан)",
41774         "kz",
41775         "7",
41776         1
41777       ],
41778       [
41779         "Kenya",
41780         "ke",
41781         "254"
41782       ],
41783       [
41784         "Kiribati",
41785         "ki",
41786         "686"
41787       ],
41788       [
41789         "Kosovo",
41790         "xk",
41791         "383"
41792       ],
41793       [
41794         "Kuwait (‫الكويت‬‎)",
41795         "kw",
41796         "965"
41797       ],
41798       [
41799         "Kyrgyzstan (Кыргызстан)",
41800         "kg",
41801         "996"
41802       ],
41803       [
41804         "Laos (ລາວ)",
41805         "la",
41806         "856"
41807       ],
41808       [
41809         "Latvia (Latvija)",
41810         "lv",
41811         "371"
41812       ],
41813       [
41814         "Lebanon (‫لبنان‬‎)",
41815         "lb",
41816         "961"
41817       ],
41818       [
41819         "Lesotho",
41820         "ls",
41821         "266"
41822       ],
41823       [
41824         "Liberia",
41825         "lr",
41826         "231"
41827       ],
41828       [
41829         "Libya (‫ليبيا‬‎)",
41830         "ly",
41831         "218"
41832       ],
41833       [
41834         "Liechtenstein",
41835         "li",
41836         "423"
41837       ],
41838       [
41839         "Lithuania (Lietuva)",
41840         "lt",
41841         "370"
41842       ],
41843       [
41844         "Luxembourg",
41845         "lu",
41846         "352"
41847       ],
41848       [
41849         "Macau (澳門)",
41850         "mo",
41851         "853"
41852       ],
41853       [
41854         "Macedonia (FYROM) (Македонија)",
41855         "mk",
41856         "389"
41857       ],
41858       [
41859         "Madagascar (Madagasikara)",
41860         "mg",
41861         "261"
41862       ],
41863       [
41864         "Malawi",
41865         "mw",
41866         "265"
41867       ],
41868       [
41869         "Malaysia",
41870         "my",
41871         "60"
41872       ],
41873       [
41874         "Maldives",
41875         "mv",
41876         "960"
41877       ],
41878       [
41879         "Mali",
41880         "ml",
41881         "223"
41882       ],
41883       [
41884         "Malta",
41885         "mt",
41886         "356"
41887       ],
41888       [
41889         "Marshall Islands",
41890         "mh",
41891         "692"
41892       ],
41893       [
41894         "Martinique",
41895         "mq",
41896         "596"
41897       ],
41898       [
41899         "Mauritania (‫موريتانيا‬‎)",
41900         "mr",
41901         "222"
41902       ],
41903       [
41904         "Mauritius (Moris)",
41905         "mu",
41906         "230"
41907       ],
41908       [
41909         "Mayotte",
41910         "yt",
41911         "262",
41912         1
41913       ],
41914       [
41915         "Mexico (México)",
41916         "mx",
41917         "52"
41918       ],
41919       [
41920         "Micronesia",
41921         "fm",
41922         "691"
41923       ],
41924       [
41925         "Moldova (Republica Moldova)",
41926         "md",
41927         "373"
41928       ],
41929       [
41930         "Monaco",
41931         "mc",
41932         "377"
41933       ],
41934       [
41935         "Mongolia (Монгол)",
41936         "mn",
41937         "976"
41938       ],
41939       [
41940         "Montenegro (Crna Gora)",
41941         "me",
41942         "382"
41943       ],
41944       [
41945         "Montserrat",
41946         "ms",
41947         "1664"
41948       ],
41949       [
41950         "Morocco (‫المغرب‬‎)",
41951         "ma",
41952         "212",
41953         0
41954       ],
41955       [
41956         "Mozambique (Moçambique)",
41957         "mz",
41958         "258"
41959       ],
41960       [
41961         "Myanmar (Burma) (မြန်မာ)",
41962         "mm",
41963         "95"
41964       ],
41965       [
41966         "Namibia (Namibië)",
41967         "na",
41968         "264"
41969       ],
41970       [
41971         "Nauru",
41972         "nr",
41973         "674"
41974       ],
41975       [
41976         "Nepal (नेपाल)",
41977         "np",
41978         "977"
41979       ],
41980       [
41981         "Netherlands (Nederland)",
41982         "nl",
41983         "31"
41984       ],
41985       [
41986         "New Caledonia (Nouvelle-Calédonie)",
41987         "nc",
41988         "687"
41989       ],
41990       [
41991         "New Zealand",
41992         "nz",
41993         "64"
41994       ],
41995       [
41996         "Nicaragua",
41997         "ni",
41998         "505"
41999       ],
42000       [
42001         "Niger (Nijar)",
42002         "ne",
42003         "227"
42004       ],
42005       [
42006         "Nigeria",
42007         "ng",
42008         "234"
42009       ],
42010       [
42011         "Niue",
42012         "nu",
42013         "683"
42014       ],
42015       [
42016         "Norfolk Island",
42017         "nf",
42018         "672"
42019       ],
42020       [
42021         "North Korea (조선 민주주의 인민 공화국)",
42022         "kp",
42023         "850"
42024       ],
42025       [
42026         "Northern Mariana Islands",
42027         "mp",
42028         "1670"
42029       ],
42030       [
42031         "Norway (Norge)",
42032         "no",
42033         "47",
42034         0
42035       ],
42036       [
42037         "Oman (‫عُمان‬‎)",
42038         "om",
42039         "968"
42040       ],
42041       [
42042         "Pakistan (‫پاکستان‬‎)",
42043         "pk",
42044         "92"
42045       ],
42046       [
42047         "Palau",
42048         "pw",
42049         "680"
42050       ],
42051       [
42052         "Palestine (‫فلسطين‬‎)",
42053         "ps",
42054         "970"
42055       ],
42056       [
42057         "Panama (Panamá)",
42058         "pa",
42059         "507"
42060       ],
42061       [
42062         "Papua New Guinea",
42063         "pg",
42064         "675"
42065       ],
42066       [
42067         "Paraguay",
42068         "py",
42069         "595"
42070       ],
42071       [
42072         "Peru (Perú)",
42073         "pe",
42074         "51"
42075       ],
42076       [
42077         "Philippines",
42078         "ph",
42079         "63"
42080       ],
42081       [
42082         "Poland (Polska)",
42083         "pl",
42084         "48"
42085       ],
42086       [
42087         "Portugal",
42088         "pt",
42089         "351"
42090       ],
42091       [
42092         "Puerto Rico",
42093         "pr",
42094         "1",
42095         3,
42096         ["787", "939"]
42097       ],
42098       [
42099         "Qatar (‫قطر‬‎)",
42100         "qa",
42101         "974"
42102       ],
42103       [
42104         "Réunion (La Réunion)",
42105         "re",
42106         "262",
42107         0
42108       ],
42109       [
42110         "Romania (România)",
42111         "ro",
42112         "40"
42113       ],
42114       [
42115         "Russia (Россия)",
42116         "ru",
42117         "7",
42118         0
42119       ],
42120       [
42121         "Rwanda",
42122         "rw",
42123         "250"
42124       ],
42125       [
42126         "Saint Barthélemy",
42127         "bl",
42128         "590",
42129         1
42130       ],
42131       [
42132         "Saint Helena",
42133         "sh",
42134         "290"
42135       ],
42136       [
42137         "Saint Kitts and Nevis",
42138         "kn",
42139         "1869"
42140       ],
42141       [
42142         "Saint Lucia",
42143         "lc",
42144         "1758"
42145       ],
42146       [
42147         "Saint Martin (Saint-Martin (partie française))",
42148         "mf",
42149         "590",
42150         2
42151       ],
42152       [
42153         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42154         "pm",
42155         "508"
42156       ],
42157       [
42158         "Saint Vincent and the Grenadines",
42159         "vc",
42160         "1784"
42161       ],
42162       [
42163         "Samoa",
42164         "ws",
42165         "685"
42166       ],
42167       [
42168         "San Marino",
42169         "sm",
42170         "378"
42171       ],
42172       [
42173         "São Tomé and Príncipe (São Tomé e Príncipe)",
42174         "st",
42175         "239"
42176       ],
42177       [
42178         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42179         "sa",
42180         "966"
42181       ],
42182       [
42183         "Senegal (Sénégal)",
42184         "sn",
42185         "221"
42186       ],
42187       [
42188         "Serbia (Србија)",
42189         "rs",
42190         "381"
42191       ],
42192       [
42193         "Seychelles",
42194         "sc",
42195         "248"
42196       ],
42197       [
42198         "Sierra Leone",
42199         "sl",
42200         "232"
42201       ],
42202       [
42203         "Singapore",
42204         "sg",
42205         "65"
42206       ],
42207       [
42208         "Sint Maarten",
42209         "sx",
42210         "1721"
42211       ],
42212       [
42213         "Slovakia (Slovensko)",
42214         "sk",
42215         "421"
42216       ],
42217       [
42218         "Slovenia (Slovenija)",
42219         "si",
42220         "386"
42221       ],
42222       [
42223         "Solomon Islands",
42224         "sb",
42225         "677"
42226       ],
42227       [
42228         "Somalia (Soomaaliya)",
42229         "so",
42230         "252"
42231       ],
42232       [
42233         "South Africa",
42234         "za",
42235         "27"
42236       ],
42237       [
42238         "South Korea (대한민국)",
42239         "kr",
42240         "82"
42241       ],
42242       [
42243         "South Sudan (‫جنوب السودان‬‎)",
42244         "ss",
42245         "211"
42246       ],
42247       [
42248         "Spain (España)",
42249         "es",
42250         "34"
42251       ],
42252       [
42253         "Sri Lanka (ශ්‍රී ලංකාව)",
42254         "lk",
42255         "94"
42256       ],
42257       [
42258         "Sudan (‫السودان‬‎)",
42259         "sd",
42260         "249"
42261       ],
42262       [
42263         "Suriname",
42264         "sr",
42265         "597"
42266       ],
42267       [
42268         "Svalbard and Jan Mayen",
42269         "sj",
42270         "47",
42271         1
42272       ],
42273       [
42274         "Swaziland",
42275         "sz",
42276         "268"
42277       ],
42278       [
42279         "Sweden (Sverige)",
42280         "se",
42281         "46"
42282       ],
42283       [
42284         "Switzerland (Schweiz)",
42285         "ch",
42286         "41"
42287       ],
42288       [
42289         "Syria (‫سوريا‬‎)",
42290         "sy",
42291         "963"
42292       ],
42293       [
42294         "Taiwan (台灣)",
42295         "tw",
42296         "886"
42297       ],
42298       [
42299         "Tajikistan",
42300         "tj",
42301         "992"
42302       ],
42303       [
42304         "Tanzania",
42305         "tz",
42306         "255"
42307       ],
42308       [
42309         "Thailand (ไทย)",
42310         "th",
42311         "66"
42312       ],
42313       [
42314         "Timor-Leste",
42315         "tl",
42316         "670"
42317       ],
42318       [
42319         "Togo",
42320         "tg",
42321         "228"
42322       ],
42323       [
42324         "Tokelau",
42325         "tk",
42326         "690"
42327       ],
42328       [
42329         "Tonga",
42330         "to",
42331         "676"
42332       ],
42333       [
42334         "Trinidad and Tobago",
42335         "tt",
42336         "1868"
42337       ],
42338       [
42339         "Tunisia (‫تونس‬‎)",
42340         "tn",
42341         "216"
42342       ],
42343       [
42344         "Turkey (Türkiye)",
42345         "tr",
42346         "90"
42347       ],
42348       [
42349         "Turkmenistan",
42350         "tm",
42351         "993"
42352       ],
42353       [
42354         "Turks and Caicos Islands",
42355         "tc",
42356         "1649"
42357       ],
42358       [
42359         "Tuvalu",
42360         "tv",
42361         "688"
42362       ],
42363       [
42364         "U.S. Virgin Islands",
42365         "vi",
42366         "1340"
42367       ],
42368       [
42369         "Uganda",
42370         "ug",
42371         "256"
42372       ],
42373       [
42374         "Ukraine (Україна)",
42375         "ua",
42376         "380"
42377       ],
42378       [
42379         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42380         "ae",
42381         "971"
42382       ],
42383       [
42384         "United Kingdom",
42385         "gb",
42386         "44",
42387         0
42388       ],
42389       [
42390         "United States",
42391         "us",
42392         "1",
42393         0
42394       ],
42395       [
42396         "Uruguay",
42397         "uy",
42398         "598"
42399       ],
42400       [
42401         "Uzbekistan (Oʻzbekiston)",
42402         "uz",
42403         "998"
42404       ],
42405       [
42406         "Vanuatu",
42407         "vu",
42408         "678"
42409       ],
42410       [
42411         "Vatican City (Città del Vaticano)",
42412         "va",
42413         "39",
42414         1
42415       ],
42416       [
42417         "Venezuela",
42418         "ve",
42419         "58"
42420       ],
42421       [
42422         "Vietnam (Việt Nam)",
42423         "vn",
42424         "84"
42425       ],
42426       [
42427         "Wallis and Futuna (Wallis-et-Futuna)",
42428         "wf",
42429         "681"
42430       ],
42431       [
42432         "Western Sahara (‫الصحراء الغربية‬‎)",
42433         "eh",
42434         "212",
42435         1
42436       ],
42437       [
42438         "Yemen (‫اليمن‬‎)",
42439         "ye",
42440         "967"
42441       ],
42442       [
42443         "Zambia",
42444         "zm",
42445         "260"
42446       ],
42447       [
42448         "Zimbabwe",
42449         "zw",
42450         "263"
42451       ],
42452       [
42453         "Åland Islands",
42454         "ax",
42455         "358",
42456         1
42457       ]
42458   ];
42459   
42460   return d;
42461 }/**
42462 *    This script refer to:
42463 *    Title: International Telephone Input
42464 *    Author: Jack O'Connor
42465 *    Code version:  v12.1.12
42466 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42467 **/
42468
42469 /**
42470  * @class Roo.bootstrap.PhoneInput
42471  * @extends Roo.bootstrap.TriggerField
42472  * An input with International dial-code selection
42473  
42474  * @cfg {String} defaultDialCode default '+852'
42475  * @cfg {Array} preferedCountries default []
42476   
42477  * @constructor
42478  * Create a new PhoneInput.
42479  * @param {Object} config Configuration options
42480  */
42481
42482 Roo.bootstrap.PhoneInput = function(config) {
42483     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42484 };
42485
42486 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42487         
42488         listWidth: undefined,
42489         
42490         selectedClass: 'active',
42491         
42492         invalidClass : "has-warning",
42493         
42494         validClass: 'has-success',
42495         
42496         allowed: '0123456789',
42497         
42498         max_length: 15,
42499         
42500         /**
42501          * @cfg {String} defaultDialCode The default dial code when initializing the input
42502          */
42503         defaultDialCode: '+852',
42504         
42505         /**
42506          * @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
42507          */
42508         preferedCountries: false,
42509         
42510         getAutoCreate : function()
42511         {
42512             var data = Roo.bootstrap.PhoneInputData();
42513             var align = this.labelAlign || this.parentLabelAlign();
42514             var id = Roo.id();
42515             
42516             this.allCountries = [];
42517             this.dialCodeMapping = [];
42518             
42519             for (var i = 0; i < data.length; i++) {
42520               var c = data[i];
42521               this.allCountries[i] = {
42522                 name: c[0],
42523                 iso2: c[1],
42524                 dialCode: c[2],
42525                 priority: c[3] || 0,
42526                 areaCodes: c[4] || null
42527               };
42528               this.dialCodeMapping[c[2]] = {
42529                   name: c[0],
42530                   iso2: c[1],
42531                   priority: c[3] || 0,
42532                   areaCodes: c[4] || null
42533               };
42534             }
42535             
42536             var cfg = {
42537                 cls: 'form-group',
42538                 cn: []
42539             };
42540             
42541             var input =  {
42542                 tag: 'input',
42543                 id : id,
42544                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42545                 maxlength: this.max_length,
42546                 cls : 'form-control tel-input',
42547                 autocomplete: 'new-password'
42548             };
42549             
42550             var hiddenInput = {
42551                 tag: 'input',
42552                 type: 'hidden',
42553                 cls: 'hidden-tel-input'
42554             };
42555             
42556             if (this.name) {
42557                 hiddenInput.name = this.name;
42558             }
42559             
42560             if (this.disabled) {
42561                 input.disabled = true;
42562             }
42563             
42564             var flag_container = {
42565                 tag: 'div',
42566                 cls: 'flag-box',
42567                 cn: [
42568                     {
42569                         tag: 'div',
42570                         cls: 'flag'
42571                     },
42572                     {
42573                         tag: 'div',
42574                         cls: 'caret'
42575                     }
42576                 ]
42577             };
42578             
42579             var box = {
42580                 tag: 'div',
42581                 cls: this.hasFeedback ? 'has-feedback' : '',
42582                 cn: [
42583                     hiddenInput,
42584                     input,
42585                     {
42586                         tag: 'input',
42587                         cls: 'dial-code-holder',
42588                         disabled: true
42589                     }
42590                 ]
42591             };
42592             
42593             var container = {
42594                 cls: 'roo-select2-container input-group',
42595                 cn: [
42596                     flag_container,
42597                     box
42598                 ]
42599             };
42600             
42601             if (this.fieldLabel.length) {
42602                 var indicator = {
42603                     tag: 'i',
42604                     tooltip: 'This field is required'
42605                 };
42606                 
42607                 var label = {
42608                     tag: 'label',
42609                     'for':  id,
42610                     cls: 'control-label',
42611                     cn: []
42612                 };
42613                 
42614                 var label_text = {
42615                     tag: 'span',
42616                     html: this.fieldLabel
42617                 };
42618                 
42619                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42620                 label.cn = [
42621                     indicator,
42622                     label_text
42623                 ];
42624                 
42625                 if(this.indicatorpos == 'right') {
42626                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42627                     label.cn = [
42628                         label_text,
42629                         indicator
42630                     ];
42631                 }
42632                 
42633                 if(align == 'left') {
42634                     container = {
42635                         tag: 'div',
42636                         cn: [
42637                             container
42638                         ]
42639                     };
42640                     
42641                     if(this.labelWidth > 12){
42642                         label.style = "width: " + this.labelWidth + 'px';
42643                     }
42644                     if(this.labelWidth < 13 && this.labelmd == 0){
42645                         this.labelmd = this.labelWidth;
42646                     }
42647                     if(this.labellg > 0){
42648                         label.cls += ' col-lg-' + this.labellg;
42649                         input.cls += ' col-lg-' + (12 - this.labellg);
42650                     }
42651                     if(this.labelmd > 0){
42652                         label.cls += ' col-md-' + this.labelmd;
42653                         container.cls += ' col-md-' + (12 - this.labelmd);
42654                     }
42655                     if(this.labelsm > 0){
42656                         label.cls += ' col-sm-' + this.labelsm;
42657                         container.cls += ' col-sm-' + (12 - this.labelsm);
42658                     }
42659                     if(this.labelxs > 0){
42660                         label.cls += ' col-xs-' + this.labelxs;
42661                         container.cls += ' col-xs-' + (12 - this.labelxs);
42662                     }
42663                 }
42664             }
42665             
42666             cfg.cn = [
42667                 label,
42668                 container
42669             ];
42670             
42671             var settings = this;
42672             
42673             ['xs','sm','md','lg'].map(function(size){
42674                 if (settings[size]) {
42675                     cfg.cls += ' col-' + size + '-' + settings[size];
42676                 }
42677             });
42678             
42679             this.store = new Roo.data.Store({
42680                 proxy : new Roo.data.MemoryProxy({}),
42681                 reader : new Roo.data.JsonReader({
42682                     fields : [
42683                         {
42684                             'name' : 'name',
42685                             'type' : 'string'
42686                         },
42687                         {
42688                             'name' : 'iso2',
42689                             'type' : 'string'
42690                         },
42691                         {
42692                             'name' : 'dialCode',
42693                             'type' : 'string'
42694                         },
42695                         {
42696                             'name' : 'priority',
42697                             'type' : 'string'
42698                         },
42699                         {
42700                             'name' : 'areaCodes',
42701                             'type' : 'string'
42702                         }
42703                     ]
42704                 })
42705             });
42706             
42707             if(!this.preferedCountries) {
42708                 this.preferedCountries = [
42709                     'hk',
42710                     'gb',
42711                     'us'
42712                 ];
42713             }
42714             
42715             var p = this.preferedCountries.reverse();
42716             
42717             if(p) {
42718                 for (var i = 0; i < p.length; i++) {
42719                     for (var j = 0; j < this.allCountries.length; j++) {
42720                         if(this.allCountries[j].iso2 == p[i]) {
42721                             var t = this.allCountries[j];
42722                             this.allCountries.splice(j,1);
42723                             this.allCountries.unshift(t);
42724                         }
42725                     } 
42726                 }
42727             }
42728             
42729             this.store.proxy.data = {
42730                 success: true,
42731                 data: this.allCountries
42732             };
42733             
42734             return cfg;
42735         },
42736         
42737         initEvents : function()
42738         {
42739             this.createList();
42740             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42741             
42742             this.indicator = this.indicatorEl();
42743             this.flag = this.flagEl();
42744             this.dialCodeHolder = this.dialCodeHolderEl();
42745             
42746             this.trigger = this.el.select('div.flag-box',true).first();
42747             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42748             
42749             var _this = this;
42750             
42751             (function(){
42752                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42753                 _this.list.setWidth(lw);
42754             }).defer(100);
42755             
42756             this.list.on('mouseover', this.onViewOver, this);
42757             this.list.on('mousemove', this.onViewMove, this);
42758             this.inputEl().on("keyup", this.onKeyUp, this);
42759             this.inputEl().on("keypress", this.onKeyPress, this);
42760             
42761             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42762
42763             this.view = new Roo.View(this.list, this.tpl, {
42764                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42765             });
42766             
42767             this.view.on('click', this.onViewClick, this);
42768             this.setValue(this.defaultDialCode);
42769         },
42770         
42771         onTriggerClick : function(e)
42772         {
42773             Roo.log('trigger click');
42774             if(this.disabled){
42775                 return;
42776             }
42777             
42778             if(this.isExpanded()){
42779                 this.collapse();
42780                 this.hasFocus = false;
42781             }else {
42782                 this.store.load({});
42783                 this.hasFocus = true;
42784                 this.expand();
42785             }
42786         },
42787         
42788         isExpanded : function()
42789         {
42790             return this.list.isVisible();
42791         },
42792         
42793         collapse : function()
42794         {
42795             if(!this.isExpanded()){
42796                 return;
42797             }
42798             this.list.hide();
42799             Roo.get(document).un('mousedown', this.collapseIf, this);
42800             Roo.get(document).un('mousewheel', this.collapseIf, this);
42801             this.fireEvent('collapse', this);
42802             this.validate();
42803         },
42804         
42805         expand : function()
42806         {
42807             Roo.log('expand');
42808
42809             if(this.isExpanded() || !this.hasFocus){
42810                 return;
42811             }
42812             
42813             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42814             this.list.setWidth(lw);
42815             
42816             this.list.show();
42817             this.restrictHeight();
42818             
42819             Roo.get(document).on('mousedown', this.collapseIf, this);
42820             Roo.get(document).on('mousewheel', this.collapseIf, this);
42821             
42822             this.fireEvent('expand', this);
42823         },
42824         
42825         restrictHeight : function()
42826         {
42827             this.list.alignTo(this.inputEl(), this.listAlign);
42828             this.list.alignTo(this.inputEl(), this.listAlign);
42829         },
42830         
42831         onViewOver : function(e, t)
42832         {
42833             if(this.inKeyMode){
42834                 return;
42835             }
42836             var item = this.view.findItemFromChild(t);
42837             
42838             if(item){
42839                 var index = this.view.indexOf(item);
42840                 this.select(index, false);
42841             }
42842         },
42843
42844         // private
42845         onViewClick : function(view, doFocus, el, e)
42846         {
42847             var index = this.view.getSelectedIndexes()[0];
42848             
42849             var r = this.store.getAt(index);
42850             
42851             if(r){
42852                 this.onSelect(r, index);
42853             }
42854             if(doFocus !== false && !this.blockFocus){
42855                 this.inputEl().focus();
42856             }
42857         },
42858         
42859         onViewMove : function(e, t)
42860         {
42861             this.inKeyMode = false;
42862         },
42863         
42864         select : function(index, scrollIntoView)
42865         {
42866             this.selectedIndex = index;
42867             this.view.select(index);
42868             if(scrollIntoView !== false){
42869                 var el = this.view.getNode(index);
42870                 if(el){
42871                     this.list.scrollChildIntoView(el, false);
42872                 }
42873             }
42874         },
42875         
42876         createList : function()
42877         {
42878             this.list = Roo.get(document.body).createChild({
42879                 tag: 'ul',
42880                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42881                 style: 'display:none'
42882             });
42883             
42884             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42885         },
42886         
42887         collapseIf : function(e)
42888         {
42889             var in_combo  = e.within(this.el);
42890             var in_list =  e.within(this.list);
42891             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42892             
42893             if (in_combo || in_list || is_list) {
42894                 return;
42895             }
42896             this.collapse();
42897         },
42898         
42899         onSelect : function(record, index)
42900         {
42901             if(this.fireEvent('beforeselect', this, record, index) !== false){
42902                 
42903                 this.setFlagClass(record.data.iso2);
42904                 this.setDialCode(record.data.dialCode);
42905                 this.hasFocus = false;
42906                 this.collapse();
42907                 this.fireEvent('select', this, record, index);
42908             }
42909         },
42910         
42911         flagEl : function()
42912         {
42913             var flag = this.el.select('div.flag',true).first();
42914             if(!flag){
42915                 return false;
42916             }
42917             return flag;
42918         },
42919         
42920         dialCodeHolderEl : function()
42921         {
42922             var d = this.el.select('input.dial-code-holder',true).first();
42923             if(!d){
42924                 return false;
42925             }
42926             return d;
42927         },
42928         
42929         setDialCode : function(v)
42930         {
42931             this.dialCodeHolder.dom.value = '+'+v;
42932         },
42933         
42934         setFlagClass : function(n)
42935         {
42936             this.flag.dom.className = 'flag '+n;
42937         },
42938         
42939         getValue : function()
42940         {
42941             var v = this.inputEl().getValue();
42942             if(this.dialCodeHolder) {
42943                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42944             }
42945             return v;
42946         },
42947         
42948         setValue : function(v)
42949         {
42950             var d = this.getDialCode(v);
42951             
42952             //invalid dial code
42953             if(v.length == 0 || !d || d.length == 0) {
42954                 if(this.rendered){
42955                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42956                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42957                 }
42958                 return;
42959             }
42960             
42961             //valid dial code
42962             this.setFlagClass(this.dialCodeMapping[d].iso2);
42963             this.setDialCode(d);
42964             this.inputEl().dom.value = v.replace('+'+d,'');
42965             this.hiddenEl().dom.value = this.getValue();
42966             
42967             this.validate();
42968         },
42969         
42970         getDialCode : function(v)
42971         {
42972             v = v ||  '';
42973             
42974             if (v.length == 0) {
42975                 return this.dialCodeHolder.dom.value;
42976             }
42977             
42978             var dialCode = "";
42979             if (v.charAt(0) != "+") {
42980                 return false;
42981             }
42982             var numericChars = "";
42983             for (var i = 1; i < v.length; i++) {
42984               var c = v.charAt(i);
42985               if (!isNaN(c)) {
42986                 numericChars += c;
42987                 if (this.dialCodeMapping[numericChars]) {
42988                   dialCode = v.substr(1, i);
42989                 }
42990                 if (numericChars.length == 4) {
42991                   break;
42992                 }
42993               }
42994             }
42995             return dialCode;
42996         },
42997         
42998         reset : function()
42999         {
43000             this.setValue(this.defaultDialCode);
43001             this.validate();
43002         },
43003         
43004         hiddenEl : function()
43005         {
43006             return this.el.select('input.hidden-tel-input',true).first();
43007         },
43008         
43009         // after setting val
43010         onKeyUp : function(e){
43011             this.setValue(this.getValue());
43012         },
43013         
43014         onKeyPress : function(e){
43015             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43016                 e.stopEvent();
43017             }
43018         }
43019         
43020 });
43021 /**
43022  * @class Roo.bootstrap.MoneyField
43023  * @extends Roo.bootstrap.ComboBox
43024  * Bootstrap MoneyField class
43025  * 
43026  * @constructor
43027  * Create a new MoneyField.
43028  * @param {Object} config Configuration options
43029  */
43030
43031 Roo.bootstrap.MoneyField = function(config) {
43032     
43033     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43034     
43035 };
43036
43037 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43038     
43039     /**
43040      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43041      */
43042     allowDecimals : true,
43043     /**
43044      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43045      */
43046     decimalSeparator : ".",
43047     /**
43048      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43049      */
43050     decimalPrecision : 0,
43051     /**
43052      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43053      */
43054     allowNegative : true,
43055     /**
43056      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43057      */
43058     allowZero: true,
43059     /**
43060      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43061      */
43062     minValue : Number.NEGATIVE_INFINITY,
43063     /**
43064      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43065      */
43066     maxValue : Number.MAX_VALUE,
43067     /**
43068      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43069      */
43070     minText : "The minimum value for this field is {0}",
43071     /**
43072      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43073      */
43074     maxText : "The maximum value for this field is {0}",
43075     /**
43076      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43077      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43078      */
43079     nanText : "{0} is not a valid number",
43080     /**
43081      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43082      */
43083     castInt : true,
43084     /**
43085      * @cfg {String} defaults currency of the MoneyField
43086      * value should be in lkey
43087      */
43088     defaultCurrency : false,
43089     /**
43090      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43091      */
43092     thousandsDelimiter : false,
43093     /**
43094      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43095      */
43096     max_length: false,
43097     
43098     inputlg : 9,
43099     inputmd : 9,
43100     inputsm : 9,
43101     inputxs : 6,
43102     
43103     store : false,
43104     
43105     getAutoCreate : function()
43106     {
43107         var align = this.labelAlign || this.parentLabelAlign();
43108         
43109         var id = Roo.id();
43110
43111         var cfg = {
43112             cls: 'form-group',
43113             cn: []
43114         };
43115
43116         var input =  {
43117             tag: 'input',
43118             id : id,
43119             cls : 'form-control roo-money-amount-input',
43120             autocomplete: 'new-password'
43121         };
43122         
43123         var hiddenInput = {
43124             tag: 'input',
43125             type: 'hidden',
43126             id: Roo.id(),
43127             cls: 'hidden-number-input'
43128         };
43129         
43130         if(this.max_length) {
43131             input.maxlength = this.max_length; 
43132         }
43133         
43134         if (this.name) {
43135             hiddenInput.name = this.name;
43136         }
43137
43138         if (this.disabled) {
43139             input.disabled = true;
43140         }
43141
43142         var clg = 12 - this.inputlg;
43143         var cmd = 12 - this.inputmd;
43144         var csm = 12 - this.inputsm;
43145         var cxs = 12 - this.inputxs;
43146         
43147         var container = {
43148             tag : 'div',
43149             cls : 'row roo-money-field',
43150             cn : [
43151                 {
43152                     tag : 'div',
43153                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43154                     cn : [
43155                         {
43156                             tag : 'div',
43157                             cls: 'roo-select2-container input-group',
43158                             cn: [
43159                                 {
43160                                     tag : 'input',
43161                                     cls : 'form-control roo-money-currency-input',
43162                                     autocomplete: 'new-password',
43163                                     readOnly : 1,
43164                                     name : this.currencyName
43165                                 },
43166                                 {
43167                                     tag :'span',
43168                                     cls : 'input-group-addon',
43169                                     cn : [
43170                                         {
43171                                             tag: 'span',
43172                                             cls: 'caret'
43173                                         }
43174                                     ]
43175                                 }
43176                             ]
43177                         }
43178                     ]
43179                 },
43180                 {
43181                     tag : 'div',
43182                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43183                     cn : [
43184                         {
43185                             tag: 'div',
43186                             cls: this.hasFeedback ? 'has-feedback' : '',
43187                             cn: [
43188                                 input
43189                             ]
43190                         }
43191                     ]
43192                 }
43193             ]
43194             
43195         };
43196         
43197         if (this.fieldLabel.length) {
43198             var indicator = {
43199                 tag: 'i',
43200                 tooltip: 'This field is required'
43201             };
43202
43203             var label = {
43204                 tag: 'label',
43205                 'for':  id,
43206                 cls: 'control-label',
43207                 cn: []
43208             };
43209
43210             var label_text = {
43211                 tag: 'span',
43212                 html: this.fieldLabel
43213             };
43214
43215             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43216             label.cn = [
43217                 indicator,
43218                 label_text
43219             ];
43220
43221             if(this.indicatorpos == 'right') {
43222                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43223                 label.cn = [
43224                     label_text,
43225                     indicator
43226                 ];
43227             }
43228
43229             if(align == 'left') {
43230                 container = {
43231                     tag: 'div',
43232                     cn: [
43233                         container
43234                     ]
43235                 };
43236
43237                 if(this.labelWidth > 12){
43238                     label.style = "width: " + this.labelWidth + 'px';
43239                 }
43240                 if(this.labelWidth < 13 && this.labelmd == 0){
43241                     this.labelmd = this.labelWidth;
43242                 }
43243                 if(this.labellg > 0){
43244                     label.cls += ' col-lg-' + this.labellg;
43245                     input.cls += ' col-lg-' + (12 - this.labellg);
43246                 }
43247                 if(this.labelmd > 0){
43248                     label.cls += ' col-md-' + this.labelmd;
43249                     container.cls += ' col-md-' + (12 - this.labelmd);
43250                 }
43251                 if(this.labelsm > 0){
43252                     label.cls += ' col-sm-' + this.labelsm;
43253                     container.cls += ' col-sm-' + (12 - this.labelsm);
43254                 }
43255                 if(this.labelxs > 0){
43256                     label.cls += ' col-xs-' + this.labelxs;
43257                     container.cls += ' col-xs-' + (12 - this.labelxs);
43258                 }
43259             }
43260         }
43261
43262         cfg.cn = [
43263             label,
43264             container,
43265             hiddenInput
43266         ];
43267         
43268         var settings = this;
43269
43270         ['xs','sm','md','lg'].map(function(size){
43271             if (settings[size]) {
43272                 cfg.cls += ' col-' + size + '-' + settings[size];
43273             }
43274         });
43275         
43276         return cfg;
43277     },
43278     
43279     initEvents : function()
43280     {
43281         this.indicator = this.indicatorEl();
43282         
43283         this.initCurrencyEvent();
43284         
43285         this.initNumberEvent();
43286     },
43287     
43288     initCurrencyEvent : function()
43289     {
43290         if (!this.store) {
43291             throw "can not find store for combo";
43292         }
43293         
43294         this.store = Roo.factory(this.store, Roo.data);
43295         this.store.parent = this;
43296         
43297         this.createList();
43298         
43299         this.triggerEl = this.el.select('.input-group-addon', true).first();
43300         
43301         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43302         
43303         var _this = this;
43304         
43305         (function(){
43306             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43307             _this.list.setWidth(lw);
43308         }).defer(100);
43309         
43310         this.list.on('mouseover', this.onViewOver, this);
43311         this.list.on('mousemove', this.onViewMove, this);
43312         this.list.on('scroll', this.onViewScroll, this);
43313         
43314         if(!this.tpl){
43315             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43316         }
43317         
43318         this.view = new Roo.View(this.list, this.tpl, {
43319             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43320         });
43321         
43322         this.view.on('click', this.onViewClick, this);
43323         
43324         this.store.on('beforeload', this.onBeforeLoad, this);
43325         this.store.on('load', this.onLoad, this);
43326         this.store.on('loadexception', this.onLoadException, this);
43327         
43328         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43329             "up" : function(e){
43330                 this.inKeyMode = true;
43331                 this.selectPrev();
43332             },
43333
43334             "down" : function(e){
43335                 if(!this.isExpanded()){
43336                     this.onTriggerClick();
43337                 }else{
43338                     this.inKeyMode = true;
43339                     this.selectNext();
43340                 }
43341             },
43342
43343             "enter" : function(e){
43344                 this.collapse();
43345                 
43346                 if(this.fireEvent("specialkey", this, e)){
43347                     this.onViewClick(false);
43348                 }
43349                 
43350                 return true;
43351             },
43352
43353             "esc" : function(e){
43354                 this.collapse();
43355             },
43356
43357             "tab" : function(e){
43358                 this.collapse();
43359                 
43360                 if(this.fireEvent("specialkey", this, e)){
43361                     this.onViewClick(false);
43362                 }
43363                 
43364                 return true;
43365             },
43366
43367             scope : this,
43368
43369             doRelay : function(foo, bar, hname){
43370                 if(hname == 'down' || this.scope.isExpanded()){
43371                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43372                 }
43373                 return true;
43374             },
43375
43376             forceKeyDown: true
43377         });
43378         
43379         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43380         
43381     },
43382     
43383     initNumberEvent : function(e)
43384     {
43385         this.inputEl().on("keydown" , this.fireKey,  this);
43386         this.inputEl().on("focus", this.onFocus,  this);
43387         this.inputEl().on("blur", this.onBlur,  this);
43388         
43389         this.inputEl().relayEvent('keyup', this);
43390         
43391         if(this.indicator){
43392             this.indicator.addClass('invisible');
43393         }
43394  
43395         this.originalValue = this.getValue();
43396         
43397         if(this.validationEvent == 'keyup'){
43398             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43399             this.inputEl().on('keyup', this.filterValidation, this);
43400         }
43401         else if(this.validationEvent !== false){
43402             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43403         }
43404         
43405         if(this.selectOnFocus){
43406             this.on("focus", this.preFocus, this);
43407             
43408         }
43409         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43410             this.inputEl().on("keypress", this.filterKeys, this);
43411         } else {
43412             this.inputEl().relayEvent('keypress', this);
43413         }
43414         
43415         var allowed = "0123456789";
43416         
43417         if(this.allowDecimals){
43418             allowed += this.decimalSeparator;
43419         }
43420         
43421         if(this.allowNegative){
43422             allowed += "-";
43423         }
43424         
43425         if(this.thousandsDelimiter) {
43426             allowed += ",";
43427         }
43428         
43429         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43430         
43431         var keyPress = function(e){
43432             
43433             var k = e.getKey();
43434             
43435             var c = e.getCharCode();
43436             
43437             if(
43438                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43439                     allowed.indexOf(String.fromCharCode(c)) === -1
43440             ){
43441                 e.stopEvent();
43442                 return;
43443             }
43444             
43445             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43446                 return;
43447             }
43448             
43449             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43450                 e.stopEvent();
43451             }
43452         };
43453         
43454         this.inputEl().on("keypress", keyPress, this);
43455         
43456     },
43457     
43458     onTriggerClick : function(e)
43459     {   
43460         if(this.disabled){
43461             return;
43462         }
43463         
43464         this.page = 0;
43465         this.loadNext = false;
43466         
43467         if(this.isExpanded()){
43468             this.collapse();
43469             return;
43470         }
43471         
43472         this.hasFocus = true;
43473         
43474         if(this.triggerAction == 'all') {
43475             this.doQuery(this.allQuery, true);
43476             return;
43477         }
43478         
43479         this.doQuery(this.getRawValue());
43480     },
43481     
43482     getCurrency : function()
43483     {   
43484         var v = this.currencyEl().getValue();
43485         
43486         return v;
43487     },
43488     
43489     restrictHeight : function()
43490     {
43491         this.list.alignTo(this.currencyEl(), this.listAlign);
43492         this.list.alignTo(this.currencyEl(), this.listAlign);
43493     },
43494     
43495     onViewClick : function(view, doFocus, el, e)
43496     {
43497         var index = this.view.getSelectedIndexes()[0];
43498         
43499         var r = this.store.getAt(index);
43500         
43501         if(r){
43502             this.onSelect(r, index);
43503         }
43504     },
43505     
43506     onSelect : function(record, index){
43507         
43508         if(this.fireEvent('beforeselect', this, record, index) !== false){
43509         
43510             this.setFromCurrencyData(index > -1 ? record.data : false);
43511             
43512             this.collapse();
43513             
43514             this.fireEvent('select', this, record, index);
43515         }
43516     },
43517     
43518     setFromCurrencyData : function(o)
43519     {
43520         var currency = '';
43521         
43522         this.lastCurrency = o;
43523         
43524         if (this.currencyField) {
43525             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43526         } else {
43527             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43528         }
43529         
43530         this.lastSelectionText = currency;
43531         
43532         //setting default currency
43533         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43534             this.setCurrency(this.defaultCurrency);
43535             return;
43536         }
43537         
43538         this.setCurrency(currency);
43539     },
43540     
43541     setFromData : function(o)
43542     {
43543         var c = {};
43544         
43545         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43546         
43547         this.setFromCurrencyData(c);
43548         
43549         var value = '';
43550         
43551         if (this.name) {
43552             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43553         } else {
43554             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43555         }
43556         
43557         this.setValue(value);
43558         
43559     },
43560     
43561     setCurrency : function(v)
43562     {   
43563         this.currencyValue = v;
43564         
43565         if(this.rendered){
43566             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43567             this.validate();
43568         }
43569     },
43570     
43571     setValue : function(v)
43572     {
43573         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43574         
43575         this.value = v;
43576         
43577         if(this.rendered){
43578             
43579             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43580             
43581             this.inputEl().dom.value = (v == '') ? '' :
43582                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43583             
43584             if(!this.allowZero && v === '0') {
43585                 this.hiddenEl().dom.value = '';
43586                 this.inputEl().dom.value = '';
43587             }
43588             
43589             this.validate();
43590         }
43591     },
43592     
43593     getRawValue : function()
43594     {
43595         var v = this.inputEl().getValue();
43596         
43597         return v;
43598     },
43599     
43600     getValue : function()
43601     {
43602         return this.fixPrecision(this.parseValue(this.getRawValue()));
43603     },
43604     
43605     parseValue : function(value)
43606     {
43607         if(this.thousandsDelimiter) {
43608             value += "";
43609             r = new RegExp(",", "g");
43610             value = value.replace(r, "");
43611         }
43612         
43613         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43614         return isNaN(value) ? '' : value;
43615         
43616     },
43617     
43618     fixPrecision : function(value)
43619     {
43620         if(this.thousandsDelimiter) {
43621             value += "";
43622             r = new RegExp(",", "g");
43623             value = value.replace(r, "");
43624         }
43625         
43626         var nan = isNaN(value);
43627         
43628         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43629             return nan ? '' : value;
43630         }
43631         return parseFloat(value).toFixed(this.decimalPrecision);
43632     },
43633     
43634     decimalPrecisionFcn : function(v)
43635     {
43636         return Math.floor(v);
43637     },
43638     
43639     validateValue : function(value)
43640     {
43641         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43642             return false;
43643         }
43644         
43645         var num = this.parseValue(value);
43646         
43647         if(isNaN(num)){
43648             this.markInvalid(String.format(this.nanText, value));
43649             return false;
43650         }
43651         
43652         if(num < this.minValue){
43653             this.markInvalid(String.format(this.minText, this.minValue));
43654             return false;
43655         }
43656         
43657         if(num > this.maxValue){
43658             this.markInvalid(String.format(this.maxText, this.maxValue));
43659             return false;
43660         }
43661         
43662         return true;
43663     },
43664     
43665     validate : function()
43666     {
43667         if(this.disabled || this.allowBlank){
43668             this.markValid();
43669             return true;
43670         }
43671         
43672         var currency = this.getCurrency();
43673         
43674         if(this.validateValue(this.getRawValue()) && currency.length){
43675             this.markValid();
43676             return true;
43677         }
43678         
43679         this.markInvalid();
43680         return false;
43681     },
43682     
43683     getName: function()
43684     {
43685         return this.name;
43686     },
43687     
43688     beforeBlur : function()
43689     {
43690         if(!this.castInt){
43691             return;
43692         }
43693         
43694         var v = this.parseValue(this.getRawValue());
43695         
43696         if(v || v == 0){
43697             this.setValue(v);
43698         }
43699     },
43700     
43701     onBlur : function()
43702     {
43703         this.beforeBlur();
43704         
43705         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43706             //this.el.removeClass(this.focusClass);
43707         }
43708         
43709         this.hasFocus = false;
43710         
43711         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43712             this.validate();
43713         }
43714         
43715         var v = this.getValue();
43716         
43717         if(String(v) !== String(this.startValue)){
43718             this.fireEvent('change', this, v, this.startValue);
43719         }
43720         
43721         this.fireEvent("blur", this);
43722     },
43723     
43724     inputEl : function()
43725     {
43726         return this.el.select('.roo-money-amount-input', true).first();
43727     },
43728     
43729     currencyEl : function()
43730     {
43731         return this.el.select('.roo-money-currency-input', true).first();
43732     },
43733     
43734     hiddenEl : function()
43735     {
43736         return this.el.select('input.hidden-number-input',true).first();
43737     }
43738     
43739 });/**
43740  * @class Roo.bootstrap.BezierSignature
43741  * @extends Roo.bootstrap.Component
43742  * Bootstrap BezierSignature class
43743  * This script refer to:
43744  *    Title: Signature Pad
43745  *    Author: szimek
43746  *    Availability: https://github.com/szimek/signature_pad
43747  *
43748  * @constructor
43749  * Create a new BezierSignature
43750  * @param {Object} config The config object
43751  */
43752
43753 Roo.bootstrap.BezierSignature = function(config){
43754     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43755     this.addEvents({
43756         "resize" : true
43757     });
43758 };
43759
43760 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43761 {
43762      
43763     curve_data: [],
43764     
43765     is_empty: true,
43766     
43767     mouse_btn_down: true,
43768     
43769     /**
43770      * @cfg {int} canvas height
43771      */
43772     canvas_height: '200px',
43773     
43774     /**
43775      * @cfg {float|function} Radius of a single dot.
43776      */ 
43777     dot_size: false,
43778     
43779     /**
43780      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43781      */
43782     min_width: 0.5,
43783     
43784     /**
43785      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43786      */
43787     max_width: 2.5,
43788     
43789     /**
43790      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43791      */
43792     throttle: 16,
43793     
43794     /**
43795      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43796      */
43797     min_distance: 5,
43798     
43799     /**
43800      * @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.
43801      */
43802     bg_color: 'rgba(0, 0, 0, 0)',
43803     
43804     /**
43805      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43806      */
43807     dot_color: 'black',
43808     
43809     /**
43810      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43811      */ 
43812     velocity_filter_weight: 0.7,
43813     
43814     /**
43815      * @cfg {function} Callback when stroke begin. 
43816      */
43817     onBegin: false,
43818     
43819     /**
43820      * @cfg {function} Callback when stroke end.
43821      */
43822     onEnd: false,
43823     
43824     getAutoCreate : function()
43825     {
43826         var cls = 'roo-signature column';
43827         
43828         if(this.cls){
43829             cls += ' ' + this.cls;
43830         }
43831         
43832         var col_sizes = [
43833             'lg',
43834             'md',
43835             'sm',
43836             'xs'
43837         ];
43838         
43839         for(var i = 0; i < col_sizes.length; i++) {
43840             if(this[col_sizes[i]]) {
43841                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43842             }
43843         }
43844         
43845         var cfg = {
43846             tag: 'div',
43847             cls: cls,
43848             cn: [
43849                 {
43850                     tag: 'div',
43851                     cls: 'roo-signature-body',
43852                     cn: [
43853                         {
43854                             tag: 'canvas',
43855                             cls: 'roo-signature-body-canvas',
43856                             height: this.canvas_height,
43857                             width: this.canvas_width
43858                         }
43859                     ]
43860                 },
43861                 {
43862                     tag: 'input',
43863                     type: 'file',
43864                     style: 'display: none'
43865                 }
43866             ]
43867         };
43868         
43869         return cfg;
43870     },
43871     
43872     initEvents: function() 
43873     {
43874         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43875         
43876         var canvas = this.canvasEl();
43877         
43878         // mouse && touch event swapping...
43879         canvas.dom.style.touchAction = 'none';
43880         canvas.dom.style.msTouchAction = 'none';
43881         
43882         this.mouse_btn_down = false;
43883         canvas.on('mousedown', this._handleMouseDown, this);
43884         canvas.on('mousemove', this._handleMouseMove, this);
43885         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43886         
43887         if (window.PointerEvent) {
43888             canvas.on('pointerdown', this._handleMouseDown, this);
43889             canvas.on('pointermove', this._handleMouseMove, this);
43890             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43891         }
43892         
43893         if ('ontouchstart' in window) {
43894             canvas.on('touchstart', this._handleTouchStart, this);
43895             canvas.on('touchmove', this._handleTouchMove, this);
43896             canvas.on('touchend', this._handleTouchEnd, this);
43897         }
43898         
43899         Roo.EventManager.onWindowResize(this.resize, this, true);
43900         
43901         // file input event
43902         this.fileEl().on('change', this.uploadImage, this);
43903         
43904         this.clear();
43905         
43906         this.resize();
43907     },
43908     
43909     resize: function(){
43910         
43911         var canvas = this.canvasEl().dom;
43912         var ctx = this.canvasElCtx();
43913         var img_data = false;
43914         
43915         if(canvas.width > 0) {
43916             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43917         }
43918         // setting canvas width will clean img data
43919         canvas.width = 0;
43920         
43921         var style = window.getComputedStyle ? 
43922             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43923             
43924         var padding_left = parseInt(style.paddingLeft) || 0;
43925         var padding_right = parseInt(style.paddingRight) || 0;
43926         
43927         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43928         
43929         if(img_data) {
43930             ctx.putImageData(img_data, 0, 0);
43931         }
43932     },
43933     
43934     _handleMouseDown: function(e)
43935     {
43936         if (e.browserEvent.which === 1) {
43937             this.mouse_btn_down = true;
43938             this.strokeBegin(e);
43939         }
43940     },
43941     
43942     _handleMouseMove: function (e)
43943     {
43944         if (this.mouse_btn_down) {
43945             this.strokeMoveUpdate(e);
43946         }
43947     },
43948     
43949     _handleMouseUp: function (e)
43950     {
43951         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43952             this.mouse_btn_down = false;
43953             this.strokeEnd(e);
43954         }
43955     },
43956     
43957     _handleTouchStart: function (e) {
43958         
43959         e.preventDefault();
43960         if (e.browserEvent.targetTouches.length === 1) {
43961             // var touch = e.browserEvent.changedTouches[0];
43962             // this.strokeBegin(touch);
43963             
43964              this.strokeBegin(e); // assume e catching the correct xy...
43965         }
43966     },
43967     
43968     _handleTouchMove: function (e) {
43969         e.preventDefault();
43970         // var touch = event.targetTouches[0];
43971         // _this._strokeMoveUpdate(touch);
43972         this.strokeMoveUpdate(e);
43973     },
43974     
43975     _handleTouchEnd: function (e) {
43976         var wasCanvasTouched = e.target === this.canvasEl().dom;
43977         if (wasCanvasTouched) {
43978             e.preventDefault();
43979             // var touch = event.changedTouches[0];
43980             // _this._strokeEnd(touch);
43981             this.strokeEnd(e);
43982         }
43983     },
43984     
43985     reset: function () {
43986         this._lastPoints = [];
43987         this._lastVelocity = 0;
43988         this._lastWidth = (this.min_width + this.max_width) / 2;
43989         this.canvasElCtx().fillStyle = this.dot_color;
43990     },
43991     
43992     strokeMoveUpdate: function(e)
43993     {
43994         this.strokeUpdate(e);
43995         
43996         if (this.throttle) {
43997             this.throttleStroke(this.strokeUpdate, this.throttle);
43998         }
43999         else {
44000             this.strokeUpdate(e);
44001         }
44002     },
44003     
44004     strokeBegin: function(e)
44005     {
44006         var newPointGroup = {
44007             color: this.dot_color,
44008             points: []
44009         };
44010         
44011         if (typeof this.onBegin === 'function') {
44012             this.onBegin(e);
44013         }
44014         
44015         this.curve_data.push(newPointGroup);
44016         this.reset();
44017         this.strokeUpdate(e);
44018     },
44019     
44020     strokeUpdate: function(e)
44021     {
44022         var rect = this.canvasEl().dom.getBoundingClientRect();
44023         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44024         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44025         var lastPoints = lastPointGroup.points;
44026         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44027         var isLastPointTooClose = lastPoint
44028             ? point.distanceTo(lastPoint) <= this.min_distance
44029             : false;
44030         var color = lastPointGroup.color;
44031         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44032             var curve = this.addPoint(point);
44033             if (!lastPoint) {
44034                 this.drawDot({color: color, point: point});
44035             }
44036             else if (curve) {
44037                 this.drawCurve({color: color, curve: curve});
44038             }
44039             lastPoints.push({
44040                 time: point.time,
44041                 x: point.x,
44042                 y: point.y
44043             });
44044         }
44045     },
44046     
44047     strokeEnd: function(e)
44048     {
44049         this.strokeUpdate(e);
44050         if (typeof this.onEnd === 'function') {
44051             this.onEnd(e);
44052         }
44053     },
44054     
44055     addPoint:  function (point) {
44056         var _lastPoints = this._lastPoints;
44057         _lastPoints.push(point);
44058         if (_lastPoints.length > 2) {
44059             if (_lastPoints.length === 3) {
44060                 _lastPoints.unshift(_lastPoints[0]);
44061             }
44062             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44063             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44064             _lastPoints.shift();
44065             return curve;
44066         }
44067         return null;
44068     },
44069     
44070     calculateCurveWidths: function (startPoint, endPoint) {
44071         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44072             (1 - this.velocity_filter_weight) * this._lastVelocity;
44073
44074         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44075         var widths = {
44076             end: newWidth,
44077             start: this._lastWidth
44078         };
44079         
44080         this._lastVelocity = velocity;
44081         this._lastWidth = newWidth;
44082         return widths;
44083     },
44084     
44085     drawDot: function (_a) {
44086         var color = _a.color, point = _a.point;
44087         var ctx = this.canvasElCtx();
44088         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44089         ctx.beginPath();
44090         this.drawCurveSegment(point.x, point.y, width);
44091         ctx.closePath();
44092         ctx.fillStyle = color;
44093         ctx.fill();
44094     },
44095     
44096     drawCurve: function (_a) {
44097         var color = _a.color, curve = _a.curve;
44098         var ctx = this.canvasElCtx();
44099         var widthDelta = curve.endWidth - curve.startWidth;
44100         var drawSteps = Math.floor(curve.length()) * 2;
44101         ctx.beginPath();
44102         ctx.fillStyle = color;
44103         for (var i = 0; i < drawSteps; i += 1) {
44104         var t = i / drawSteps;
44105         var tt = t * t;
44106         var ttt = tt * t;
44107         var u = 1 - t;
44108         var uu = u * u;
44109         var uuu = uu * u;
44110         var x = uuu * curve.startPoint.x;
44111         x += 3 * uu * t * curve.control1.x;
44112         x += 3 * u * tt * curve.control2.x;
44113         x += ttt * curve.endPoint.x;
44114         var y = uuu * curve.startPoint.y;
44115         y += 3 * uu * t * curve.control1.y;
44116         y += 3 * u * tt * curve.control2.y;
44117         y += ttt * curve.endPoint.y;
44118         var width = curve.startWidth + ttt * widthDelta;
44119         this.drawCurveSegment(x, y, width);
44120         }
44121         ctx.closePath();
44122         ctx.fill();
44123     },
44124     
44125     drawCurveSegment: function (x, y, width) {
44126         var ctx = this.canvasElCtx();
44127         ctx.moveTo(x, y);
44128         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44129         this.is_empty = false;
44130     },
44131     
44132     clear: function()
44133     {
44134         var ctx = this.canvasElCtx();
44135         var canvas = this.canvasEl().dom;
44136         ctx.fillStyle = this.bg_color;
44137         ctx.clearRect(0, 0, canvas.width, canvas.height);
44138         ctx.fillRect(0, 0, canvas.width, canvas.height);
44139         this.curve_data = [];
44140         this.reset();
44141         this.is_empty = true;
44142     },
44143     
44144     fileEl: function()
44145     {
44146         return  this.el.select('input',true).first();
44147     },
44148     
44149     canvasEl: function()
44150     {
44151         return this.el.select('canvas',true).first();
44152     },
44153     
44154     canvasElCtx: function()
44155     {
44156         return this.el.select('canvas',true).first().dom.getContext('2d');
44157     },
44158     
44159     getImage: function(type)
44160     {
44161         if(this.is_empty) {
44162             return false;
44163         }
44164         
44165         // encryption ?
44166         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44167     },
44168     
44169     drawFromImage: function(img_src)
44170     {
44171         var img = new Image();
44172         
44173         img.onload = function(){
44174             this.canvasElCtx().drawImage(img, 0, 0);
44175         }.bind(this);
44176         
44177         img.src = img_src;
44178         
44179         this.is_empty = false;
44180     },
44181     
44182     selectImage: function()
44183     {
44184         this.fileEl().dom.click();
44185     },
44186     
44187     uploadImage: function(e)
44188     {
44189         var reader = new FileReader();
44190         
44191         reader.onload = function(e){
44192             var img = new Image();
44193             img.onload = function(){
44194                 this.reset();
44195                 this.canvasElCtx().drawImage(img, 0, 0);
44196             }.bind(this);
44197             img.src = e.target.result;
44198         }.bind(this);
44199         
44200         reader.readAsDataURL(e.target.files[0]);
44201     },
44202     
44203     // Bezier Point Constructor
44204     Point: (function () {
44205         function Point(x, y, time) {
44206             this.x = x;
44207             this.y = y;
44208             this.time = time || Date.now();
44209         }
44210         Point.prototype.distanceTo = function (start) {
44211             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44212         };
44213         Point.prototype.equals = function (other) {
44214             return this.x === other.x && this.y === other.y && this.time === other.time;
44215         };
44216         Point.prototype.velocityFrom = function (start) {
44217             return this.time !== start.time
44218             ? this.distanceTo(start) / (this.time - start.time)
44219             : 0;
44220         };
44221         return Point;
44222     }()),
44223     
44224     
44225     // Bezier Constructor
44226     Bezier: (function () {
44227         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44228             this.startPoint = startPoint;
44229             this.control2 = control2;
44230             this.control1 = control1;
44231             this.endPoint = endPoint;
44232             this.startWidth = startWidth;
44233             this.endWidth = endWidth;
44234         }
44235         Bezier.fromPoints = function (points, widths, scope) {
44236             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44237             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44238             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44239         };
44240         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44241             var dx1 = s1.x - s2.x;
44242             var dy1 = s1.y - s2.y;
44243             var dx2 = s2.x - s3.x;
44244             var dy2 = s2.y - s3.y;
44245             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44246             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44247             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44248             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44249             var dxm = m1.x - m2.x;
44250             var dym = m1.y - m2.y;
44251             var k = l2 / (l1 + l2);
44252             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44253             var tx = s2.x - cm.x;
44254             var ty = s2.y - cm.y;
44255             return {
44256                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44257                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44258             };
44259         };
44260         Bezier.prototype.length = function () {
44261             var steps = 10;
44262             var length = 0;
44263             var px;
44264             var py;
44265             for (var i = 0; i <= steps; i += 1) {
44266                 var t = i / steps;
44267                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44268                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44269                 if (i > 0) {
44270                     var xdiff = cx - px;
44271                     var ydiff = cy - py;
44272                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44273                 }
44274                 px = cx;
44275                 py = cy;
44276             }
44277             return length;
44278         };
44279         Bezier.prototype.point = function (t, start, c1, c2, end) {
44280             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44281             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44282             + (3.0 * c2 * (1.0 - t) * t * t)
44283             + (end * t * t * t);
44284         };
44285         return Bezier;
44286     }()),
44287     
44288     throttleStroke: function(fn, wait) {
44289       if (wait === void 0) { wait = 250; }
44290       var previous = 0;
44291       var timeout = null;
44292       var result;
44293       var storedContext;
44294       var storedArgs;
44295       var later = function () {
44296           previous = Date.now();
44297           timeout = null;
44298           result = fn.apply(storedContext, storedArgs);
44299           if (!timeout) {
44300               storedContext = null;
44301               storedArgs = [];
44302           }
44303       };
44304       return function wrapper() {
44305           var args = [];
44306           for (var _i = 0; _i < arguments.length; _i++) {
44307               args[_i] = arguments[_i];
44308           }
44309           var now = Date.now();
44310           var remaining = wait - (now - previous);
44311           storedContext = this;
44312           storedArgs = args;
44313           if (remaining <= 0 || remaining > wait) {
44314               if (timeout) {
44315                   clearTimeout(timeout);
44316                   timeout = null;
44317               }
44318               previous = now;
44319               result = fn.apply(storedContext, storedArgs);
44320               if (!timeout) {
44321                   storedContext = null;
44322                   storedArgs = [];
44323               }
44324           }
44325           else if (!timeout) {
44326               timeout = window.setTimeout(later, remaining);
44327           }
44328           return result;
44329       };
44330   }
44331   
44332 });
44333
44334  
44335
44336