f926485f15338f6a857544fe9b3fb0a24a6cb63c
[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 {Boolean} bodyOverflow should the body element have overflow auto added default false
3951  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953  * @cfg {String} size (sm|lg|xl) default empty
3954  * @cfg {Number} max_width set the max width of modal
3955  * @cfg {Boolean} editableTitle can the title be edited
3956
3957  *
3958  *
3959  * @constructor
3960  * Create a new Modal Dialog
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.Modal = function(config){
3965     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3966     this.addEvents({
3967         // raw events
3968         /**
3969          * @event btnclick
3970          * The raw btnclick event for the button
3971          * @param {Roo.EventObject} e
3972          */
3973         "btnclick" : true,
3974         /**
3975          * @event resize
3976          * Fire when dialog resize
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} e
3979          */
3980         "resize" : true,
3981         /**
3982          * @event titlechanged
3983          * Fire when the editable title has been changed
3984          * @param {Roo.bootstrap.Modal} this
3985          * @param {Roo.EventObject} value
3986          */
3987         "titlechanged" : true 
3988         
3989     });
3990     this.buttons = this.buttons || [];
3991
3992     if (this.tmpl) {
3993         this.tmpl = Roo.factory(this.tmpl);
3994     }
3995
3996 };
3997
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3999
4000     title : 'test dialog',
4001
4002     buttons : false,
4003
4004     // set on load...
4005
4006     html: false,
4007
4008     tmp: false,
4009
4010     specificTitle: false,
4011
4012     buttonPosition: 'right',
4013
4014     allow_close : true,
4015
4016     animate : true,
4017
4018     fitwindow: false,
4019     
4020      // private
4021     dialogEl: false,
4022     bodyEl:  false,
4023     footerEl:  false,
4024     titleEl:  false,
4025     closeEl:  false,
4026
4027     size: '',
4028     
4029     max_width: 0,
4030     
4031     max_height: 0,
4032     
4033     fit_content: false,
4034     editableTitle  : false,
4035
4036     onRender : function(ct, position)
4037     {
4038         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4039
4040         if(!this.el){
4041             var cfg = Roo.apply({},  this.getAutoCreate());
4042             cfg.id = Roo.id();
4043             //if(!cfg.name){
4044             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045             //}
4046             //if (!cfg.name.length) {
4047             //    delete cfg.name;
4048            // }
4049             if (this.cls) {
4050                 cfg.cls += ' ' + this.cls;
4051             }
4052             if (this.style) {
4053                 cfg.style = this.style;
4054             }
4055             this.el = Roo.get(document.body).createChild(cfg, position);
4056         }
4057         //var type = this.el.dom.type;
4058
4059
4060         if(this.tabIndex !== undefined){
4061             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4062         }
4063
4064         this.dialogEl = this.el.select('.modal-dialog',true).first();
4065         this.bodyEl = this.el.select('.modal-body',true).first();
4066         this.closeEl = this.el.select('.modal-header .close', true).first();
4067         this.headerEl = this.el.select('.modal-header',true).first();
4068         this.titleEl = this.el.select('.modal-title',true).first();
4069         this.footerEl = this.el.select('.modal-footer',true).first();
4070
4071         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072         
4073         //this.el.addClass("x-dlg-modal");
4074
4075         if (this.buttons.length) {
4076             Roo.each(this.buttons, function(bb) {
4077                 var b = Roo.apply({}, bb);
4078                 b.xns = b.xns || Roo.bootstrap;
4079                 b.xtype = b.xtype || 'Button';
4080                 if (typeof(b.listeners) == 'undefined') {
4081                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4082                 }
4083
4084                 var btn = Roo.factory(b);
4085
4086                 btn.render(this.getButtonContainer());
4087
4088             },this);
4089         }
4090         // render the children.
4091         var nitems = [];
4092
4093         if(typeof(this.items) != 'undefined'){
4094             var items = this.items;
4095             delete this.items;
4096
4097             for(var i =0;i < items.length;i++) {
4098                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4099             }
4100         }
4101
4102         this.items = nitems;
4103
4104         // where are these used - they used to be body/close/footer
4105
4106
4107         this.initEvents();
4108         //this.el.addClass([this.fieldClass, this.cls]);
4109
4110     },
4111
4112     getAutoCreate : function()
4113     {
4114         // we will default to modal-body-overflow - might need to remove or make optional later.
4115         var bdy = {
4116                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4117                 html : this.html || ''
4118         };
4119
4120         var title = {
4121             tag: 'h5',
4122             cls : 'modal-title',
4123             html : this.title
4124         };
4125
4126         if(this.specificTitle){ // WTF is this?
4127             title = this.title;
4128         }
4129
4130         var header = [];
4131         if (this.allow_close && Roo.bootstrap.version == 3) {
4132             header.push({
4133                 tag: 'button',
4134                 cls : 'close',
4135                 html : '&times'
4136             });
4137         }
4138
4139         header.push(title);
4140
4141         if (this.editableTitle) {
4142             header.push({
4143                 cls: 'form-control roo-editable-title d-none',
4144                 tag: 'input',
4145                 type: 'text'
4146             });
4147         }
4148         
4149         if (this.allow_close && Roo.bootstrap.version == 4) {
4150             header.push({
4151                 tag: 'button',
4152                 cls : 'close',
4153                 html : '&times'
4154             });
4155         }
4156         
4157         var size = '';
4158
4159         if(this.size.length){
4160             size = 'modal-' + this.size;
4161         }
4162         
4163         var footer = Roo.bootstrap.version == 3 ?
4164             {
4165                 cls : 'modal-footer',
4166                 cn : [
4167                     {
4168                         tag: 'div',
4169                         cls: 'btn-' + this.buttonPosition
4170                     }
4171                 ]
4172
4173             } :
4174             {  // BS4 uses mr-auto on left buttons....
4175                 cls : 'modal-footer'
4176             };
4177
4178             
4179
4180         
4181         
4182         var modal = {
4183             cls: "modal",
4184              cn : [
4185                 {
4186                     cls: "modal-dialog " + size,
4187                     cn : [
4188                         {
4189                             cls : "modal-content",
4190                             cn : [
4191                                 {
4192                                     cls : 'modal-header',
4193                                     cn : header
4194                                 },
4195                                 bdy,
4196                                 footer
4197                             ]
4198
4199                         }
4200                     ]
4201
4202                 }
4203             ]
4204         };
4205
4206         if(this.animate){
4207             modal.cls += ' fade';
4208         }
4209
4210         return modal;
4211
4212     },
4213     getChildContainer : function() {
4214
4215          return this.bodyEl;
4216
4217     },
4218     getButtonContainer : function() {
4219         
4220          return Roo.bootstrap.version == 4 ?
4221             this.el.select('.modal-footer',true).first()
4222             : this.el.select('.modal-footer div',true).first();
4223
4224     },
4225     initEvents : function()
4226     {
4227         if (this.allow_close) {
4228             this.closeEl.on('click', this.hide, this);
4229         }
4230         Roo.EventManager.onWindowResize(this.resize, this, true);
4231         if (this.editableTitle) {
4232             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4233             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234             this.headerEditEl.on('keyup', function(e) {
4235                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236                         this.toggleHeaderInput(false)
4237                     }
4238                 }, this);
4239             this.headerEditEl.on('blur', function(e) {
4240                 this.toggleHeaderInput(false)
4241             },this);
4242         }
4243
4244     },
4245   
4246
4247     resize : function()
4248     {
4249         this.maskEl.setSize(
4250             Roo.lib.Dom.getViewWidth(true),
4251             Roo.lib.Dom.getViewHeight(true)
4252         );
4253         
4254         if (this.fitwindow) {
4255             
4256            this.dialogEl.setStyle( { 'max-width' : '100%' });
4257             this.setSize(
4258                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4260             );
4261             return;
4262         }
4263         
4264         if(this.max_width !== 0) {
4265             
4266             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4267             
4268             if(this.height) {
4269                 this.setSize(w, this.height);
4270                 return;
4271             }
4272             
4273             if(this.max_height) {
4274                 this.setSize(w,Math.min(
4275                     this.max_height,
4276                     Roo.lib.Dom.getViewportHeight(true) - 60
4277                 ));
4278                 
4279                 return;
4280             }
4281             
4282             if(!this.fit_content) {
4283                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4284                 return;
4285             }
4286             
4287             this.setSize(w, Math.min(
4288                 60 +
4289                 this.headerEl.getHeight() + 
4290                 this.footerEl.getHeight() + 
4291                 this.getChildHeight(this.bodyEl.dom.childNodes),
4292                 Roo.lib.Dom.getViewportHeight(true) - 60)
4293             );
4294         }
4295         
4296     },
4297
4298     setSize : function(w,h)
4299     {
4300         if (!w && !h) {
4301             return;
4302         }
4303         
4304         this.resizeTo(w,h);
4305     },
4306
4307     show : function() {
4308
4309         if (!this.rendered) {
4310             this.render();
4311         }
4312         this.toggleHeaderInput(false);
4313         //this.el.setStyle('display', 'block');
4314         this.el.removeClass('hideing');
4315         this.el.dom.style.display='block';
4316         
4317         Roo.get(document.body).addClass('modal-open');
4318  
4319         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4320             
4321             (function(){
4322                 this.el.addClass('show');
4323                 this.el.addClass('in');
4324             }).defer(50, this);
4325         }else{
4326             this.el.addClass('show');
4327             this.el.addClass('in');
4328         }
4329
4330         // not sure how we can show data in here..
4331         //if (this.tmpl) {
4332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4333         //}
4334
4335         Roo.get(document.body).addClass("x-body-masked");
4336         
4337         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4338         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339         this.maskEl.dom.style.display = 'block';
4340         this.maskEl.addClass('show');
4341         
4342         
4343         this.resize();
4344         
4345         this.fireEvent('show', this);
4346
4347         // set zindex here - otherwise it appears to be ignored...
4348         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4349
4350         (function () {
4351             this.items.forEach( function(e) {
4352                 e.layout ? e.layout() : false;
4353
4354             });
4355         }).defer(100,this);
4356
4357     },
4358     hide : function()
4359     {
4360         if(this.fireEvent("beforehide", this) !== false){
4361             
4362             this.maskEl.removeClass('show');
4363             
4364             this.maskEl.dom.style.display = '';
4365             Roo.get(document.body).removeClass("x-body-masked");
4366             this.el.removeClass('in');
4367             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368
4369             if(this.animate){ // why
4370                 this.el.addClass('hideing');
4371                 this.el.removeClass('show');
4372                 (function(){
4373                     if (!this.el.hasClass('hideing')) {
4374                         return; // it's been shown again...
4375                     }
4376                     
4377                     this.el.dom.style.display='';
4378
4379                     Roo.get(document.body).removeClass('modal-open');
4380                     this.el.removeClass('hideing');
4381                 }).defer(150,this);
4382                 
4383             }else{
4384                 this.el.removeClass('show');
4385                 this.el.dom.style.display='';
4386                 Roo.get(document.body).removeClass('modal-open');
4387
4388             }
4389             this.fireEvent('hide', this);
4390         }
4391     },
4392     isVisible : function()
4393     {
4394         
4395         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4396         
4397     },
4398
4399     addButton : function(str, cb)
4400     {
4401
4402
4403         var b = Roo.apply({}, { html : str } );
4404         b.xns = b.xns || Roo.bootstrap;
4405         b.xtype = b.xtype || 'Button';
4406         if (typeof(b.listeners) == 'undefined') {
4407             b.listeners = { click : cb.createDelegate(this)  };
4408         }
4409
4410         var btn = Roo.factory(b);
4411
4412         btn.render(this.getButtonContainer());
4413
4414         return btn;
4415
4416     },
4417
4418     setDefaultButton : function(btn)
4419     {
4420         //this.el.select('.modal-footer').()
4421     },
4422
4423     resizeTo: function(w,h)
4424     {
4425         this.dialogEl.setWidth(w);
4426         
4427         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4428
4429         this.bodyEl.setHeight(h - diff);
4430         
4431         this.fireEvent('resize', this);
4432     },
4433     
4434     setContentSize  : function(w, h)
4435     {
4436
4437     },
4438     onButtonClick: function(btn,e)
4439     {
4440         //Roo.log([a,b,c]);
4441         this.fireEvent('btnclick', btn.name, e);
4442     },
4443      /**
4444      * Set the title of the Dialog
4445      * @param {String} str new Title
4446      */
4447     setTitle: function(str) {
4448         this.titleEl.dom.innerHTML = str;
4449         this.title = str;
4450     },
4451     /**
4452      * Set the body of the Dialog
4453      * @param {String} str new Title
4454      */
4455     setBody: function(str) {
4456         this.bodyEl.dom.innerHTML = str;
4457     },
4458     /**
4459      * Set the body of the Dialog using the template
4460      * @param {Obj} data - apply this data to the template and replace the body contents.
4461      */
4462     applyBody: function(obj)
4463     {
4464         if (!this.tmpl) {
4465             Roo.log("Error - using apply Body without a template");
4466             //code
4467         }
4468         this.tmpl.overwrite(this.bodyEl, obj);
4469     },
4470     
4471     getChildHeight : function(child_nodes)
4472     {
4473         if(
4474             !child_nodes ||
4475             child_nodes.length == 0
4476         ) {
4477             return 0;
4478         }
4479         
4480         var child_height = 0;
4481         
4482         for(var i = 0; i < child_nodes.length; i++) {
4483             
4484             /*
4485             * for modal with tabs...
4486             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487                 
4488                 var layout_childs = child_nodes[i].childNodes;
4489                 
4490                 for(var j = 0; j < layout_childs.length; j++) {
4491                     
4492                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493                         
4494                         var layout_body_childs = layout_childs[j].childNodes;
4495                         
4496                         for(var k = 0; k < layout_body_childs.length; k++) {
4497                             
4498                             if(layout_body_childs[k].classList.contains('navbar')) {
4499                                 child_height += layout_body_childs[k].offsetHeight;
4500                                 continue;
4501                             }
4502                             
4503                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504                                 
4505                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506                                 
4507                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508                                     
4509                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4511                                         continue;
4512                                     }
4513                                     
4514                                 }
4515                                 
4516                             }
4517                             
4518                         }
4519                     }
4520                 }
4521                 continue;
4522             }
4523             */
4524             
4525             child_height += child_nodes[i].offsetHeight;
4526             // Roo.log(child_nodes[i].offsetHeight);
4527         }
4528         
4529         return child_height;
4530     },
4531     toggleHeaderInput : function(is_edit)
4532     {
4533         if (!this.editableTitle) {
4534             return; // not editable.
4535         }
4536         if (is_edit && this.is_header_editing) {
4537             return; // already editing..
4538         }
4539         if (is_edit) {
4540     
4541             this.headerEditEl.dom.value = this.title;
4542             this.headerEditEl.removeClass('d-none');
4543             this.headerEditEl.dom.focus();
4544             this.titleEl.addClass('d-none');
4545             
4546             this.is_header_editing = true;
4547             return
4548         }
4549         // flip back to not editing.
4550         this.title = this.headerEditEl.dom.value;
4551         this.headerEditEl.addClass('d-none');
4552         this.titleEl.removeClass('d-none');
4553         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554         this.is_header_editing = false;
4555         this.fireEvent('titlechanged', this, this.title);
4556     
4557             
4558         
4559     }
4560
4561 });
4562
4563
4564 Roo.apply(Roo.bootstrap.Modal,  {
4565     /**
4566          * Button config that displays a single OK button
4567          * @type Object
4568          */
4569         OK :  [{
4570             name : 'ok',
4571             weight : 'primary',
4572             html : 'OK'
4573         }],
4574         /**
4575          * Button config that displays Yes and No buttons
4576          * @type Object
4577          */
4578         YESNO : [
4579             {
4580                 name  : 'no',
4581                 html : 'No'
4582             },
4583             {
4584                 name  :'yes',
4585                 weight : 'primary',
4586                 html : 'Yes'
4587             }
4588         ],
4589
4590         /**
4591          * Button config that displays OK and Cancel buttons
4592          * @type Object
4593          */
4594         OKCANCEL : [
4595             {
4596                name : 'cancel',
4597                 html : 'Cancel'
4598             },
4599             {
4600                 name : 'ok',
4601                 weight : 'primary',
4602                 html : 'OK'
4603             }
4604         ],
4605         /**
4606          * Button config that displays Yes, No and Cancel buttons
4607          * @type Object
4608          */
4609         YESNOCANCEL : [
4610             {
4611                 name : 'yes',
4612                 weight : 'primary',
4613                 html : 'Yes'
4614             },
4615             {
4616                 name : 'no',
4617                 html : 'No'
4618             },
4619             {
4620                 name : 'cancel',
4621                 html : 'Cancel'
4622             }
4623         ],
4624         
4625         zIndex : 10001
4626 });
4627
4628 /*
4629  * - LGPL
4630  *
4631  * messagebox - can be used as a replace
4632  * 
4633  */
4634 /**
4635  * @class Roo.MessageBox
4636  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4637  * Example usage:
4638  *<pre><code>
4639 // Basic alert:
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644     if (btn == 'ok'){
4645         // process text value...
4646     }
4647 });
4648
4649 // Show a dialog using config options:
4650 Roo.Msg.show({
4651    title:'Save Changes?',
4652    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653    buttons: Roo.Msg.YESNOCANCEL,
4654    fn: processResult,
4655    animEl: 'elId'
4656 });
4657 </code></pre>
4658  * @singleton
4659  */
4660 Roo.bootstrap.MessageBox = function(){
4661     var dlg, opt, mask, waitTimer;
4662     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663     var buttons, activeTextEl, bwidth;
4664
4665     
4666     // private
4667     var handleButton = function(button){
4668         dlg.hide();
4669         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4670     };
4671
4672     // private
4673     var handleHide = function(){
4674         if(opt && opt.cls){
4675             dlg.el.removeClass(opt.cls);
4676         }
4677         //if(waitTimer){
4678         //    Roo.TaskMgr.stop(waitTimer);
4679         //    waitTimer = null;
4680         //}
4681     };
4682
4683     // private
4684     var updateButtons = function(b){
4685         var width = 0;
4686         if(!b){
4687             buttons["ok"].hide();
4688             buttons["cancel"].hide();
4689             buttons["yes"].hide();
4690             buttons["no"].hide();
4691             dlg.footerEl.hide();
4692             
4693             return width;
4694         }
4695         dlg.footerEl.show();
4696         for(var k in buttons){
4697             if(typeof buttons[k] != "function"){
4698                 if(b[k]){
4699                     buttons[k].show();
4700                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701                     width += buttons[k].el.getWidth()+15;
4702                 }else{
4703                     buttons[k].hide();
4704                 }
4705             }
4706         }
4707         return width;
4708     };
4709
4710     // private
4711     var handleEsc = function(d, k, e){
4712         if(opt && opt.closable !== false){
4713             dlg.hide();
4714         }
4715         if(e){
4716             e.stopEvent();
4717         }
4718     };
4719
4720     return {
4721         /**
4722          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723          * @return {Roo.BasicDialog} The BasicDialog element
4724          */
4725         getDialog : function(){
4726            if(!dlg){
4727                 dlg = new Roo.bootstrap.Modal( {
4728                     //draggable: true,
4729                     //resizable:false,
4730                     //constraintoviewport:false,
4731                     //fixedcenter:true,
4732                     //collapsible : false,
4733                     //shim:true,
4734                     //modal: true,
4735                 //    width: 'auto',
4736                   //  height:100,
4737                     //buttonAlign:"center",
4738                     closeClick : function(){
4739                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4740                             handleButton("no");
4741                         }else{
4742                             handleButton("cancel");
4743                         }
4744                     }
4745                 });
4746                 dlg.render();
4747                 dlg.on("hide", handleHide);
4748                 mask = dlg.mask;
4749                 //dlg.addKeyListener(27, handleEsc);
4750                 buttons = {};
4751                 this.buttons = buttons;
4752                 var bt = this.buttonText;
4753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757                 //Roo.log(buttons);
4758                 bodyEl = dlg.bodyEl.createChild({
4759
4760                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761                         '<textarea class="roo-mb-textarea"></textarea>' +
4762                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4763                 });
4764                 msgEl = bodyEl.dom.firstChild;
4765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766                 textboxEl.enableDisplayMode();
4767                 textboxEl.addKeyListener([10,13], function(){
4768                     if(dlg.isVisible() && opt && opt.buttons){
4769                         if(opt.buttons.ok){
4770                             handleButton("ok");
4771                         }else if(opt.buttons.yes){
4772                             handleButton("yes");
4773                         }
4774                     }
4775                 });
4776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777                 textareaEl.enableDisplayMode();
4778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779                 progressEl.enableDisplayMode();
4780                 
4781                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782                 var pf = progressEl.dom.firstChild;
4783                 if (pf) {
4784                     pp = Roo.get(pf.firstChild);
4785                     pp.setHeight(pf.offsetHeight);
4786                 }
4787                 
4788             }
4789             return dlg;
4790         },
4791
4792         /**
4793          * Updates the message box body text
4794          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795          * the XHTML-compliant non-breaking space character '&amp;#160;')
4796          * @return {Roo.MessageBox} This message box
4797          */
4798         updateText : function(text)
4799         {
4800             if(!dlg.isVisible() && !opt.width){
4801                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803             }
4804             msgEl.innerHTML = text || '&#160;';
4805       
4806             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808             var w = Math.max(
4809                     Math.min(opt.width || cw , this.maxWidth), 
4810                     Math.max(opt.minWidth || this.minWidth, bwidth)
4811             );
4812             if(opt.prompt){
4813                 activeTextEl.setWidth(w);
4814             }
4815             if(dlg.isVisible()){
4816                 dlg.fixedcenter = false;
4817             }
4818             // to big, make it scroll. = But as usual stupid IE does not support
4819             // !important..
4820             
4821             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.height = '';
4826                 bodyEl.dom.style.overflowY = '';
4827             }
4828             if (cw > w) {
4829                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830             } else {
4831                 bodyEl.dom.style.overflowX = '';
4832             }
4833             
4834             dlg.setContentSize(w, bodyEl.getHeight());
4835             if(dlg.isVisible()){
4836                 dlg.fixedcenter = true;
4837             }
4838             return this;
4839         },
4840
4841         /**
4842          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4843          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846          * @return {Roo.MessageBox} This message box
4847          */
4848         updateProgress : function(value, text){
4849             if(text){
4850                 this.updateText(text);
4851             }
4852             
4853             if (pp) { // weird bug on my firefox - for some reason this is not defined
4854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4856             }
4857             return this;
4858         },        
4859
4860         /**
4861          * Returns true if the message box is currently displayed
4862          * @return {Boolean} True if the message box is visible, else false
4863          */
4864         isVisible : function(){
4865             return dlg && dlg.isVisible();  
4866         },
4867
4868         /**
4869          * Hides the message box if it is displayed
4870          */
4871         hide : function(){
4872             if(this.isVisible()){
4873                 dlg.hide();
4874             }  
4875         },
4876
4877         /**
4878          * Displays a new message box, or reinitializes an existing message box, based on the config options
4879          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880          * The following config object properties are supported:
4881          * <pre>
4882 Property    Type             Description
4883 ----------  ---------------  ------------------------------------------------------------------------------------
4884 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4885                                    closes (defaults to undefined)
4886 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4889                                    progress and wait dialogs will ignore this property and always hide the
4890                                    close button as they can only be closed programmatically.
4891 cls               String           A custom CSS class to apply to the message box element
4892 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4893                                    displayed (defaults to 75)
4894 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4895                                    function will be btn (the name of the button that was clicked, if applicable,
4896                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4897                                    Progress and wait dialogs will ignore this option since they do not respond to
4898                                    user actions and can only be closed programmatically, so any required function
4899                                    should be called by the same code after it closes the dialog.
4900 icon              String           A CSS class that provides a background image to be used as an icon for
4901                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4903 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4904 modal             Boolean          False to allow user interaction with the page while the message box is
4905                                    displayed (defaults to true)
4906 msg               String           A string that will replace the existing message box body text (defaults
4907                                    to the XHTML-compliant non-breaking space character '&#160;')
4908 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4909 progress          Boolean          True to display a progress bar (defaults to false)
4910 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4913 title             String           The title text
4914 value             String           The string value to set into the active textbox element if displayed
4915 wait              Boolean          True to display a progress bar (defaults to false)
4916 width             Number           The width of the dialog in pixels
4917 </pre>
4918          *
4919          * Example usage:
4920          * <pre><code>
4921 Roo.Msg.show({
4922    title: 'Address',
4923    msg: 'Please enter your address:',
4924    width: 300,
4925    buttons: Roo.MessageBox.OKCANCEL,
4926    multiline: true,
4927    fn: saveAddress,
4928    animEl: 'addAddressBtn'
4929 });
4930 </code></pre>
4931          * @param {Object} config Configuration options
4932          * @return {Roo.MessageBox} This message box
4933          */
4934         show : function(options)
4935         {
4936             
4937             // this causes nightmares if you show one dialog after another
4938             // especially on callbacks..
4939              
4940             if(this.isVisible()){
4941                 
4942                 this.hide();
4943                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4945                 Roo.log("New Dialog Message:" +  options.msg )
4946                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4948                 
4949             }
4950             var d = this.getDialog();
4951             opt = options;
4952             d.setTitle(opt.title || "&#160;");
4953             d.closeEl.setDisplayed(opt.closable !== false);
4954             activeTextEl = textboxEl;
4955             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4956             if(opt.prompt){
4957                 if(opt.multiline){
4958                     textboxEl.hide();
4959                     textareaEl.show();
4960                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4961                         opt.multiline : this.defaultTextHeight);
4962                     activeTextEl = textareaEl;
4963                 }else{
4964                     textboxEl.show();
4965                     textareaEl.hide();
4966                 }
4967             }else{
4968                 textboxEl.hide();
4969                 textareaEl.hide();
4970             }
4971             progressEl.setDisplayed(opt.progress === true);
4972             if (opt.progress) {
4973                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974             }
4975             this.updateProgress(0);
4976             activeTextEl.dom.value = opt.value || "";
4977             if(opt.prompt){
4978                 dlg.setDefaultButton(activeTextEl);
4979             }else{
4980                 var bs = opt.buttons;
4981                 var db = null;
4982                 if(bs && bs.ok){
4983                     db = buttons["ok"];
4984                 }else if(bs && bs.yes){
4985                     db = buttons["yes"];
4986                 }
4987                 dlg.setDefaultButton(db);
4988             }
4989             bwidth = updateButtons(opt.buttons);
4990             this.updateText(opt.msg);
4991             if(opt.cls){
4992                 d.el.addClass(opt.cls);
4993             }
4994             d.proxyDrag = opt.proxyDrag === true;
4995             d.modal = opt.modal !== false;
4996             d.mask = opt.modal !== false ? mask : false;
4997             if(!d.isVisible()){
4998                 // force it to the end of the z-index stack so it gets a cursor in FF
4999                 document.body.appendChild(dlg.el.dom);
5000                 d.animateTarget = null;
5001                 d.show(options.animEl);
5002             }
5003             return this;
5004         },
5005
5006         /**
5007          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5008          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009          * and closing the message box when the process is complete.
5010          * @param {String} title The title bar text
5011          * @param {String} msg The message box body text
5012          * @return {Roo.MessageBox} This message box
5013          */
5014         progress : function(title, msg){
5015             this.show({
5016                 title : title,
5017                 msg : msg,
5018                 buttons: false,
5019                 progress:true,
5020                 closable:false,
5021                 minWidth: this.minProgressWidth,
5022                 modal : true
5023             });
5024             return this;
5025         },
5026
5027         /**
5028          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029          * If a callback function is passed it will be called after the user clicks the button, and the
5030          * id of the button that was clicked will be passed as the only parameter to the callback
5031          * (could also be the top-right close button).
5032          * @param {String} title The title bar text
5033          * @param {String} msg The message box body text
5034          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035          * @param {Object} scope (optional) The scope of the callback function
5036          * @return {Roo.MessageBox} This message box
5037          */
5038         alert : function(title, msg, fn, scope)
5039         {
5040             this.show({
5041                 title : title,
5042                 msg : msg,
5043                 buttons: this.OK,
5044                 fn: fn,
5045                 closable : false,
5046                 scope : scope,
5047                 modal : true
5048             });
5049             return this;
5050         },
5051
5052         /**
5053          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5054          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055          * You are responsible for closing the message box when the process is complete.
5056          * @param {String} msg The message box body text
5057          * @param {String} title (optional) The title bar text
5058          * @return {Roo.MessageBox} This message box
5059          */
5060         wait : function(msg, title){
5061             this.show({
5062                 title : title,
5063                 msg : msg,
5064                 buttons: false,
5065                 closable:false,
5066                 progress:true,
5067                 modal:true,
5068                 width:300,
5069                 wait:true
5070             });
5071             waitTimer = Roo.TaskMgr.start({
5072                 run: function(i){
5073                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5074                 },
5075                 interval: 1000
5076             });
5077             return this;
5078         },
5079
5080         /**
5081          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084          * @param {String} title The title bar text
5085          * @param {String} msg The message box body text
5086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087          * @param {Object} scope (optional) The scope of the callback function
5088          * @return {Roo.MessageBox} This message box
5089          */
5090         confirm : function(title, msg, fn, scope){
5091             this.show({
5092                 title : title,
5093                 msg : msg,
5094                 buttons: this.YESNO,
5095                 fn: fn,
5096                 scope : scope,
5097                 modal : true
5098             });
5099             return this;
5100         },
5101
5102         /**
5103          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5105          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106          * (could also be the top-right close button) and the text that was entered will be passed as the two
5107          * parameters to the callback.
5108          * @param {String} title The title bar text
5109          * @param {String} msg The message box body text
5110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111          * @param {Object} scope (optional) The scope of the callback function
5112          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114          * @return {Roo.MessageBox} This message box
5115          */
5116         prompt : function(title, msg, fn, scope, multiline){
5117             this.show({
5118                 title : title,
5119                 msg : msg,
5120                 buttons: this.OKCANCEL,
5121                 fn: fn,
5122                 minWidth:250,
5123                 scope : scope,
5124                 prompt:true,
5125                 multiline: multiline,
5126                 modal : true
5127             });
5128             return this;
5129         },
5130
5131         /**
5132          * Button config that displays a single OK button
5133          * @type Object
5134          */
5135         OK : {ok:true},
5136         /**
5137          * Button config that displays Yes and No buttons
5138          * @type Object
5139          */
5140         YESNO : {yes:true, no:true},
5141         /**
5142          * Button config that displays OK and Cancel buttons
5143          * @type Object
5144          */
5145         OKCANCEL : {ok:true, cancel:true},
5146         /**
5147          * Button config that displays Yes, No and Cancel buttons
5148          * @type Object
5149          */
5150         YESNOCANCEL : {yes:true, no:true, cancel:true},
5151
5152         /**
5153          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5154          * @type Number
5155          */
5156         defaultTextHeight : 75,
5157         /**
5158          * The maximum width in pixels of the message box (defaults to 600)
5159          * @type Number
5160          */
5161         maxWidth : 600,
5162         /**
5163          * The minimum width in pixels of the message box (defaults to 100)
5164          * @type Number
5165          */
5166         minWidth : 100,
5167         /**
5168          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5169          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5170          * @type Number
5171          */
5172         minProgressWidth : 250,
5173         /**
5174          * An object containing the default button text strings that can be overriden for localized language support.
5175          * Supported properties are: ok, cancel, yes and no.
5176          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5177          * @type Object
5178          */
5179         buttonText : {
5180             ok : "OK",
5181             cancel : "Cancel",
5182             yes : "Yes",
5183             no : "No"
5184         }
5185     };
5186 }();
5187
5188 /**
5189  * Shorthand for {@link Roo.MessageBox}
5190  */
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5193 /*
5194  * - LGPL
5195  *
5196  * navbar
5197  * 
5198  */
5199
5200 /**
5201  * @class Roo.bootstrap.Navbar
5202  * @extends Roo.bootstrap.Component
5203  * Bootstrap Navbar class
5204
5205  * @constructor
5206  * Create a new Navbar
5207  * @param {Object} config The config object
5208  */
5209
5210
5211 Roo.bootstrap.Navbar = function(config){
5212     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5213     this.addEvents({
5214         // raw events
5215         /**
5216          * @event beforetoggle
5217          * Fire before toggle the menu
5218          * @param {Roo.EventObject} e
5219          */
5220         "beforetoggle" : true
5221     });
5222 };
5223
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5225     
5226     
5227    
5228     // private
5229     navItems : false,
5230     loadMask : false,
5231     
5232     
5233     getAutoCreate : function(){
5234         
5235         
5236         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5237         
5238     },
5239     
5240     initEvents :function ()
5241     {
5242         //Roo.log(this.el.select('.navbar-toggle',true));
5243         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244         
5245         var mark = {
5246             tag: "div",
5247             cls:"x-dlg-mask"
5248         };
5249         
5250         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251         
5252         var size = this.el.getSize();
5253         this.maskEl.setSize(size.width, size.height);
5254         this.maskEl.enableDisplayMode("block");
5255         this.maskEl.hide();
5256         
5257         if(this.loadMask){
5258             this.maskEl.show();
5259         }
5260     },
5261     
5262     
5263     getChildContainer : function()
5264     {
5265         if (this.el && this.el.select('.collapse').getCount()) {
5266             return this.el.select('.collapse',true).first();
5267         }
5268         
5269         return this.el;
5270     },
5271     
5272     mask : function()
5273     {
5274         this.maskEl.show();
5275     },
5276     
5277     unmask : function()
5278     {
5279         this.maskEl.hide();
5280     },
5281     onToggle : function()
5282     {
5283         
5284         if(this.fireEvent('beforetoggle', this) === false){
5285             return;
5286         }
5287         var ce = this.el.select('.navbar-collapse',true).first();
5288       
5289         if (!ce.hasClass('show')) {
5290            this.expand();
5291         } else {
5292             this.collapse();
5293         }
5294         
5295         
5296     
5297     },
5298     /**
5299      * Expand the navbar pulldown 
5300      */
5301     expand : function ()
5302     {
5303        
5304         var ce = this.el.select('.navbar-collapse',true).first();
5305         if (ce.hasClass('collapsing')) {
5306             return;
5307         }
5308         ce.dom.style.height = '';
5309                // show it...
5310         ce.addClass('in'); // old...
5311         ce.removeClass('collapse');
5312         ce.addClass('show');
5313         var h = ce.getHeight();
5314         Roo.log(h);
5315         ce.removeClass('show');
5316         // at this point we should be able to see it..
5317         ce.addClass('collapsing');
5318         
5319         ce.setHeight(0); // resize it ...
5320         ce.on('transitionend', function() {
5321             //Roo.log('done transition');
5322             ce.removeClass('collapsing');
5323             ce.addClass('show');
5324             ce.removeClass('collapse');
5325
5326             ce.dom.style.height = '';
5327         }, this, { single: true} );
5328         ce.setHeight(h);
5329         ce.dom.scrollTop = 0;
5330     },
5331     /**
5332      * Collapse the navbar pulldown 
5333      */
5334     collapse : function()
5335     {
5336          var ce = this.el.select('.navbar-collapse',true).first();
5337        
5338         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339             // it's collapsed or collapsing..
5340             return;
5341         }
5342         ce.removeClass('in'); // old...
5343         ce.setHeight(ce.getHeight());
5344         ce.removeClass('show');
5345         ce.addClass('collapsing');
5346         
5347         ce.on('transitionend', function() {
5348             ce.dom.style.height = '';
5349             ce.removeClass('collapsing');
5350             ce.addClass('collapse');
5351         }, this, { single: true} );
5352         ce.setHeight(0);
5353     }
5354     
5355     
5356     
5357 });
5358
5359
5360
5361  
5362
5363  /*
5364  * - LGPL
5365  *
5366  * navbar
5367  * 
5368  */
5369
5370 /**
5371  * @class Roo.bootstrap.NavSimplebar
5372  * @extends Roo.bootstrap.Navbar
5373  * Bootstrap Sidebar class
5374  *
5375  * @cfg {Boolean} inverse is inverted color
5376  * 
5377  * @cfg {String} type (nav | pills | tabs)
5378  * @cfg {Boolean} arrangement stacked | justified
5379  * @cfg {String} align (left | right) alignment
5380  * 
5381  * @cfg {Boolean} main (true|false) main nav bar? default false
5382  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383  * 
5384  * @cfg {String} tag (header|footer|nav|div) default is nav 
5385
5386  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new Sidebar
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.NavSimplebar = function(config){
5396     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5397 };
5398
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5400     
5401     inverse: false,
5402     
5403     type: false,
5404     arrangement: '',
5405     align : false,
5406     
5407     weight : 'light',
5408     
5409     main : false,
5410     
5411     
5412     tag : false,
5413     
5414     
5415     getAutoCreate : function(){
5416         
5417         
5418         var cfg = {
5419             tag : this.tag || 'div',
5420             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421         };
5422         if (['light','white'].indexOf(this.weight) > -1) {
5423             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424         }
5425         cfg.cls += ' bg-' + this.weight;
5426         
5427         if (this.inverse) {
5428             cfg.cls += ' navbar-inverse';
5429             
5430         }
5431         
5432         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433         
5434         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5435             return cfg;
5436         }
5437         
5438         
5439     
5440         
5441         cfg.cn = [
5442             {
5443                 cls: 'nav nav-' + this.xtype,
5444                 tag : 'ul'
5445             }
5446         ];
5447         
5448          
5449         this.type = this.type || 'nav';
5450         if (['tabs','pills'].indexOf(this.type) != -1) {
5451             cfg.cn[0].cls += ' nav-' + this.type
5452         
5453         
5454         } else {
5455             if (this.type!=='nav') {
5456                 Roo.log('nav type must be nav/tabs/pills')
5457             }
5458             cfg.cn[0].cls += ' navbar-nav'
5459         }
5460         
5461         
5462         
5463         
5464         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465             cfg.cn[0].cls += ' nav-' + this.arrangement;
5466         }
5467         
5468         
5469         if (this.align === 'right') {
5470             cfg.cn[0].cls += ' navbar-right';
5471         }
5472         
5473         
5474         
5475         
5476         return cfg;
5477     
5478         
5479     }
5480     
5481     
5482     
5483 });
5484
5485
5486
5487  
5488
5489  
5490        /*
5491  * - LGPL
5492  *
5493  * navbar
5494  * navbar-fixed-top
5495  * navbar-expand-md  fixed-top 
5496  */
5497
5498 /**
5499  * @class Roo.bootstrap.NavHeaderbar
5500  * @extends Roo.bootstrap.NavSimplebar
5501  * Bootstrap Sidebar class
5502  *
5503  * @cfg {String} brand what is brand
5504  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505  * @cfg {String} brand_href href of the brand
5506  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5507  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5510  * 
5511  * @constructor
5512  * Create a new Sidebar
5513  * @param {Object} config The config object
5514  */
5515
5516
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5519       
5520 };
5521
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5523     
5524     position: '',
5525     brand: '',
5526     brand_href: false,
5527     srButton : true,
5528     autohide : false,
5529     desktopCenter : false,
5530    
5531     
5532     getAutoCreate : function(){
5533         
5534         var   cfg = {
5535             tag: this.nav || 'nav',
5536             cls: 'navbar navbar-expand-md',
5537             role: 'navigation',
5538             cn: []
5539         };
5540         
5541         var cn = cfg.cn;
5542         if (this.desktopCenter) {
5543             cn.push({cls : 'container', cn : []});
5544             cn = cn[0].cn;
5545         }
5546         
5547         if(this.srButton){
5548             var btn = {
5549                 tag: 'button',
5550                 type: 'button',
5551                 cls: 'navbar-toggle navbar-toggler',
5552                 'data-toggle': 'collapse',
5553                 cn: [
5554                     {
5555                         tag: 'span',
5556                         cls: 'sr-only',
5557                         html: 'Toggle navigation'
5558                     },
5559                     {
5560                         tag: 'span',
5561                         cls: 'icon-bar navbar-toggler-icon'
5562                     },
5563                     {
5564                         tag: 'span',
5565                         cls: 'icon-bar'
5566                     },
5567                     {
5568                         tag: 'span',
5569                         cls: 'icon-bar'
5570                     }
5571                 ]
5572             };
5573             
5574             cn.push( Roo.bootstrap.version == 4 ? btn : {
5575                 tag: 'div',
5576                 cls: 'navbar-header',
5577                 cn: [
5578                     btn
5579                 ]
5580             });
5581         }
5582         
5583         cn.push({
5584             tag: 'div',
5585             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5586             cn : []
5587         });
5588         
5589         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590         
5591         if (['light','white'].indexOf(this.weight) > -1) {
5592             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593         }
5594         cfg.cls += ' bg-' + this.weight;
5595         
5596         
5597         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599             
5600             // tag can override this..
5601             
5602             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5603         }
5604         
5605         if (this.brand !== '') {
5606             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608                 tag: 'a',
5609                 href: this.brand_href ? this.brand_href : '#',
5610                 cls: 'navbar-brand',
5611                 cn: [
5612                 this.brand
5613                 ]
5614             });
5615         }
5616         
5617         if(this.main){
5618             cfg.cls += ' main-nav';
5619         }
5620         
5621         
5622         return cfg;
5623
5624         
5625     },
5626     getHeaderChildContainer : function()
5627     {
5628         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629             return this.el.select('.navbar-header',true).first();
5630         }
5631         
5632         return this.getChildContainer();
5633     },
5634     
5635     getChildContainer : function()
5636     {
5637          
5638         return this.el.select('.roo-navbar-collapse',true).first();
5639          
5640         
5641     },
5642     
5643     initEvents : function()
5644     {
5645         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646         
5647         if (this.autohide) {
5648             
5649             var prevScroll = 0;
5650             var ft = this.el;
5651             
5652             Roo.get(document).on('scroll',function(e) {
5653                 var ns = Roo.get(document).getScroll().top;
5654                 var os = prevScroll;
5655                 prevScroll = ns;
5656                 
5657                 if(ns > os){
5658                     ft.removeClass('slideDown');
5659                     ft.addClass('slideUp');
5660                     return;
5661                 }
5662                 ft.removeClass('slideUp');
5663                 ft.addClass('slideDown');
5664                  
5665               
5666           },this);
5667         }
5668     }    
5669     
5670 });
5671
5672
5673
5674  
5675
5676  /*
5677  * - LGPL
5678  *
5679  * navbar
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.NavSidebar
5685  * @extends Roo.bootstrap.Navbar
5686  * Bootstrap Sidebar class
5687  * 
5688  * @constructor
5689  * Create a new Sidebar
5690  * @param {Object} config The config object
5691  */
5692
5693
5694 Roo.bootstrap.NavSidebar = function(config){
5695     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5696 };
5697
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5699     
5700     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701     
5702     getAutoCreate : function(){
5703         
5704         
5705         return  {
5706             tag: 'div',
5707             cls: 'sidebar sidebar-nav'
5708         };
5709     
5710         
5711     }
5712     
5713     
5714     
5715 });
5716
5717
5718
5719  
5720
5721  /*
5722  * - LGPL
5723  *
5724  * nav group
5725  * 
5726  */
5727
5728 /**
5729  * @class Roo.bootstrap.NavGroup
5730  * @extends Roo.bootstrap.Component
5731  * Bootstrap NavGroup class
5732  * @cfg {String} align (left|right)
5733  * @cfg {Boolean} inverse
5734  * @cfg {String} type (nav|pills|tab) default nav
5735  * @cfg {String} navId - reference Id for navbar.
5736  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5737  * 
5738  * @constructor
5739  * Create a new nav group
5740  * @param {Object} config The config object
5741  */
5742
5743 Roo.bootstrap.NavGroup = function(config){
5744     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5745     this.navItems = [];
5746    
5747     Roo.bootstrap.NavGroup.register(this);
5748      this.addEvents({
5749         /**
5750              * @event changed
5751              * Fires when the active item changes
5752              * @param {Roo.bootstrap.NavGroup} this
5753              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5755          */
5756         'changed': true
5757      });
5758     
5759 };
5760
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5762     
5763     align: '',
5764     inverse: false,
5765     form: false,
5766     type: 'nav',
5767     navId : '',
5768     // private
5769     pilltype : true,
5770     
5771     navItems : false, 
5772     
5773     getAutoCreate : function()
5774     {
5775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5776         
5777         cfg = {
5778             tag : 'ul',
5779             cls: 'nav' 
5780         };
5781         if (Roo.bootstrap.version == 4) {
5782             if (['tabs','pills'].indexOf(this.type) != -1) {
5783                 cfg.cls += ' nav-' + this.type; 
5784             } else {
5785                 // trying to remove so header bar can right align top?
5786                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787                     // do not use on header bar... 
5788                     cfg.cls += ' navbar-nav';
5789                 }
5790             }
5791             
5792         } else {
5793             if (['tabs','pills'].indexOf(this.type) != -1) {
5794                 cfg.cls += ' nav-' + this.type
5795             } else {
5796                 if (this.type !== 'nav') {
5797                     Roo.log('nav type must be nav/tabs/pills')
5798                 }
5799                 cfg.cls += ' navbar-nav'
5800             }
5801         }
5802         
5803         if (this.parent() && this.parent().sidebar) {
5804             cfg = {
5805                 tag: 'ul',
5806                 cls: 'dashboard-menu sidebar-menu'
5807             };
5808             
5809             return cfg;
5810         }
5811         
5812         if (this.form === true) {
5813             cfg = {
5814                 tag: 'form',
5815                 cls: 'navbar-form form-inline'
5816             };
5817             //nav navbar-right ml-md-auto
5818             if (this.align === 'right') {
5819                 cfg.cls += ' navbar-right ml-md-auto';
5820             } else {
5821                 cfg.cls += ' navbar-left';
5822             }
5823         }
5824         
5825         if (this.align === 'right') {
5826             cfg.cls += ' navbar-right ml-md-auto';
5827         } else {
5828             cfg.cls += ' mr-auto';
5829         }
5830         
5831         if (this.inverse) {
5832             cfg.cls += ' navbar-inverse';
5833             
5834         }
5835         
5836         
5837         return cfg;
5838     },
5839     /**
5840     * sets the active Navigation item
5841     * @param {Roo.bootstrap.NavItem} the new current navitem
5842     */
5843     setActiveItem : function(item)
5844     {
5845         var prev = false;
5846         Roo.each(this.navItems, function(v){
5847             if (v == item) {
5848                 return ;
5849             }
5850             if (v.isActive()) {
5851                 v.setActive(false, true);
5852                 prev = v;
5853                 
5854             }
5855             
5856         });
5857
5858         item.setActive(true, true);
5859         this.fireEvent('changed', this, item, prev);
5860         
5861         
5862     },
5863     /**
5864     * gets the active Navigation item
5865     * @return {Roo.bootstrap.NavItem} the current navitem
5866     */
5867     getActive : function()
5868     {
5869         
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             
5873             if (v.isActive()) {
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879         return prev;
5880     },
5881     
5882     indexOfNav : function()
5883     {
5884         
5885         var prev = false;
5886         Roo.each(this.navItems, function(v,i){
5887             
5888             if (v.isActive()) {
5889                 prev = i;
5890                 
5891             }
5892             
5893         });
5894         return prev;
5895     },
5896     /**
5897     * adds a Navigation item
5898     * @param {Roo.bootstrap.NavItem} the navitem to add
5899     */
5900     addItem : function(cfg)
5901     {
5902         if (this.form && Roo.bootstrap.version == 4) {
5903             cfg.tag = 'div';
5904         }
5905         var cn = new Roo.bootstrap.NavItem(cfg);
5906         this.register(cn);
5907         cn.parentId = this.id;
5908         cn.onRender(this.el, null);
5909         return cn;
5910     },
5911     /**
5912     * register a Navigation item
5913     * @param {Roo.bootstrap.NavItem} the navitem to add
5914     */
5915     register : function(item)
5916     {
5917         this.navItems.push( item);
5918         item.navId = this.navId;
5919     
5920     },
5921     
5922     /**
5923     * clear all the Navigation item
5924     */
5925    
5926     clearAll : function()
5927     {
5928         this.navItems = [];
5929         this.el.dom.innerHTML = '';
5930     },
5931     
5932     getNavItem: function(tabId)
5933     {
5934         var ret = false;
5935         Roo.each(this.navItems, function(e) {
5936             if (e.tabId == tabId) {
5937                ret =  e;
5938                return false;
5939             }
5940             return true;
5941             
5942         });
5943         return ret;
5944     },
5945     
5946     setActiveNext : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i > this.navItems.length) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i+1]);
5953     },
5954     setActivePrev : function()
5955     {
5956         var i = this.indexOfNav(this.getActive());
5957         if (i  < 1) {
5958             return;
5959         }
5960         this.setActiveItem(this.navItems[i-1]);
5961     },
5962     clearWasActive : function(except) {
5963         Roo.each(this.navItems, function(e) {
5964             if (e.tabId != except.tabId && e.was_active) {
5965                e.was_active = false;
5966                return false;
5967             }
5968             return true;
5969             
5970         });
5971     },
5972     getWasActive : function ()
5973     {
5974         var r = false;
5975         Roo.each(this.navItems, function(e) {
5976             if (e.was_active) {
5977                r = e;
5978                return false;
5979             }
5980             return true;
5981             
5982         });
5983         return r;
5984     }
5985     
5986     
5987 });
5988
5989  
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5991     
5992     groups: {},
5993      /**
5994     * register a Navigation Group
5995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996     */
5997     register : function(navgrp)
5998     {
5999         this.groups[navgrp.navId] = navgrp;
6000         
6001     },
6002     /**
6003     * fetch a Navigation Group based on the navigation ID
6004     * @param {string} the navgroup to add
6005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6006     */
6007     get: function(navId) {
6008         if (typeof(this.groups[navId]) == 'undefined') {
6009             return false;
6010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011         }
6012         return this.groups[navId] ;
6013     }
6014     
6015     
6016     
6017 });
6018
6019  /*
6020  * - LGPL
6021  *
6022  * row
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.NavItem
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Navbar.NavItem class
6030  * @cfg {String} href  link to
6031  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032  * @cfg {Boolean} button_outline show and outlined button
6033  * @cfg {String} html content of button
6034  * @cfg {String} badge text inside badge
6035  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036  * @cfg {String} glyphicon DEPRICATED - use fa
6037  * @cfg {String} icon DEPRICATED - use fa
6038  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039  * @cfg {Boolean} active Is item active
6040  * @cfg {Boolean} disabled Is item disabled
6041  * @cfg {String} linkcls  Link Class
6042  * @cfg {Boolean} preventDefault (true | false) default false
6043  * @cfg {String} tabId the tab that this item activates.
6044  * @cfg {String} tagtype (a|span) render as a href or span?
6045  * @cfg {Boolean} animateRef (true|false) link to element default false  
6046   
6047  * @constructor
6048  * Create a new Navbar Item
6049  * @param {Object} config The config object
6050  */
6051 Roo.bootstrap.NavItem = function(config){
6052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6053     this.addEvents({
6054         // raw events
6055         /**
6056          * @event click
6057          * The raw click event for the entire grid.
6058          * @param {Roo.EventObject} e
6059          */
6060         "click" : true,
6061          /**
6062             * @event changed
6063             * Fires when the active item active state changes
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {boolean} state the new state
6066              
6067          */
6068         'changed': true,
6069         /**
6070             * @event scrollto
6071             * Fires when scroll to element
6072             * @param {Roo.bootstrap.NavItem} this
6073             * @param {Object} options
6074             * @param {Roo.EventObject} e
6075              
6076          */
6077         'scrollto': true
6078     });
6079    
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6083     
6084     href: false,
6085     html: '',
6086     badge: '',
6087     icon: false,
6088     fa : false,
6089     glyphicon: false,
6090     active: false,
6091     preventDefault : false,
6092     tabId : false,
6093     tagtype : 'a',
6094     tag: 'li',
6095     disabled : false,
6096     animateRef : false,
6097     was_active : false,
6098     button_weight : '',
6099     button_outline : false,
6100     linkcls : '',
6101     navLink: false,
6102     
6103     getAutoCreate : function(){
6104          
6105         var cfg = {
6106             tag: this.tag,
6107             cls: 'nav-item'
6108         };
6109         
6110         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6111         
6112         if (this.active) {
6113             cfg.cls +=  ' active' ;
6114         }
6115         if (this.disabled) {
6116             cfg.cls += ' disabled';
6117         }
6118         
6119         // BS4 only?
6120         if (this.button_weight.length) {
6121             cfg.tag = this.href ? 'a' : 'button';
6122             cfg.html = this.html || '';
6123             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6124             if (this.href) {
6125                 cfg.href = this.href;
6126             }
6127             if (this.fa) {
6128                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6129             }
6130             
6131             // menu .. should add dropdown-menu class - so no need for carat..
6132             
6133             if (this.badge !== '') {
6134                  
6135                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6136             }
6137             return cfg;
6138         }
6139         
6140         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6141             cfg.cn = [
6142                 {
6143                     tag: this.tagtype,
6144                     href : this.href || "#",
6145                     html: this.html || ''
6146                 }
6147             ];
6148             if (this.tagtype == 'a') {
6149                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6150         
6151             }
6152             if (this.icon) {
6153                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if (this.fa) {
6156                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if(this.glyphicon) {
6159                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6160             }
6161             
6162             if (this.menu) {
6163                 
6164                 cfg.cn[0].html += " <span class='caret'></span>";
6165              
6166             }
6167             
6168             if (this.badge !== '') {
6169                  
6170                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6171             }
6172         }
6173         
6174         
6175         
6176         return cfg;
6177     },
6178     onRender : function(ct, position)
6179     {
6180        // Roo.log("Call onRender: " + this.xtype);
6181         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182             this.tag = 'div';
6183         }
6184         
6185         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186         this.navLink = this.el.select('.nav-link',true).first();
6187         return ret;
6188     },
6189       
6190     
6191     initEvents: function() 
6192     {
6193         if (typeof (this.menu) != 'undefined') {
6194             this.menu.parentType = this.xtype;
6195             this.menu.triggerEl = this.el;
6196             this.menu = this.addxtype(Roo.apply({}, this.menu));
6197         }
6198         
6199         this.el.on('click', this.onClick, this);
6200         
6201         //if(this.tagtype == 'span'){
6202         //    this.el.select('span',true).on('click', this.onClick, this);
6203         //}
6204        
6205         // at this point parent should be available..
6206         this.parent().register(this);
6207     },
6208     
6209     onClick : function(e)
6210     {
6211         if (e.getTarget('.dropdown-menu-item')) {
6212             // did you click on a menu itemm.... - then don't trigger onclick..
6213             return;
6214         }
6215         
6216         if(
6217                 this.preventDefault || 
6218                 this.href == '#' 
6219         ){
6220             Roo.log("NavItem - prevent Default?");
6221             e.preventDefault();
6222         }
6223         
6224         if (this.disabled) {
6225             return;
6226         }
6227         
6228         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229         if (tg && tg.transition) {
6230             Roo.log("waiting for the transitionend");
6231             return;
6232         }
6233         
6234         
6235         
6236         //Roo.log("fire event clicked");
6237         if(this.fireEvent('click', this, e) === false){
6238             return;
6239         };
6240         
6241         if(this.tagtype == 'span'){
6242             return;
6243         }
6244         
6245         //Roo.log(this.href);
6246         var ael = this.el.select('a',true).first();
6247         //Roo.log(ael);
6248         
6249         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252                 return; // ignore... - it's a 'hash' to another page.
6253             }
6254             Roo.log("NavItem - prevent Default?");
6255             e.preventDefault();
6256             this.scrollToElement(e);
6257         }
6258         
6259         
6260         var p =  this.parent();
6261    
6262         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263             if (typeof(p.setActiveItem) !== 'undefined') {
6264                 p.setActiveItem(this);
6265             }
6266         }
6267         
6268         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270             // remove the collapsed menu expand...
6271             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6272         }
6273     },
6274     
6275     isActive: function () {
6276         return this.active
6277     },
6278     setActive : function(state, fire, is_was_active)
6279     {
6280         if (this.active && !state && this.navId) {
6281             this.was_active = true;
6282             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6283             if (nv) {
6284                 nv.clearWasActive(this);
6285             }
6286             
6287         }
6288         this.active = state;
6289         
6290         if (!state ) {
6291             this.el.removeClass('active');
6292             this.navLink ? this.navLink.removeClass('active') : false;
6293         } else if (!this.el.hasClass('active')) {
6294             
6295             this.el.addClass('active');
6296             if (Roo.bootstrap.version == 4 && this.navLink ) {
6297                 this.navLink.addClass('active');
6298             }
6299             
6300         }
6301         if (fire) {
6302             this.fireEvent('changed', this, state);
6303         }
6304         
6305         // show a panel if it's registered and related..
6306         
6307         if (!this.navId || !this.tabId || !state || is_was_active) {
6308             return;
6309         }
6310         
6311         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312         if (!tg) {
6313             return;
6314         }
6315         var pan = tg.getPanelByName(this.tabId);
6316         if (!pan) {
6317             return;
6318         }
6319         // if we can not flip to new panel - go back to old nav highlight..
6320         if (false == tg.showPanel(pan)) {
6321             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6322             if (nv) {
6323                 var onav = nv.getWasActive();
6324                 if (onav) {
6325                     onav.setActive(true, false, true);
6326                 }
6327             }
6328             
6329         }
6330         
6331         
6332         
6333     },
6334      // this should not be here...
6335     setDisabled : function(state)
6336     {
6337         this.disabled = state;
6338         if (!state ) {
6339             this.el.removeClass('disabled');
6340         } else if (!this.el.hasClass('disabled')) {
6341             this.el.addClass('disabled');
6342         }
6343         
6344     },
6345     
6346     /**
6347      * Fetch the element to display the tooltip on.
6348      * @return {Roo.Element} defaults to this.el
6349      */
6350     tooltipEl : function()
6351     {
6352         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6353     },
6354     
6355     scrollToElement : function(e)
6356     {
6357         var c = document.body;
6358         
6359         /*
6360          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6361          */
6362         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363             c = document.documentElement;
6364         }
6365         
6366         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6367         
6368         if(!target){
6369             return;
6370         }
6371
6372         var o = target.calcOffsetsTo(c);
6373         
6374         var options = {
6375             target : target,
6376             value : o[1]
6377         };
6378         
6379         this.fireEvent('scrollto', this, options, e);
6380         
6381         Roo.get(c).scrollTo('top', options.value, true);
6382         
6383         return;
6384     }
6385 });
6386  
6387
6388  /*
6389  * - LGPL
6390  *
6391  * sidebar item
6392  *
6393  *  li
6394  *    <span> icon </span>
6395  *    <span> text </span>
6396  *    <span>badge </span>
6397  */
6398
6399 /**
6400  * @class Roo.bootstrap.NavSidebarItem
6401  * @extends Roo.bootstrap.NavItem
6402  * Bootstrap Navbar.NavSidebarItem class
6403  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404  * {Boolean} open is the menu open
6405  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407  * {String} buttonSize (sm|md|lg)the extra classes for the button
6408  * {Boolean} showArrow show arrow next to the text (default true)
6409  * @constructor
6410  * Create a new Navbar Button
6411  * @param {Object} config The config object
6412  */
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6415     this.addEvents({
6416         // raw events
6417         /**
6418          * @event click
6419          * The raw click event for the entire grid.
6420          * @param {Roo.EventObject} e
6421          */
6422         "click" : true,
6423          /**
6424             * @event changed
6425             * Fires when the active item active state changes
6426             * @param {Roo.bootstrap.NavSidebarItem} this
6427             * @param {boolean} state the new state
6428              
6429          */
6430         'changed': true
6431     });
6432    
6433 };
6434
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6436     
6437     badgeWeight : 'default',
6438     
6439     open: false,
6440     
6441     buttonView : false,
6442     
6443     buttonWeight : 'default',
6444     
6445     buttonSize : 'md',
6446     
6447     showArrow : true,
6448     
6449     getAutoCreate : function(){
6450         
6451         
6452         var a = {
6453                 tag: 'a',
6454                 href : this.href || '#',
6455                 cls: '',
6456                 html : '',
6457                 cn : []
6458         };
6459         
6460         if(this.buttonView){
6461             a = {
6462                 tag: 'button',
6463                 href : this.href || '#',
6464                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6465                 html : this.html,
6466                 cn : []
6467             };
6468         }
6469         
6470         var cfg = {
6471             tag: 'li',
6472             cls: '',
6473             cn: [ a ]
6474         };
6475         
6476         if (this.active) {
6477             cfg.cls += ' active';
6478         }
6479         
6480         if (this.disabled) {
6481             cfg.cls += ' disabled';
6482         }
6483         if (this.open) {
6484             cfg.cls += ' open x-open';
6485         }
6486         // left icon..
6487         if (this.glyphicon || this.icon) {
6488             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6489             a.cn.push({ tag : 'i', cls : c }) ;
6490         }
6491         
6492         if(!this.buttonView){
6493             var span = {
6494                 tag: 'span',
6495                 html : this.html || ''
6496             };
6497
6498             a.cn.push(span);
6499             
6500         }
6501         
6502         if (this.badge !== '') {
6503             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6504         }
6505         
6506         if (this.menu) {
6507             
6508             if(this.showArrow){
6509                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6510             }
6511             
6512             a.cls += ' dropdown-toggle treeview' ;
6513         }
6514         
6515         return cfg;
6516     },
6517     
6518     initEvents : function()
6519     { 
6520         if (typeof (this.menu) != 'undefined') {
6521             this.menu.parentType = this.xtype;
6522             this.menu.triggerEl = this.el;
6523             this.menu = this.addxtype(Roo.apply({}, this.menu));
6524         }
6525         
6526         this.el.on('click', this.onClick, this);
6527         
6528         if(this.badge !== ''){
6529             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6530         }
6531         
6532     },
6533     
6534     onClick : function(e)
6535     {
6536         if(this.disabled){
6537             e.preventDefault();
6538             return;
6539         }
6540         
6541         if(this.preventDefault){
6542             e.preventDefault();
6543         }
6544         
6545         this.fireEvent('click', this, e);
6546     },
6547     
6548     disable : function()
6549     {
6550         this.setDisabled(true);
6551     },
6552     
6553     enable : function()
6554     {
6555         this.setDisabled(false);
6556     },
6557     
6558     setDisabled : function(state)
6559     {
6560         if(this.disabled == state){
6561             return;
6562         }
6563         
6564         this.disabled = state;
6565         
6566         if (state) {
6567             this.el.addClass('disabled');
6568             return;
6569         }
6570         
6571         this.el.removeClass('disabled');
6572         
6573         return;
6574     },
6575     
6576     setActive : function(state)
6577     {
6578         if(this.active == state){
6579             return;
6580         }
6581         
6582         this.active = state;
6583         
6584         if (state) {
6585             this.el.addClass('active');
6586             return;
6587         }
6588         
6589         this.el.removeClass('active');
6590         
6591         return;
6592     },
6593     
6594     isActive: function () 
6595     {
6596         return this.active;
6597     },
6598     
6599     setBadge : function(str)
6600     {
6601         if(!this.badgeEl){
6602             return;
6603         }
6604         
6605         this.badgeEl.dom.innerHTML = str;
6606     }
6607     
6608    
6609      
6610  
6611 });
6612  
6613
6614  /*
6615  * - LGPL
6616  *
6617  *  Breadcrumb Nav
6618  * 
6619  */
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6621
6622
6623 /**
6624  * @class Roo.bootstrap.breadcrumb.Nav
6625  * @extends Roo.bootstrap.Component
6626  * Bootstrap Breadcrumb Nav Class
6627  *  
6628  * @children Roo.bootstrap.breadcrumb.Item
6629  * 
6630  * @constructor
6631  * Create a new breadcrumb.Nav
6632  * @param {Object} config The config object
6633  */
6634
6635
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6638     
6639     
6640 };
6641
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6643     
6644     getAutoCreate : function()
6645     {
6646
6647         var cfg = {
6648             tag: 'nav',
6649             cn : [
6650                 {
6651                     tag : 'ol',
6652                     cls : 'breadcrumb'
6653                 }
6654             ]
6655             
6656         };
6657           
6658         return cfg;
6659     },
6660     
6661     initEvents: function()
6662     {
6663         this.olEl = this.el.select('ol',true).first();    
6664     },
6665     getChildContainer : function()
6666     {
6667         return this.olEl;  
6668     }
6669     
6670 });
6671
6672  /*
6673  * - LGPL
6674  *
6675  *  Breadcrumb Item
6676  * 
6677  */
6678
6679
6680 /**
6681  * @class Roo.bootstrap.breadcrumb.Nav
6682  * @extends Roo.bootstrap.Component
6683  * Bootstrap Breadcrumb Nav Class
6684  *  
6685  * @children Roo.bootstrap.breadcrumb.Component
6686  * @cfg {String} html the content of the link.
6687  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688  * @cfg {Boolean} active is it active
6689
6690  * 
6691  * @constructor
6692  * Create a new breadcrumb.Nav
6693  * @param {Object} config The config object
6694  */
6695
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6698     this.addEvents({
6699         // img events
6700         /**
6701          * @event click
6702          * The img click event for the img.
6703          * @param {Roo.EventObject} e
6704          */
6705         "click" : true
6706     });
6707     
6708 };
6709
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6711     
6712     href: false,
6713     html : '',
6714     
6715     getAutoCreate : function()
6716     {
6717
6718         var cfg = {
6719             tag: 'li',
6720             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6721         };
6722         if (this.href !== false) {
6723             cfg.cn = [{
6724                 tag : 'a',
6725                 href : this.href,
6726                 html : this.html
6727             }];
6728         } else {
6729             cfg.html = this.html;
6730         }
6731         
6732         return cfg;
6733     },
6734     
6735     initEvents: function()
6736     {
6737         if (this.href) {
6738             this.el.select('a', true).first().on('click',this.onClick, this)
6739         }
6740         
6741     },
6742     onClick : function(e)
6743     {
6744         e.preventDefault();
6745         this.fireEvent('click',this,  e);
6746     }
6747     
6748 });
6749
6750  /*
6751  * - LGPL
6752  *
6753  * row
6754  * 
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.Row
6759  * @extends Roo.bootstrap.Component
6760  * Bootstrap Row class (contains columns...)
6761  * 
6762  * @constructor
6763  * Create a new Row
6764  * @param {Object} config The config object
6765  */
6766
6767 Roo.bootstrap.Row = function(config){
6768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6769 };
6770
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6772     
6773     getAutoCreate : function(){
6774        return {
6775             cls: 'row clearfix'
6776        };
6777     }
6778     
6779     
6780 });
6781
6782  
6783
6784  /*
6785  * - LGPL
6786  *
6787  * pagination
6788  * 
6789  */
6790
6791 /**
6792  * @class Roo.bootstrap.Pagination
6793  * @extends Roo.bootstrap.Component
6794  * Bootstrap Pagination class
6795  * @cfg {String} size xs | sm | md | lg
6796  * @cfg {Boolean} inverse false | true
6797  * 
6798  * @constructor
6799  * Create a new Pagination
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Pagination = function(config){
6804     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6808     
6809     cls: false,
6810     size: false,
6811     inverse: false,
6812     
6813     getAutoCreate : function(){
6814         var cfg = {
6815             tag: 'ul',
6816                 cls: 'pagination'
6817         };
6818         if (this.inverse) {
6819             cfg.cls += ' inverse';
6820         }
6821         if (this.html) {
6822             cfg.html=this.html;
6823         }
6824         if (this.cls) {
6825             cfg.cls += " " + this.cls;
6826         }
6827         return cfg;
6828     }
6829    
6830 });
6831
6832  
6833
6834  /*
6835  * - LGPL
6836  *
6837  * Pagination item
6838  * 
6839  */
6840
6841
6842 /**
6843  * @class Roo.bootstrap.PaginationItem
6844  * @extends Roo.bootstrap.Component
6845  * Bootstrap PaginationItem class
6846  * @cfg {String} html text
6847  * @cfg {String} href the link
6848  * @cfg {Boolean} preventDefault (true | false) default true
6849  * @cfg {Boolean} active (true | false) default false
6850  * @cfg {Boolean} disabled default false
6851  * 
6852  * 
6853  * @constructor
6854  * Create a new PaginationItem
6855  * @param {Object} config The config object
6856  */
6857
6858
6859 Roo.bootstrap.PaginationItem = function(config){
6860     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6861     this.addEvents({
6862         // raw events
6863         /**
6864          * @event click
6865          * The raw click event for the entire grid.
6866          * @param {Roo.EventObject} e
6867          */
6868         "click" : true
6869     });
6870 };
6871
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6873     
6874     href : false,
6875     html : false,
6876     preventDefault: true,
6877     active : false,
6878     cls : false,
6879     disabled: false,
6880     
6881     getAutoCreate : function(){
6882         var cfg= {
6883             tag: 'li',
6884             cn: [
6885                 {
6886                     tag : 'a',
6887                     href : this.href ? this.href : '#',
6888                     html : this.html ? this.html : ''
6889                 }
6890             ]
6891         };
6892         
6893         if(this.cls){
6894             cfg.cls = this.cls;
6895         }
6896         
6897         if(this.disabled){
6898             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899         }
6900         
6901         if(this.active){
6902             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6903         }
6904         
6905         return cfg;
6906     },
6907     
6908     initEvents: function() {
6909         
6910         this.el.on('click', this.onClick, this);
6911         
6912     },
6913     onClick : function(e)
6914     {
6915         Roo.log('PaginationItem on click ');
6916         if(this.preventDefault){
6917             e.preventDefault();
6918         }
6919         
6920         if(this.disabled){
6921             return;
6922         }
6923         
6924         this.fireEvent('click', this, e);
6925     }
6926    
6927 });
6928
6929  
6930
6931  /*
6932  * - LGPL
6933  *
6934  * slider
6935  * 
6936  */
6937
6938
6939 /**
6940  * @class Roo.bootstrap.Slider
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap Slider class
6943  *    
6944  * @constructor
6945  * Create a new Slider
6946  * @param {Object} config The config object
6947  */
6948
6949 Roo.bootstrap.Slider = function(config){
6950     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6951 };
6952
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6954     
6955     getAutoCreate : function(){
6956         
6957         var cfg = {
6958             tag: 'div',
6959             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960             cn: [
6961                 {
6962                     tag: 'a',
6963                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6964                 }
6965             ]
6966         };
6967         
6968         return cfg;
6969     }
6970    
6971 });
6972
6973  /*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983  
6984
6985 /**
6986  * @class Roo.grid.ColumnModel
6987  * @extends Roo.util.Observable
6988  * This is the default implementation of a ColumnModel used by the Grid. It defines
6989  * the columns in the grid.
6990  * <br>Usage:<br>
6991  <pre><code>
6992  var colModel = new Roo.grid.ColumnModel([
6993         {header: "Ticker", width: 60, sortable: true, locked: true},
6994         {header: "Company Name", width: 150, sortable: true},
6995         {header: "Market Cap.", width: 100, sortable: true},
6996         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997         {header: "Employees", width: 100, sortable: true, resizable: false}
6998  ]);
6999  </code></pre>
7000  * <p>
7001  
7002  * The config options listed for this class are options which may appear in each
7003  * individual column definition.
7004  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7005  * @constructor
7006  * @param {Object} config An Array of column config objects. See this class's
7007  * config objects for details.
7008 */
7009 Roo.grid.ColumnModel = function(config){
7010         /**
7011      * The config passed into the constructor
7012      */
7013     this.config = config;
7014     this.lookup = {};
7015
7016     // if no id, create one
7017     // if the column does not have a dataIndex mapping,
7018     // map it to the order it is in the config
7019     for(var i = 0, len = config.length; i < len; i++){
7020         var c = config[i];
7021         if(typeof c.dataIndex == "undefined"){
7022             c.dataIndex = i;
7023         }
7024         if(typeof c.renderer == "string"){
7025             c.renderer = Roo.util.Format[c.renderer];
7026         }
7027         if(typeof c.id == "undefined"){
7028             c.id = Roo.id();
7029         }
7030         if(c.editor && c.editor.xtype){
7031             c.editor  = Roo.factory(c.editor, Roo.grid);
7032         }
7033         if(c.editor && c.editor.isFormField){
7034             c.editor = new Roo.grid.GridEditor(c.editor);
7035         }
7036         this.lookup[c.id] = c;
7037     }
7038
7039     /**
7040      * The width of columns which have no width specified (defaults to 100)
7041      * @type Number
7042      */
7043     this.defaultWidth = 100;
7044
7045     /**
7046      * Default sortable of columns which have no sortable specified (defaults to false)
7047      * @type Boolean
7048      */
7049     this.defaultSortable = false;
7050
7051     this.addEvents({
7052         /**
7053              * @event widthchange
7054              * Fires when the width of a column changes.
7055              * @param {ColumnModel} this
7056              * @param {Number} columnIndex The column index
7057              * @param {Number} newWidth The new width
7058              */
7059             "widthchange": true,
7060         /**
7061              * @event headerchange
7062              * Fires when the text of a header changes.
7063              * @param {ColumnModel} this
7064              * @param {Number} columnIndex The column index
7065              * @param {Number} newText The new header text
7066              */
7067             "headerchange": true,
7068         /**
7069              * @event hiddenchange
7070              * Fires when a column is hidden or "unhidden".
7071              * @param {ColumnModel} this
7072              * @param {Number} columnIndex The column index
7073              * @param {Boolean} hidden true if hidden, false otherwise
7074              */
7075             "hiddenchange": true,
7076             /**
7077          * @event columnmoved
7078          * Fires when a column is moved.
7079          * @param {ColumnModel} this
7080          * @param {Number} oldIndex
7081          * @param {Number} newIndex
7082          */
7083         "columnmoved" : true,
7084         /**
7085          * @event columlockchange
7086          * Fires when a column's locked state is changed
7087          * @param {ColumnModel} this
7088          * @param {Number} colIndex
7089          * @param {Boolean} locked true if locked
7090          */
7091         "columnlockchange" : true
7092     });
7093     Roo.grid.ColumnModel.superclass.constructor.call(this);
7094 };
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7096     /**
7097      * @cfg {String} header The header text to display in the Grid view.
7098      */
7099     /**
7100      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102      * specified, the column's index is used as an index into the Record's data Array.
7103      */
7104     /**
7105      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7107      */
7108     /**
7109      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110      * Defaults to the value of the {@link #defaultSortable} property.
7111      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7112      */
7113     /**
7114      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7121      */
7122     /**
7123      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7124      */
7125     /**
7126      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7130      */
7131        /**
7132      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7133      */
7134     /**
7135      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} cursor (Optional)
7142      */
7143     /**
7144      * @cfg {String} tooltip (Optional)
7145      */
7146     /**
7147      * @cfg {Number} xs (Optional)
7148      */
7149     /**
7150      * @cfg {Number} sm (Optional)
7151      */
7152     /**
7153      * @cfg {Number} md (Optional)
7154      */
7155     /**
7156      * @cfg {Number} lg (Optional)
7157      */
7158     /**
7159      * Returns the id of the column at the specified index.
7160      * @param {Number} index The column index
7161      * @return {String} the id
7162      */
7163     getColumnId : function(index){
7164         return this.config[index].id;
7165     },
7166
7167     /**
7168      * Returns the column for a specified id.
7169      * @param {String} id The column id
7170      * @return {Object} the column
7171      */
7172     getColumnById : function(id){
7173         return this.lookup[id];
7174     },
7175
7176     
7177     /**
7178      * Returns the column for a specified dataIndex.
7179      * @param {String} dataIndex The column dataIndex
7180      * @return {Object|Boolean} the column or false if not found
7181      */
7182     getColumnByDataIndex: function(dataIndex){
7183         var index = this.findColumnIndex(dataIndex);
7184         return index > -1 ? this.config[index] : false;
7185     },
7186     
7187     /**
7188      * Returns the index for a specified column id.
7189      * @param {String} id The column id
7190      * @return {Number} the index, or -1 if not found
7191      */
7192     getIndexById : function(id){
7193         for(var i = 0, len = this.config.length; i < len; i++){
7194             if(this.config[i].id == id){
7195                 return i;
7196             }
7197         }
7198         return -1;
7199     },
7200     
7201     /**
7202      * Returns the index for a specified column dataIndex.
7203      * @param {String} dataIndex The column dataIndex
7204      * @return {Number} the index, or -1 if not found
7205      */
7206     
7207     findColumnIndex : function(dataIndex){
7208         for(var i = 0, len = this.config.length; i < len; i++){
7209             if(this.config[i].dataIndex == dataIndex){
7210                 return i;
7211             }
7212         }
7213         return -1;
7214     },
7215     
7216     
7217     moveColumn : function(oldIndex, newIndex){
7218         var c = this.config[oldIndex];
7219         this.config.splice(oldIndex, 1);
7220         this.config.splice(newIndex, 0, c);
7221         this.dataMap = null;
7222         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7223     },
7224
7225     isLocked : function(colIndex){
7226         return this.config[colIndex].locked === true;
7227     },
7228
7229     setLocked : function(colIndex, value, suppressEvent){
7230         if(this.isLocked(colIndex) == value){
7231             return;
7232         }
7233         this.config[colIndex].locked = value;
7234         if(!suppressEvent){
7235             this.fireEvent("columnlockchange", this, colIndex, value);
7236         }
7237     },
7238
7239     getTotalLockedWidth : function(){
7240         var totalWidth = 0;
7241         for(var i = 0; i < this.config.length; i++){
7242             if(this.isLocked(i) && !this.isHidden(i)){
7243                 this.totalWidth += this.getColumnWidth(i);
7244             }
7245         }
7246         return totalWidth;
7247     },
7248
7249     getLockedCount : function(){
7250         for(var i = 0, len = this.config.length; i < len; i++){
7251             if(!this.isLocked(i)){
7252                 return i;
7253             }
7254         }
7255         
7256         return this.config.length;
7257     },
7258
7259     /**
7260      * Returns the number of columns.
7261      * @return {Number}
7262      */
7263     getColumnCount : function(visibleOnly){
7264         if(visibleOnly === true){
7265             var c = 0;
7266             for(var i = 0, len = this.config.length; i < len; i++){
7267                 if(!this.isHidden(i)){
7268                     c++;
7269                 }
7270             }
7271             return c;
7272         }
7273         return this.config.length;
7274     },
7275
7276     /**
7277      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278      * @param {Function} fn
7279      * @param {Object} scope (optional)
7280      * @return {Array} result
7281      */
7282     getColumnsBy : function(fn, scope){
7283         var r = [];
7284         for(var i = 0, len = this.config.length; i < len; i++){
7285             var c = this.config[i];
7286             if(fn.call(scope||this, c, i) === true){
7287                 r[r.length] = c;
7288             }
7289         }
7290         return r;
7291     },
7292
7293     /**
7294      * Returns true if the specified column is sortable.
7295      * @param {Number} col The column index
7296      * @return {Boolean}
7297      */
7298     isSortable : function(col){
7299         if(typeof this.config[col].sortable == "undefined"){
7300             return this.defaultSortable;
7301         }
7302         return this.config[col].sortable;
7303     },
7304
7305     /**
7306      * Returns the rendering (formatting) function defined for the column.
7307      * @param {Number} col The column index.
7308      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7309      */
7310     getRenderer : function(col){
7311         if(!this.config[col].renderer){
7312             return Roo.grid.ColumnModel.defaultRenderer;
7313         }
7314         return this.config[col].renderer;
7315     },
7316
7317     /**
7318      * Sets the rendering (formatting) function for a column.
7319      * @param {Number} col The column index
7320      * @param {Function} fn The function to use to process the cell's raw data
7321      * to return HTML markup for the grid view. The render function is called with
7322      * the following parameters:<ul>
7323      * <li>Data value.</li>
7324      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325      * <li>css A CSS style string to apply to the table cell.</li>
7326      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328      * <li>Row index</li>
7329      * <li>Column index</li>
7330      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7331      */
7332     setRenderer : function(col, fn){
7333         this.config[col].renderer = fn;
7334     },
7335
7336     /**
7337      * Returns the width for the specified column.
7338      * @param {Number} col The column index
7339      * @return {Number}
7340      */
7341     getColumnWidth : function(col){
7342         return this.config[col].width * 1 || this.defaultWidth;
7343     },
7344
7345     /**
7346      * Sets the width for a column.
7347      * @param {Number} col The column index
7348      * @param {Number} width The new width
7349      */
7350     setColumnWidth : function(col, width, suppressEvent){
7351         this.config[col].width = width;
7352         this.totalWidth = null;
7353         if(!suppressEvent){
7354              this.fireEvent("widthchange", this, col, width);
7355         }
7356     },
7357
7358     /**
7359      * Returns the total width of all columns.
7360      * @param {Boolean} includeHidden True to include hidden column widths
7361      * @return {Number}
7362      */
7363     getTotalWidth : function(includeHidden){
7364         if(!this.totalWidth){
7365             this.totalWidth = 0;
7366             for(var i = 0, len = this.config.length; i < len; i++){
7367                 if(includeHidden || !this.isHidden(i)){
7368                     this.totalWidth += this.getColumnWidth(i);
7369                 }
7370             }
7371         }
7372         return this.totalWidth;
7373     },
7374
7375     /**
7376      * Returns the header for the specified column.
7377      * @param {Number} col The column index
7378      * @return {String}
7379      */
7380     getColumnHeader : function(col){
7381         return this.config[col].header;
7382     },
7383
7384     /**
7385      * Sets the header for a column.
7386      * @param {Number} col The column index
7387      * @param {String} header The new header
7388      */
7389     setColumnHeader : function(col, header){
7390         this.config[col].header = header;
7391         this.fireEvent("headerchange", this, col, header);
7392     },
7393
7394     /**
7395      * Returns the tooltip for the specified column.
7396      * @param {Number} col The column index
7397      * @return {String}
7398      */
7399     getColumnTooltip : function(col){
7400             return this.config[col].tooltip;
7401     },
7402     /**
7403      * Sets the tooltip for a column.
7404      * @param {Number} col The column index
7405      * @param {String} tooltip The new tooltip
7406      */
7407     setColumnTooltip : function(col, tooltip){
7408             this.config[col].tooltip = tooltip;
7409     },
7410
7411     /**
7412      * Returns the dataIndex for the specified column.
7413      * @param {Number} col The column index
7414      * @return {Number}
7415      */
7416     getDataIndex : function(col){
7417         return this.config[col].dataIndex;
7418     },
7419
7420     /**
7421      * Sets the dataIndex for a column.
7422      * @param {Number} col The column index
7423      * @param {Number} dataIndex The new dataIndex
7424      */
7425     setDataIndex : function(col, dataIndex){
7426         this.config[col].dataIndex = dataIndex;
7427     },
7428
7429     
7430     
7431     /**
7432      * Returns true if the cell is editable.
7433      * @param {Number} colIndex The column index
7434      * @param {Number} rowIndex The row index - this is nto actually used..?
7435      * @return {Boolean}
7436      */
7437     isCellEditable : function(colIndex, rowIndex){
7438         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439     },
7440
7441     /**
7442      * Returns the editor defined for the cell/column.
7443      * return false or null to disable editing.
7444      * @param {Number} colIndex The column index
7445      * @param {Number} rowIndex The row index
7446      * @return {Object}
7447      */
7448     getCellEditor : function(colIndex, rowIndex){
7449         return this.config[colIndex].editor;
7450     },
7451
7452     /**
7453      * Sets if a column is editable.
7454      * @param {Number} col The column index
7455      * @param {Boolean} editable True if the column is editable
7456      */
7457     setEditable : function(col, editable){
7458         this.config[col].editable = editable;
7459     },
7460
7461
7462     /**
7463      * Returns true if the column is hidden.
7464      * @param {Number} colIndex The column index
7465      * @return {Boolean}
7466      */
7467     isHidden : function(colIndex){
7468         return this.config[colIndex].hidden;
7469     },
7470
7471
7472     /**
7473      * Returns true if the column width cannot be changed
7474      */
7475     isFixed : function(colIndex){
7476         return this.config[colIndex].fixed;
7477     },
7478
7479     /**
7480      * Returns true if the column can be resized
7481      * @return {Boolean}
7482      */
7483     isResizable : function(colIndex){
7484         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7485     },
7486     /**
7487      * Sets if a column is hidden.
7488      * @param {Number} colIndex The column index
7489      * @param {Boolean} hidden True if the column is hidden
7490      */
7491     setHidden : function(colIndex, hidden){
7492         this.config[colIndex].hidden = hidden;
7493         this.totalWidth = null;
7494         this.fireEvent("hiddenchange", this, colIndex, hidden);
7495     },
7496
7497     /**
7498      * Sets the editor for a column.
7499      * @param {Number} col The column index
7500      * @param {Object} editor The editor object
7501      */
7502     setEditor : function(col, editor){
7503         this.config[col].editor = editor;
7504     }
7505 });
7506
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7508 {
7509     if(typeof value == "object") {
7510         return value;
7511     }
7512         if(typeof value == "string" && value.length < 1){
7513             return "&#160;";
7514         }
7515     
7516         return String.format("{0}", value);
7517 };
7518
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7521 /*
7522  * Based on:
7523  * Ext JS Library 1.1.1
7524  * Copyright(c) 2006-2007, Ext JS, LLC.
7525  *
7526  * Originally Released Under LGPL - original licence link has changed is not relivant.
7527  *
7528  * Fork - LGPL
7529  * <script type="text/javascript">
7530  */
7531  
7532 /**
7533  * @class Roo.LoadMask
7534  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7535  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7537  * element's UpdateManager load indicator and will be destroyed after the initial load.
7538  * @constructor
7539  * Create a new LoadMask
7540  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541  * @param {Object} config The config object
7542  */
7543 Roo.LoadMask = function(el, config){
7544     this.el = Roo.get(el);
7545     Roo.apply(this, config);
7546     if(this.store){
7547         this.store.on('beforeload', this.onBeforeLoad, this);
7548         this.store.on('load', this.onLoad, this);
7549         this.store.on('loadexception', this.onLoadException, this);
7550         this.removeMask = false;
7551     }else{
7552         var um = this.el.getUpdateManager();
7553         um.showLoadIndicator = false; // disable the default indicator
7554         um.on('beforeupdate', this.onBeforeLoad, this);
7555         um.on('update', this.onLoad, this);
7556         um.on('failure', this.onLoad, this);
7557         this.removeMask = true;
7558     }
7559 };
7560
7561 Roo.LoadMask.prototype = {
7562     /**
7563      * @cfg {Boolean} removeMask
7564      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7566      */
7567     /**
7568      * @cfg {String} msg
7569      * The text to display in a centered loading message box (defaults to 'Loading...')
7570      */
7571     msg : 'Loading...',
7572     /**
7573      * @cfg {String} msgCls
7574      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7575      */
7576     msgCls : 'x-mask-loading',
7577
7578     /**
7579      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7580      * @type Boolean
7581      */
7582     disabled: false,
7583
7584     /**
7585      * Disables the mask to prevent it from being displayed
7586      */
7587     disable : function(){
7588        this.disabled = true;
7589     },
7590
7591     /**
7592      * Enables the mask so that it can be displayed
7593      */
7594     enable : function(){
7595         this.disabled = false;
7596     },
7597     
7598     onLoadException : function()
7599     {
7600         Roo.log(arguments);
7601         
7602         if (typeof(arguments[3]) != 'undefined') {
7603             Roo.MessageBox.alert("Error loading",arguments[3]);
7604         } 
7605         /*
7606         try {
7607             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7609             }   
7610         } catch(e) {
7611             
7612         }
7613         */
7614     
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617     // private
7618     onLoad : function()
7619     {
7620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621     },
7622
7623     // private
7624     onBeforeLoad : function(){
7625         if(!this.disabled){
7626             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7627         }
7628     },
7629
7630     // private
7631     destroy : function(){
7632         if(this.store){
7633             this.store.un('beforeload', this.onBeforeLoad, this);
7634             this.store.un('load', this.onLoad, this);
7635             this.store.un('loadexception', this.onLoadException, this);
7636         }else{
7637             var um = this.el.getUpdateManager();
7638             um.un('beforeupdate', this.onBeforeLoad, this);
7639             um.un('update', this.onLoad, this);
7640             um.un('failure', this.onLoad, this);
7641         }
7642     }
7643 };/*
7644  * - LGPL
7645  *
7646  * table
7647  * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.Table
7652  * @extends Roo.bootstrap.Component
7653  * Bootstrap Table class
7654  * @cfg {String} cls table class
7655  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656  * @cfg {String} bgcolor Specifies the background color for a table
7657  * @cfg {Number} border Specifies whether the table cells should have borders or not
7658  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659  * @cfg {Number} cellspacing Specifies the space between cells
7660  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662  * @cfg {String} sortable Specifies that the table should be sortable
7663  * @cfg {String} summary Specifies a summary of the content of a table
7664  * @cfg {Number} width Specifies the width of a table
7665  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7666  * 
7667  * @cfg {boolean} striped Should the rows be alternative striped
7668  * @cfg {boolean} bordered Add borders to the table
7669  * @cfg {boolean} hover Add hover highlighting
7670  * @cfg {boolean} condensed Format condensed
7671  * @cfg {boolean} responsive Format condensed
7672  * @cfg {Boolean} loadMask (true|false) default false
7673  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675  * @cfg {Boolean} rowSelection (true|false) default false
7676  * @cfg {Boolean} cellSelection (true|false) default false
7677  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7679  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7680  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7681  
7682  * 
7683  * @constructor
7684  * Create a new Table
7685  * @param {Object} config The config object
7686  */
7687
7688 Roo.bootstrap.Table = function(config){
7689     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7690     
7691   
7692     
7693     // BC...
7694     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7698     
7699     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7700     if (this.sm) {
7701         this.sm.grid = this;
7702         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703         this.sm = this.selModel;
7704         this.sm.xmodule = this.xmodule || false;
7705     }
7706     
7707     if (this.cm && typeof(this.cm.config) == 'undefined') {
7708         this.colModel = new Roo.grid.ColumnModel(this.cm);
7709         this.cm = this.colModel;
7710         this.cm.xmodule = this.xmodule || false;
7711     }
7712     if (this.store) {
7713         this.store= Roo.factory(this.store, Roo.data);
7714         this.ds = this.store;
7715         this.ds.xmodule = this.xmodule || false;
7716          
7717     }
7718     if (this.footer && this.store) {
7719         this.footer.dataSource = this.ds;
7720         this.footer = Roo.factory(this.footer);
7721     }
7722     
7723     /** @private */
7724     this.addEvents({
7725         /**
7726          * @event cellclick
7727          * Fires when a cell is clicked
7728          * @param {Roo.bootstrap.Table} this
7729          * @param {Roo.Element} el
7730          * @param {Number} rowIndex
7731          * @param {Number} columnIndex
7732          * @param {Roo.EventObject} e
7733          */
7734         "cellclick" : true,
7735         /**
7736          * @event celldblclick
7737          * Fires when a cell is double clicked
7738          * @param {Roo.bootstrap.Table} this
7739          * @param {Roo.Element} el
7740          * @param {Number} rowIndex
7741          * @param {Number} columnIndex
7742          * @param {Roo.EventObject} e
7743          */
7744         "celldblclick" : true,
7745         /**
7746          * @event rowclick
7747          * Fires when a row is clicked
7748          * @param {Roo.bootstrap.Table} this
7749          * @param {Roo.Element} el
7750          * @param {Number} rowIndex
7751          * @param {Roo.EventObject} e
7752          */
7753         "rowclick" : true,
7754         /**
7755          * @event rowdblclick
7756          * Fires when a row is double clicked
7757          * @param {Roo.bootstrap.Table} this
7758          * @param {Roo.Element} el
7759          * @param {Number} rowIndex
7760          * @param {Roo.EventObject} e
7761          */
7762         "rowdblclick" : true,
7763         /**
7764          * @event mouseover
7765          * Fires when a mouseover occur
7766          * @param {Roo.bootstrap.Table} this
7767          * @param {Roo.Element} el
7768          * @param {Number} rowIndex
7769          * @param {Number} columnIndex
7770          * @param {Roo.EventObject} e
7771          */
7772         "mouseover" : true,
7773         /**
7774          * @event mouseout
7775          * Fires when a mouseout occur
7776          * @param {Roo.bootstrap.Table} this
7777          * @param {Roo.Element} el
7778          * @param {Number} rowIndex
7779          * @param {Number} columnIndex
7780          * @param {Roo.EventObject} e
7781          */
7782         "mouseout" : true,
7783         /**
7784          * @event rowclass
7785          * Fires when a row is rendered, so you can change add a style to it.
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7788          */
7789         'rowclass' : true,
7790           /**
7791          * @event rowsrendered
7792          * Fires when all the  rows have been rendered
7793          * @param {Roo.bootstrap.Table} this
7794          */
7795         'rowsrendered' : true,
7796         /**
7797          * @event contextmenu
7798          * The raw contextmenu event for the entire grid.
7799          * @param {Roo.EventObject} e
7800          */
7801         "contextmenu" : true,
7802         /**
7803          * @event rowcontextmenu
7804          * Fires when a row is right clicked
7805          * @param {Roo.bootstrap.Table} this
7806          * @param {Number} rowIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "rowcontextmenu" : true,
7810         /**
7811          * @event cellcontextmenu
7812          * Fires when a cell is right clicked
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Number} rowIndex
7815          * @param {Number} cellIndex
7816          * @param {Roo.EventObject} e
7817          */
7818          "cellcontextmenu" : true,
7819          /**
7820          * @event headercontextmenu
7821          * Fires when a header is right clicked
7822          * @param {Roo.bootstrap.Table} this
7823          * @param {Number} columnIndex
7824          * @param {Roo.EventObject} e
7825          */
7826         "headercontextmenu" : true
7827     });
7828 };
7829
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7831     
7832     cls: false,
7833     align: false,
7834     bgcolor: false,
7835     border: false,
7836     cellpadding: false,
7837     cellspacing: false,
7838     frame: false,
7839     rules: false,
7840     sortable: false,
7841     summary: false,
7842     width: false,
7843     striped : false,
7844     scrollBody : false,
7845     bordered: false,
7846     hover:  false,
7847     condensed : false,
7848     responsive : false,
7849     sm : false,
7850     cm : false,
7851     store : false,
7852     loadMask : false,
7853     footerShow : true,
7854     headerShow : true,
7855   
7856     rowSelection : false,
7857     cellSelection : false,
7858     layout : false,
7859     
7860     // Roo.Element - the tbody
7861     mainBody: false,
7862     // Roo.Element - thead element
7863     mainHead: false,
7864     
7865     container: false, // used by gridpanel...
7866     
7867     lazyLoad : false,
7868     
7869     CSS : Roo.util.CSS,
7870     
7871     auto_hide_footer : false,
7872     
7873     getAutoCreate : function()
7874     {
7875         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7876         
7877         cfg = {
7878             tag: 'table',
7879             cls : 'table',
7880             cn : []
7881         };
7882         if (this.scrollBody) {
7883             cfg.cls += ' table-body-fixed';
7884         }    
7885         if (this.striped) {
7886             cfg.cls += ' table-striped';
7887         }
7888         
7889         if (this.hover) {
7890             cfg.cls += ' table-hover';
7891         }
7892         if (this.bordered) {
7893             cfg.cls += ' table-bordered';
7894         }
7895         if (this.condensed) {
7896             cfg.cls += ' table-condensed';
7897         }
7898         if (this.responsive) {
7899             cfg.cls += ' table-responsive';
7900         }
7901         
7902         if (this.cls) {
7903             cfg.cls+=  ' ' +this.cls;
7904         }
7905         
7906         // this lot should be simplifed...
7907         var _t = this;
7908         var cp = [
7909             'align',
7910             'bgcolor',
7911             'border',
7912             'cellpadding',
7913             'cellspacing',
7914             'frame',
7915             'rules',
7916             'sortable',
7917             'summary',
7918             'width'
7919         ].forEach(function(k) {
7920             if (_t[k]) {
7921                 cfg[k] = _t[k];
7922             }
7923         });
7924         
7925         
7926         if (this.layout) {
7927             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7928         }
7929         
7930         if(this.store || this.cm){
7931             if(this.headerShow){
7932                 cfg.cn.push(this.renderHeader());
7933             }
7934             
7935             cfg.cn.push(this.renderBody());
7936             
7937             if(this.footerShow){
7938                 cfg.cn.push(this.renderFooter());
7939             }
7940             // where does this come from?
7941             //cfg.cls+=  ' TableGrid';
7942         }
7943         
7944         return { cn : [ cfg ] };
7945     },
7946     
7947     initEvents : function()
7948     {   
7949         if(!this.store || !this.cm){
7950             return;
7951         }
7952         if (this.selModel) {
7953             this.selModel.initEvents();
7954         }
7955         
7956         
7957         //Roo.log('initEvents with ds!!!!');
7958         
7959         this.mainBody = this.el.select('tbody', true).first();
7960         this.mainHead = this.el.select('thead', true).first();
7961         this.mainFoot = this.el.select('tfoot', true).first();
7962         
7963         
7964         
7965         var _this = this;
7966         
7967         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968             e.on('click', _this.sort, _this);
7969         });
7970         
7971         this.mainBody.on("click", this.onClick, this);
7972         this.mainBody.on("dblclick", this.onDblClick, this);
7973         
7974         // why is this done????? = it breaks dialogs??
7975         //this.parent().el.setStyle('position', 'relative');
7976         
7977         
7978         if (this.footer) {
7979             this.footer.parentId = this.id;
7980             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7981             
7982             if(this.lazyLoad){
7983                 this.el.select('tfoot tr td').first().addClass('hide');
7984             }
7985         } 
7986         
7987         if(this.loadMask) {
7988             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7989         }
7990         
7991         this.store.on('load', this.onLoad, this);
7992         this.store.on('beforeload', this.onBeforeLoad, this);
7993         this.store.on('update', this.onUpdate, this);
7994         this.store.on('add', this.onAdd, this);
7995         this.store.on("clear", this.clear, this);
7996         
7997         this.el.on("contextmenu", this.onContextMenu, this);
7998         
7999         this.mainBody.on('scroll', this.onBodyScroll, this);
8000         
8001         this.cm.on("headerchange", this.onHeaderChange, this);
8002         
8003         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004         
8005     },
8006     
8007     onContextMenu : function(e, t)
8008     {
8009         this.processEvent("contextmenu", e);
8010     },
8011     
8012     processEvent : function(name, e)
8013     {
8014         if (name != 'touchstart' ) {
8015             this.fireEvent(name, e);    
8016         }
8017         
8018         var t = e.getTarget();
8019         
8020         var cell = Roo.get(t);
8021         
8022         if(!cell){
8023             return;
8024         }
8025         
8026         if(cell.findParent('tfoot', false, true)){
8027             return;
8028         }
8029         
8030         if(cell.findParent('thead', false, true)){
8031             
8032             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033                 cell = Roo.get(t).findParent('th', false, true);
8034                 if (!cell) {
8035                     Roo.log("failed to find th in thead?");
8036                     Roo.log(e.getTarget());
8037                     return;
8038                 }
8039             }
8040             
8041             var cellIndex = cell.dom.cellIndex;
8042             
8043             var ename = name == 'touchstart' ? 'click' : name;
8044             this.fireEvent("header" + ename, this, cellIndex, e);
8045             
8046             return;
8047         }
8048         
8049         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050             cell = Roo.get(t).findParent('td', false, true);
8051             if (!cell) {
8052                 Roo.log("failed to find th in tbody?");
8053                 Roo.log(e.getTarget());
8054                 return;
8055             }
8056         }
8057         
8058         var row = cell.findParent('tr', false, true);
8059         var cellIndex = cell.dom.cellIndex;
8060         var rowIndex = row.dom.rowIndex - 1;
8061         
8062         if(row !== false){
8063             
8064             this.fireEvent("row" + name, this, rowIndex, e);
8065             
8066             if(cell !== false){
8067             
8068                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8069             }
8070         }
8071         
8072     },
8073     
8074     onMouseover : function(e, el)
8075     {
8076         var cell = Roo.get(el);
8077         
8078         if(!cell){
8079             return;
8080         }
8081         
8082         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083             cell = cell.findParent('td', false, true);
8084         }
8085         
8086         var row = cell.findParent('tr', false, true);
8087         var cellIndex = cell.dom.cellIndex;
8088         var rowIndex = row.dom.rowIndex - 1; // start from 0
8089         
8090         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091         
8092     },
8093     
8094     onMouseout : function(e, el)
8095     {
8096         var cell = Roo.get(el);
8097         
8098         if(!cell){
8099             return;
8100         }
8101         
8102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103             cell = cell.findParent('td', false, true);
8104         }
8105         
8106         var row = cell.findParent('tr', false, true);
8107         var cellIndex = cell.dom.cellIndex;
8108         var rowIndex = row.dom.rowIndex - 1; // start from 0
8109         
8110         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111         
8112     },
8113     
8114     onClick : function(e, el)
8115     {
8116         var cell = Roo.get(el);
8117         
8118         if(!cell || (!this.cellSelection && !this.rowSelection)){
8119             return;
8120         }
8121         
8122         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123             cell = cell.findParent('td', false, true);
8124         }
8125         
8126         if(!cell || typeof(cell) == 'undefined'){
8127             return;
8128         }
8129         
8130         var row = cell.findParent('tr', false, true);
8131         
8132         if(!row || typeof(row) == 'undefined'){
8133             return;
8134         }
8135         
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = this.getRowIndex(row);
8138         
8139         // why??? - should these not be based on SelectionModel?
8140         if(this.cellSelection){
8141             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8142         }
8143         
8144         if(this.rowSelection){
8145             this.fireEvent('rowclick', this, row, rowIndex, e);
8146         }
8147         
8148         
8149     },
8150         
8151     onDblClick : function(e,el)
8152     {
8153         var cell = Roo.get(el);
8154         
8155         if(!cell || (!this.cellSelection && !this.rowSelection)){
8156             return;
8157         }
8158         
8159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160             cell = cell.findParent('td', false, true);
8161         }
8162         
8163         if(!cell || typeof(cell) == 'undefined'){
8164             return;
8165         }
8166         
8167         var row = cell.findParent('tr', false, true);
8168         
8169         if(!row || typeof(row) == 'undefined'){
8170             return;
8171         }
8172         
8173         var cellIndex = cell.dom.cellIndex;
8174         var rowIndex = this.getRowIndex(row);
8175         
8176         if(this.cellSelection){
8177             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8178         }
8179         
8180         if(this.rowSelection){
8181             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182         }
8183     },
8184     
8185     sort : function(e,el)
8186     {
8187         var col = Roo.get(el);
8188         
8189         if(!col.hasClass('sortable')){
8190             return;
8191         }
8192         
8193         var sort = col.attr('sort');
8194         var dir = 'ASC';
8195         
8196         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197             dir = 'DESC';
8198         }
8199         
8200         this.store.sortInfo = {field : sort, direction : dir};
8201         
8202         if (this.footer) {
8203             Roo.log("calling footer first");
8204             this.footer.onClick('first');
8205         } else {
8206         
8207             this.store.load({ params : { start : 0 } });
8208         }
8209     },
8210     
8211     renderHeader : function()
8212     {
8213         var header = {
8214             tag: 'thead',
8215             cn : []
8216         };
8217         
8218         var cm = this.cm;
8219         this.totalWidth = 0;
8220         
8221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8222             
8223             var config = cm.config[i];
8224             
8225             var c = {
8226                 tag: 'th',
8227                 cls : 'x-hcol-' + i,
8228                 style : '',
8229                 html: cm.getColumnHeader(i)
8230             };
8231             
8232             var hh = '';
8233             
8234             if(typeof(config.sortable) != 'undefined' && config.sortable){
8235                 c.cls = 'sortable';
8236                 c.html = '<i class="glyphicon"></i>' + c.html;
8237             }
8238             
8239             // could use BS4 hidden-..-down 
8240             
8241             if(typeof(config.lgHeader) != 'undefined'){
8242                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8243             }
8244             
8245             if(typeof(config.mdHeader) != 'undefined'){
8246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8247             }
8248             
8249             if(typeof(config.smHeader) != 'undefined'){
8250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8251             }
8252             
8253             if(typeof(config.xsHeader) != 'undefined'){
8254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8255             }
8256             
8257             if(hh.length){
8258                 c.html = hh;
8259             }
8260             
8261             if(typeof(config.tooltip) != 'undefined'){
8262                 c.tooltip = config.tooltip;
8263             }
8264             
8265             if(typeof(config.colspan) != 'undefined'){
8266                 c.colspan = config.colspan;
8267             }
8268             
8269             if(typeof(config.hidden) != 'undefined' && config.hidden){
8270                 c.style += ' display:none;';
8271             }
8272             
8273             if(typeof(config.dataIndex) != 'undefined'){
8274                 c.sort = config.dataIndex;
8275             }
8276             
8277            
8278             
8279             if(typeof(config.align) != 'undefined' && config.align.length){
8280                 c.style += ' text-align:' + config.align + ';';
8281             }
8282             
8283             if(typeof(config.width) != 'undefined'){
8284                 c.style += ' width:' + config.width + 'px;';
8285                 this.totalWidth += config.width;
8286             } else {
8287                 this.totalWidth += 100; // assume minimum of 100 per column?
8288             }
8289             
8290             if(typeof(config.cls) != 'undefined'){
8291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8292             }
8293             
8294             ['xs','sm','md','lg'].map(function(size){
8295                 
8296                 if(typeof(config[size]) == 'undefined'){
8297                     return;
8298                 }
8299                  
8300                 if (!config[size]) { // 0 = hidden
8301                     // BS 4 '0' is treated as hide that column and below.
8302                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303                     return;
8304                 }
8305                 
8306                 c.cls += ' col-' + size + '-' + config[size] + (
8307                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8308                 );
8309                 
8310                 
8311             });
8312             
8313             header.cn.push(c)
8314         }
8315         
8316         return header;
8317     },
8318     
8319     renderBody : function()
8320     {
8321         var body = {
8322             tag: 'tbody',
8323             cn : [
8324                 {
8325                     tag: 'tr',
8326                     cn : [
8327                         {
8328                             tag : 'td',
8329                             colspan :  this.cm.getColumnCount()
8330                         }
8331                     ]
8332                 }
8333             ]
8334         };
8335         
8336         return body;
8337     },
8338     
8339     renderFooter : function()
8340     {
8341         var footer = {
8342             tag: 'tfoot',
8343             cn : [
8344                 {
8345                     tag: 'tr',
8346                     cn : [
8347                         {
8348                             tag : 'td',
8349                             colspan :  this.cm.getColumnCount()
8350                         }
8351                     ]
8352                 }
8353             ]
8354         };
8355         
8356         return footer;
8357     },
8358     
8359     
8360     
8361     onLoad : function()
8362     {
8363 //        Roo.log('ds onload');
8364         this.clear();
8365         
8366         var _this = this;
8367         var cm = this.cm;
8368         var ds = this.store;
8369         
8370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372             if (_this.store.sortInfo) {
8373                     
8374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8376                 }
8377                 
8378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8380                 }
8381             }
8382         });
8383         
8384         var tbody =  this.mainBody;
8385               
8386         if(ds.getCount() > 0){
8387             ds.data.each(function(d,rowIndex){
8388                 var row =  this.renderRow(cm, ds, rowIndex);
8389                 
8390                 tbody.createChild(row);
8391                 
8392                 var _this = this;
8393                 
8394                 if(row.cellObjects.length){
8395                     Roo.each(row.cellObjects, function(r){
8396                         _this.renderCellObject(r);
8397                     })
8398                 }
8399                 
8400             }, this);
8401         }
8402         
8403         var tfoot = this.el.select('tfoot', true).first();
8404         
8405         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8406             
8407             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8408             
8409             var total = this.ds.getTotalCount();
8410             
8411             if(this.footer.pageSize < total){
8412                 this.mainFoot.show();
8413             }
8414         }
8415         
8416         Roo.each(this.el.select('tbody td', true).elements, function(e){
8417             e.on('mouseover', _this.onMouseover, _this);
8418         });
8419         
8420         Roo.each(this.el.select('tbody td', true).elements, function(e){
8421             e.on('mouseout', _this.onMouseout, _this);
8422         });
8423         this.fireEvent('rowsrendered', this);
8424         
8425         this.autoSize();
8426     },
8427     
8428     
8429     onUpdate : function(ds,record)
8430     {
8431         this.refreshRow(record);
8432         this.autoSize();
8433     },
8434     
8435     onRemove : function(ds, record, index, isUpdate){
8436         if(isUpdate !== true){
8437             this.fireEvent("beforerowremoved", this, index, record);
8438         }
8439         var bt = this.mainBody.dom;
8440         
8441         var rows = this.el.select('tbody > tr', true).elements;
8442         
8443         if(typeof(rows[index]) != 'undefined'){
8444             bt.removeChild(rows[index].dom);
8445         }
8446         
8447 //        if(bt.rows[index]){
8448 //            bt.removeChild(bt.rows[index]);
8449 //        }
8450         
8451         if(isUpdate !== true){
8452             //this.stripeRows(index);
8453             //this.syncRowHeights(index, index);
8454             //this.layout();
8455             this.fireEvent("rowremoved", this, index, record);
8456         }
8457     },
8458     
8459     onAdd : function(ds, records, rowIndex)
8460     {
8461         //Roo.log('on Add called');
8462         // - note this does not handle multiple adding very well..
8463         var bt = this.mainBody.dom;
8464         for (var i =0 ; i < records.length;i++) {
8465             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466             //Roo.log(records[i]);
8467             //Roo.log(this.store.getAt(rowIndex+i));
8468             this.insertRow(this.store, rowIndex + i, false);
8469             return;
8470         }
8471         
8472     },
8473     
8474     
8475     refreshRow : function(record){
8476         var ds = this.store, index;
8477         if(typeof record == 'number'){
8478             index = record;
8479             record = ds.getAt(index);
8480         }else{
8481             index = ds.indexOf(record);
8482             if (index < 0) {
8483                 return; // should not happen - but seems to 
8484             }
8485         }
8486         this.insertRow(ds, index, true);
8487         this.autoSize();
8488         this.onRemove(ds, record, index+1, true);
8489         this.autoSize();
8490         //this.syncRowHeights(index, index);
8491         //this.layout();
8492         this.fireEvent("rowupdated", this, index, record);
8493     },
8494     
8495     insertRow : function(dm, rowIndex, isUpdate){
8496         
8497         if(!isUpdate){
8498             this.fireEvent("beforerowsinserted", this, rowIndex);
8499         }
8500             //var s = this.getScrollState();
8501         var row = this.renderRow(this.cm, this.store, rowIndex);
8502         // insert before rowIndex..
8503         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504         
8505         var _this = this;
8506                 
8507         if(row.cellObjects.length){
8508             Roo.each(row.cellObjects, function(r){
8509                 _this.renderCellObject(r);
8510             })
8511         }
8512             
8513         if(!isUpdate){
8514             this.fireEvent("rowsinserted", this, rowIndex);
8515             //this.syncRowHeights(firstRow, lastRow);
8516             //this.stripeRows(firstRow);
8517             //this.layout();
8518         }
8519         
8520     },
8521     
8522     
8523     getRowDom : function(rowIndex)
8524     {
8525         var rows = this.el.select('tbody > tr', true).elements;
8526         
8527         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8528         
8529     },
8530     // returns the object tree for a tr..
8531   
8532     
8533     renderRow : function(cm, ds, rowIndex) 
8534     {
8535         var d = ds.getAt(rowIndex);
8536         
8537         var row = {
8538             tag : 'tr',
8539             cls : 'x-row-' + rowIndex,
8540             cn : []
8541         };
8542             
8543         var cellObjects = [];
8544         
8545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546             var config = cm.config[i];
8547             
8548             var renderer = cm.getRenderer(i);
8549             var value = '';
8550             var id = false;
8551             
8552             if(typeof(renderer) !== 'undefined'){
8553                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8554             }
8555             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556             // and are rendered into the cells after the row is rendered - using the id for the element.
8557             
8558             if(typeof(value) === 'object'){
8559                 id = Roo.id();
8560                 cellObjects.push({
8561                     container : id,
8562                     cfg : value 
8563                 })
8564             }
8565             
8566             var rowcfg = {
8567                 record: d,
8568                 rowIndex : rowIndex,
8569                 colIndex : i,
8570                 rowClass : ''
8571             };
8572
8573             this.fireEvent('rowclass', this, rowcfg);
8574             
8575             var td = {
8576                 tag: 'td',
8577                 cls : rowcfg.rowClass + ' x-col-' + i,
8578                 style: '',
8579                 html: (typeof(value) === 'object') ? '' : value
8580             };
8581             
8582             if (id) {
8583                 td.id = id;
8584             }
8585             
8586             if(typeof(config.colspan) != 'undefined'){
8587                 td.colspan = config.colspan;
8588             }
8589             
8590             if(typeof(config.hidden) != 'undefined' && config.hidden){
8591                 td.style += ' display:none;';
8592             }
8593             
8594             if(typeof(config.align) != 'undefined' && config.align.length){
8595                 td.style += ' text-align:' + config.align + ';';
8596             }
8597             if(typeof(config.valign) != 'undefined' && config.valign.length){
8598                 td.style += ' vertical-align:' + config.valign + ';';
8599             }
8600             
8601             if(typeof(config.width) != 'undefined'){
8602                 td.style += ' width:' +  config.width + 'px;';
8603             }
8604             
8605             if(typeof(config.cursor) != 'undefined'){
8606                 td.style += ' cursor:' +  config.cursor + ';';
8607             }
8608             
8609             if(typeof(config.cls) != 'undefined'){
8610                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8611             }
8612             
8613             ['xs','sm','md','lg'].map(function(size){
8614                 
8615                 if(typeof(config[size]) == 'undefined'){
8616                     return;
8617                 }
8618                 
8619                 
8620                   
8621                 if (!config[size]) { // 0 = hidden
8622                     // BS 4 '0' is treated as hide that column and below.
8623                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624                     return;
8625                 }
8626                 
8627                 td.cls += ' col-' + size + '-' + config[size] + (
8628                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8629                 );
8630                  
8631
8632             });
8633             
8634             row.cn.push(td);
8635            
8636         }
8637         
8638         row.cellObjects = cellObjects;
8639         
8640         return row;
8641           
8642     },
8643     
8644     
8645     
8646     onBeforeLoad : function()
8647     {
8648         
8649     },
8650      /**
8651      * Remove all rows
8652      */
8653     clear : function()
8654     {
8655         this.el.select('tbody', true).first().dom.innerHTML = '';
8656     },
8657     /**
8658      * Show or hide a row.
8659      * @param {Number} rowIndex to show or hide
8660      * @param {Boolean} state hide
8661      */
8662     setRowVisibility : function(rowIndex, state)
8663     {
8664         var bt = this.mainBody.dom;
8665         
8666         var rows = this.el.select('tbody > tr', true).elements;
8667         
8668         if(typeof(rows[rowIndex]) == 'undefined'){
8669             return;
8670         }
8671         rows[rowIndex].dom.style.display = state ? '' : 'none';
8672     },
8673     
8674     
8675     getSelectionModel : function(){
8676         if(!this.selModel){
8677             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8678         }
8679         return this.selModel;
8680     },
8681     /*
8682      * Render the Roo.bootstrap object from renderder
8683      */
8684     renderCellObject : function(r)
8685     {
8686         var _this = this;
8687         
8688         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8689         
8690         var t = r.cfg.render(r.container);
8691         
8692         if(r.cfg.cn){
8693             Roo.each(r.cfg.cn, function(c){
8694                 var child = {
8695                     container: t.getChildContainer(),
8696                     cfg: c
8697                 };
8698                 _this.renderCellObject(child);
8699             })
8700         }
8701     },
8702     
8703     getRowIndex : function(row)
8704     {
8705         var rowIndex = -1;
8706         
8707         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8708             if(el != row){
8709                 return;
8710             }
8711             
8712             rowIndex = index;
8713         });
8714         
8715         return rowIndex;
8716     },
8717      /**
8718      * Returns the grid's underlying element = used by panel.Grid
8719      * @return {Element} The element
8720      */
8721     getGridEl : function(){
8722         return this.el;
8723     },
8724      /**
8725      * Forces a resize - used by panel.Grid
8726      * @return {Element} The element
8727      */
8728     autoSize : function()
8729     {
8730         //var ctr = Roo.get(this.container.dom.parentElement);
8731         var ctr = Roo.get(this.el.dom);
8732         
8733         var thd = this.getGridEl().select('thead',true).first();
8734         var tbd = this.getGridEl().select('tbody', true).first();
8735         var tfd = this.getGridEl().select('tfoot', true).first();
8736         
8737         var cw = ctr.getWidth();
8738         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8739         
8740         if (tbd) {
8741             
8742             tbd.setWidth(ctr.getWidth());
8743             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744             // this needs fixing for various usage - currently only hydra job advers I think..
8745             //tdb.setHeight(
8746             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8747             //); 
8748             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8749             cw -= barsize;
8750         }
8751         cw = Math.max(cw, this.totalWidth);
8752         this.getGridEl().select('tbody tr',true).setWidth(cw);
8753         
8754         // resize 'expandable coloumn?
8755         
8756         return; // we doe not have a view in this design..
8757         
8758     },
8759     onBodyScroll: function()
8760     {
8761         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8762         if(this.mainHead){
8763             this.mainHead.setStyle({
8764                 'position' : 'relative',
8765                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8766             });
8767         }
8768         
8769         if(this.lazyLoad){
8770             
8771             var scrollHeight = this.mainBody.dom.scrollHeight;
8772             
8773             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8774             
8775             var height = this.mainBody.getHeight();
8776             
8777             if(scrollHeight - height == scrollTop) {
8778                 
8779                 var total = this.ds.getTotalCount();
8780                 
8781                 if(this.footer.cursor + this.footer.pageSize < total){
8782                     
8783                     this.footer.ds.load({
8784                         params : {
8785                             start : this.footer.cursor + this.footer.pageSize,
8786                             limit : this.footer.pageSize
8787                         },
8788                         add : true
8789                     });
8790                 }
8791             }
8792             
8793         }
8794     },
8795     
8796     onHeaderChange : function()
8797     {
8798         var header = this.renderHeader();
8799         var table = this.el.select('table', true).first();
8800         
8801         this.mainHead.remove();
8802         this.mainHead = table.createChild(header, this.mainBody, false);
8803     },
8804     
8805     onHiddenChange : function(colModel, colIndex, hidden)
8806     {
8807         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8809         
8810         this.CSS.updateRule(thSelector, "display", "");
8811         this.CSS.updateRule(tdSelector, "display", "");
8812         
8813         if(hidden){
8814             this.CSS.updateRule(thSelector, "display", "none");
8815             this.CSS.updateRule(tdSelector, "display", "none");
8816         }
8817         
8818         this.onHeaderChange();
8819         this.onLoad();
8820     },
8821     
8822     setColumnWidth: function(col_index, width)
8823     {
8824         // width = "md-2 xs-2..."
8825         if(!this.colModel.config[col_index]) {
8826             return;
8827         }
8828         
8829         var w = width.split(" ");
8830         
8831         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8832         
8833         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8834         
8835         
8836         for(var j = 0; j < w.length; j++) {
8837             
8838             if(!w[j]) {
8839                 continue;
8840             }
8841             
8842             var size_cls = w[j].split("-");
8843             
8844             if(!Number.isInteger(size_cls[1] * 1)) {
8845                 continue;
8846             }
8847             
8848             if(!this.colModel.config[col_index][size_cls[0]]) {
8849                 continue;
8850             }
8851             
8852             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853                 continue;
8854             }
8855             
8856             h_row[0].classList.replace(
8857                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858                 "col-"+size_cls[0]+"-"+size_cls[1]
8859             );
8860             
8861             for(var i = 0; i < rows.length; i++) {
8862                 
8863                 var size_cls = w[j].split("-");
8864                 
8865                 if(!Number.isInteger(size_cls[1] * 1)) {
8866                     continue;
8867                 }
8868                 
8869                 if(!this.colModel.config[col_index][size_cls[0]]) {
8870                     continue;
8871                 }
8872                 
8873                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874                     continue;
8875                 }
8876                 
8877                 rows[i].classList.replace(
8878                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879                     "col-"+size_cls[0]+"-"+size_cls[1]
8880                 );
8881             }
8882             
8883             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8884         }
8885     }
8886 });
8887
8888  
8889
8890  /*
8891  * - LGPL
8892  *
8893  * table cell
8894  * 
8895  */
8896
8897 /**
8898  * @class Roo.bootstrap.TableCell
8899  * @extends Roo.bootstrap.Component
8900  * Bootstrap TableCell class
8901  * @cfg {String} html cell contain text
8902  * @cfg {String} cls cell class
8903  * @cfg {String} tag cell tag (td|th) default td
8904  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905  * @cfg {String} align Aligns the content in a cell
8906  * @cfg {String} axis Categorizes cells
8907  * @cfg {String} bgcolor Specifies the background color of a cell
8908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909  * @cfg {Number} colspan Specifies the number of columns a cell should span
8910  * @cfg {String} headers Specifies one or more header cells a cell is related to
8911  * @cfg {Number} height Sets the height of a cell
8912  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913  * @cfg {Number} rowspan Sets the number of rows a cell should span
8914  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915  * @cfg {String} valign Vertical aligns the content in a cell
8916  * @cfg {Number} width Specifies the width of a cell
8917  * 
8918  * @constructor
8919  * Create a new TableCell
8920  * @param {Object} config The config object
8921  */
8922
8923 Roo.bootstrap.TableCell = function(config){
8924     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8925 };
8926
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8928     
8929     html: false,
8930     cls: false,
8931     tag: false,
8932     abbr: false,
8933     align: false,
8934     axis: false,
8935     bgcolor: false,
8936     charoff: false,
8937     colspan: false,
8938     headers: false,
8939     height: false,
8940     nowrap: false,
8941     rowspan: false,
8942     scope: false,
8943     valign: false,
8944     width: false,
8945     
8946     
8947     getAutoCreate : function(){
8948         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8949         
8950         cfg = {
8951             tag: 'td'
8952         };
8953         
8954         if(this.tag){
8955             cfg.tag = this.tag;
8956         }
8957         
8958         if (this.html) {
8959             cfg.html=this.html
8960         }
8961         if (this.cls) {
8962             cfg.cls=this.cls
8963         }
8964         if (this.abbr) {
8965             cfg.abbr=this.abbr
8966         }
8967         if (this.align) {
8968             cfg.align=this.align
8969         }
8970         if (this.axis) {
8971             cfg.axis=this.axis
8972         }
8973         if (this.bgcolor) {
8974             cfg.bgcolor=this.bgcolor
8975         }
8976         if (this.charoff) {
8977             cfg.charoff=this.charoff
8978         }
8979         if (this.colspan) {
8980             cfg.colspan=this.colspan
8981         }
8982         if (this.headers) {
8983             cfg.headers=this.headers
8984         }
8985         if (this.height) {
8986             cfg.height=this.height
8987         }
8988         if (this.nowrap) {
8989             cfg.nowrap=this.nowrap
8990         }
8991         if (this.rowspan) {
8992             cfg.rowspan=this.rowspan
8993         }
8994         if (this.scope) {
8995             cfg.scope=this.scope
8996         }
8997         if (this.valign) {
8998             cfg.valign=this.valign
8999         }
9000         if (this.width) {
9001             cfg.width=this.width
9002         }
9003         
9004         
9005         return cfg;
9006     }
9007    
9008 });
9009
9010  
9011
9012  /*
9013  * - LGPL
9014  *
9015  * table row
9016  * 
9017  */
9018
9019 /**
9020  * @class Roo.bootstrap.TableRow
9021  * @extends Roo.bootstrap.Component
9022  * Bootstrap TableRow class
9023  * @cfg {String} cls row class
9024  * @cfg {String} align Aligns the content in a table row
9025  * @cfg {String} bgcolor Specifies a background color for a table row
9026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027  * @cfg {String} valign Vertical aligns the content in a table row
9028  * 
9029  * @constructor
9030  * Create a new TableRow
9031  * @param {Object} config The config object
9032  */
9033
9034 Roo.bootstrap.TableRow = function(config){
9035     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9036 };
9037
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9039     
9040     cls: false,
9041     align: false,
9042     bgcolor: false,
9043     charoff: false,
9044     valign: false,
9045     
9046     getAutoCreate : function(){
9047         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9048         
9049         cfg = {
9050             tag: 'tr'
9051         };
9052             
9053         if(this.cls){
9054             cfg.cls = this.cls;
9055         }
9056         if(this.align){
9057             cfg.align = this.align;
9058         }
9059         if(this.bgcolor){
9060             cfg.bgcolor = this.bgcolor;
9061         }
9062         if(this.charoff){
9063             cfg.charoff = this.charoff;
9064         }
9065         if(this.valign){
9066             cfg.valign = this.valign;
9067         }
9068         
9069         return cfg;
9070     }
9071    
9072 });
9073
9074  
9075
9076  /*
9077  * - LGPL
9078  *
9079  * table body
9080  * 
9081  */
9082
9083 /**
9084  * @class Roo.bootstrap.TableBody
9085  * @extends Roo.bootstrap.Component
9086  * Bootstrap TableBody class
9087  * @cfg {String} cls element class
9088  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089  * @cfg {String} align Aligns the content inside the element
9090  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9092  * 
9093  * @constructor
9094  * Create a new TableBody
9095  * @param {Object} config The config object
9096  */
9097
9098 Roo.bootstrap.TableBody = function(config){
9099     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9100 };
9101
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9103     
9104     cls: false,
9105     tag: false,
9106     align: false,
9107     charoff: false,
9108     valign: false,
9109     
9110     getAutoCreate : function(){
9111         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9112         
9113         cfg = {
9114             tag: 'tbody'
9115         };
9116             
9117         if (this.cls) {
9118             cfg.cls=this.cls
9119         }
9120         if(this.tag){
9121             cfg.tag = this.tag;
9122         }
9123         
9124         if(this.align){
9125             cfg.align = this.align;
9126         }
9127         if(this.charoff){
9128             cfg.charoff = this.charoff;
9129         }
9130         if(this.valign){
9131             cfg.valign = this.valign;
9132         }
9133         
9134         return cfg;
9135     }
9136     
9137     
9138 //    initEvents : function()
9139 //    {
9140 //        
9141 //        if(!this.store){
9142 //            return;
9143 //        }
9144 //        
9145 //        this.store = Roo.factory(this.store, Roo.data);
9146 //        this.store.on('load', this.onLoad, this);
9147 //        
9148 //        this.store.load();
9149 //        
9150 //    },
9151 //    
9152 //    onLoad: function () 
9153 //    {   
9154 //        this.fireEvent('load', this);
9155 //    }
9156 //    
9157 //   
9158 });
9159
9160  
9161
9162  /*
9163  * Based on:
9164  * Ext JS Library 1.1.1
9165  * Copyright(c) 2006-2007, Ext JS, LLC.
9166  *
9167  * Originally Released Under LGPL - original licence link has changed is not relivant.
9168  *
9169  * Fork - LGPL
9170  * <script type="text/javascript">
9171  */
9172
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9175  /**
9176  * @class Roo.form.Action
9177  * Internal Class used to handle form actions
9178  * @constructor
9179  * @param {Roo.form.BasicForm} el The form element or its id
9180  * @param {Object} config Configuration options
9181  */
9182
9183  
9184  
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9187     this.form = form;
9188     this.options = options || {};
9189 };
9190 /**
9191  * Client Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9195 /**
9196  * Server Validation Failed
9197  * @const 
9198  */
9199 Roo.form.Action.SERVER_INVALID = 'server';
9200  /**
9201  * Connect to Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9205 /**
9206  * Reading Data from Server Failed
9207  * @const 
9208  */
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9210
9211 Roo.form.Action.prototype = {
9212     type : 'default',
9213     failureType : undefined,
9214     response : undefined,
9215     result : undefined,
9216
9217     // interface method
9218     run : function(options){
9219
9220     },
9221
9222     // interface method
9223     success : function(response){
9224
9225     },
9226
9227     // interface method
9228     handleResponse : function(response){
9229
9230     },
9231
9232     // default connection failure
9233     failure : function(response){
9234         
9235         this.response = response;
9236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237         this.form.afterAction(this, false);
9238     },
9239
9240     processResponse : function(response){
9241         this.response = response;
9242         if(!response.responseText){
9243             return true;
9244         }
9245         this.result = this.handleResponse(response);
9246         return this.result;
9247     },
9248
9249     // utility functions used internally
9250     getUrl : function(appendParams){
9251         var url = this.options.url || this.form.url || this.form.el.dom.action;
9252         if(appendParams){
9253             var p = this.getParams();
9254             if(p){
9255                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9256             }
9257         }
9258         return url;
9259     },
9260
9261     getMethod : function(){
9262         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9263     },
9264
9265     getParams : function(){
9266         var bp = this.form.baseParams;
9267         var p = this.options.params;
9268         if(p){
9269             if(typeof p == "object"){
9270                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271             }else if(typeof p == 'string' && bp){
9272                 p += '&' + Roo.urlEncode(bp);
9273             }
9274         }else if(bp){
9275             p = Roo.urlEncode(bp);
9276         }
9277         return p;
9278     },
9279
9280     createCallback : function(){
9281         return {
9282             success: this.success,
9283             failure: this.failure,
9284             scope: this,
9285             timeout: (this.form.timeout*1000),
9286             upload: this.form.fileUpload ? this.success : undefined
9287         };
9288     }
9289 };
9290
9291 Roo.form.Action.Submit = function(form, options){
9292     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9293 };
9294
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9296     type : 'submit',
9297
9298     haveProgress : false,
9299     uploadComplete : false,
9300     
9301     // uploadProgress indicator.
9302     uploadProgress : function()
9303     {
9304         if (!this.form.progressUrl) {
9305             return;
9306         }
9307         
9308         if (!this.haveProgress) {
9309             Roo.MessageBox.progress("Uploading", "Uploading");
9310         }
9311         if (this.uploadComplete) {
9312            Roo.MessageBox.hide();
9313            return;
9314         }
9315         
9316         this.haveProgress = true;
9317    
9318         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9319         
9320         var c = new Roo.data.Connection();
9321         c.request({
9322             url : this.form.progressUrl,
9323             params: {
9324                 id : uid
9325             },
9326             method: 'GET',
9327             success : function(req){
9328                //console.log(data);
9329                 var rdata = false;
9330                 var edata;
9331                 try  {
9332                    rdata = Roo.decode(req.responseText)
9333                 } catch (e) {
9334                     Roo.log("Invalid data from server..");
9335                     Roo.log(edata);
9336                     return;
9337                 }
9338                 if (!rdata || !rdata.success) {
9339                     Roo.log(rdata);
9340                     Roo.MessageBox.alert(Roo.encode(rdata));
9341                     return;
9342                 }
9343                 var data = rdata.data;
9344                 
9345                 if (this.uploadComplete) {
9346                    Roo.MessageBox.hide();
9347                    return;
9348                 }
9349                    
9350                 if (data){
9351                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9353                     );
9354                 }
9355                 this.uploadProgress.defer(2000,this);
9356             },
9357        
9358             failure: function(data) {
9359                 Roo.log('progress url failed ');
9360                 Roo.log(data);
9361             },
9362             scope : this
9363         });
9364            
9365     },
9366     
9367     
9368     run : function()
9369     {
9370         // run get Values on the form, so it syncs any secondary forms.
9371         this.form.getValues();
9372         
9373         var o = this.options;
9374         var method = this.getMethod();
9375         var isPost = method == 'POST';
9376         if(o.clientValidation === false || this.form.isValid()){
9377             
9378             if (this.form.progressUrl) {
9379                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380                     (new Date() * 1) + '' + Math.random());
9381                     
9382             } 
9383             
9384             
9385             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386                 form:this.form.el.dom,
9387                 url:this.getUrl(!isPost),
9388                 method: method,
9389                 params:isPost ? this.getParams() : null,
9390                 isUpload: this.form.fileUpload,
9391                 formData : this.form.formData
9392             }));
9393             
9394             this.uploadProgress();
9395
9396         }else if (o.clientValidation !== false){ // client validation failed
9397             this.failureType = Roo.form.Action.CLIENT_INVALID;
9398             this.form.afterAction(this, false);
9399         }
9400     },
9401
9402     success : function(response)
9403     {
9404         this.uploadComplete= true;
9405         if (this.haveProgress) {
9406             Roo.MessageBox.hide();
9407         }
9408         
9409         
9410         var result = this.processResponse(response);
9411         if(result === true || result.success){
9412             this.form.afterAction(this, true);
9413             return;
9414         }
9415         if(result.errors){
9416             this.form.markInvalid(result.errors);
9417             this.failureType = Roo.form.Action.SERVER_INVALID;
9418         }
9419         this.form.afterAction(this, false);
9420     },
9421     failure : function(response)
9422     {
9423         this.uploadComplete= true;
9424         if (this.haveProgress) {
9425             Roo.MessageBox.hide();
9426         }
9427         
9428         this.response = response;
9429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430         this.form.afterAction(this, false);
9431     },
9432     
9433     handleResponse : function(response){
9434         if(this.form.errorReader){
9435             var rs = this.form.errorReader.read(response);
9436             var errors = [];
9437             if(rs.records){
9438                 for(var i = 0, len = rs.records.length; i < len; i++) {
9439                     var r = rs.records[i];
9440                     errors[i] = r.data;
9441                 }
9442             }
9443             if(errors.length < 1){
9444                 errors = null;
9445             }
9446             return {
9447                 success : rs.success,
9448                 errors : errors
9449             };
9450         }
9451         var ret = false;
9452         try {
9453             ret = Roo.decode(response.responseText);
9454         } catch (e) {
9455             ret = {
9456                 success: false,
9457                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9458                 errors : []
9459             };
9460         }
9461         return ret;
9462         
9463     }
9464 });
9465
9466
9467 Roo.form.Action.Load = function(form, options){
9468     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469     this.reader = this.form.reader;
9470 };
9471
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9473     type : 'load',
9474
9475     run : function(){
9476         
9477         Roo.Ajax.request(Roo.apply(
9478                 this.createCallback(), {
9479                     method:this.getMethod(),
9480                     url:this.getUrl(false),
9481                     params:this.getParams()
9482         }));
9483     },
9484
9485     success : function(response){
9486         
9487         var result = this.processResponse(response);
9488         if(result === true || !result.success || !result.data){
9489             this.failureType = Roo.form.Action.LOAD_FAILURE;
9490             this.form.afterAction(this, false);
9491             return;
9492         }
9493         this.form.clearInvalid();
9494         this.form.setValues(result.data);
9495         this.form.afterAction(this, true);
9496     },
9497
9498     handleResponse : function(response){
9499         if(this.form.reader){
9500             var rs = this.form.reader.read(response);
9501             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9502             return {
9503                 success : rs.success,
9504                 data : data
9505             };
9506         }
9507         return Roo.decode(response.responseText);
9508     }
9509 });
9510
9511 Roo.form.Action.ACTION_TYPES = {
9512     'load' : Roo.form.Action.Load,
9513     'submit' : Roo.form.Action.Submit
9514 };/*
9515  * - LGPL
9516  *
9517  * form
9518  *
9519  */
9520
9521 /**
9522  * @class Roo.bootstrap.Form
9523  * @extends Roo.bootstrap.Component
9524  * Bootstrap Form class
9525  * @cfg {String} method  GET | POST (default POST)
9526  * @cfg {String} labelAlign top | left (default top)
9527  * @cfg {String} align left  | right - for navbars
9528  * @cfg {Boolean} loadMask load mask when submit (default true)
9529
9530  *
9531  * @constructor
9532  * Create a new Form
9533  * @param {Object} config The config object
9534  */
9535
9536
9537 Roo.bootstrap.Form = function(config){
9538     
9539     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9540     
9541     Roo.bootstrap.Form.popover.apply();
9542     
9543     this.addEvents({
9544         /**
9545          * @event clientvalidation
9546          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547          * @param {Form} this
9548          * @param {Boolean} valid true if the form has passed client-side validation
9549          */
9550         clientvalidation: true,
9551         /**
9552          * @event beforeaction
9553          * Fires before any action is performed. Return false to cancel the action.
9554          * @param {Form} this
9555          * @param {Action} action The action to be performed
9556          */
9557         beforeaction: true,
9558         /**
9559          * @event actionfailed
9560          * Fires when an action fails.
9561          * @param {Form} this
9562          * @param {Action} action The action that failed
9563          */
9564         actionfailed : true,
9565         /**
9566          * @event actioncomplete
9567          * Fires when an action is completed.
9568          * @param {Form} this
9569          * @param {Action} action The action that completed
9570          */
9571         actioncomplete : true
9572     });
9573 };
9574
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9576
9577      /**
9578      * @cfg {String} method
9579      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9580      */
9581     method : 'POST',
9582     /**
9583      * @cfg {String} url
9584      * The URL to use for form actions if one isn't supplied in the action options.
9585      */
9586     /**
9587      * @cfg {Boolean} fileUpload
9588      * Set to true if this form is a file upload.
9589      */
9590
9591     /**
9592      * @cfg {Object} baseParams
9593      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594      */
9595
9596     /**
9597      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598      */
9599     timeout: 30,
9600     /**
9601      * @cfg {Sting} align (left|right) for navbar forms
9602      */
9603     align : 'left',
9604
9605     // private
9606     activeAction : null,
9607
9608     /**
9609      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610      * element by passing it or its id or mask the form itself by passing in true.
9611      * @type Mixed
9612      */
9613     waitMsgTarget : false,
9614
9615     loadMask : true,
9616     
9617     /**
9618      * @cfg {Boolean} errorMask (true|false) default false
9619      */
9620     errorMask : false,
9621     
9622     /**
9623      * @cfg {Number} maskOffset Default 100
9624      */
9625     maskOffset : 100,
9626     
9627     /**
9628      * @cfg {Boolean} maskBody
9629      */
9630     maskBody : false,
9631
9632     getAutoCreate : function(){
9633
9634         var cfg = {
9635             tag: 'form',
9636             method : this.method || 'POST',
9637             id : this.id || Roo.id(),
9638             cls : ''
9639         };
9640         if (this.parent().xtype.match(/^Nav/)) {
9641             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642
9643         }
9644
9645         if (this.labelAlign == 'left' ) {
9646             cfg.cls += ' form-horizontal';
9647         }
9648
9649
9650         return cfg;
9651     },
9652     initEvents : function()
9653     {
9654         this.el.on('submit', this.onSubmit, this);
9655         // this was added as random key presses on the form where triggering form submit.
9656         this.el.on('keypress', function(e) {
9657             if (e.getCharCode() != 13) {
9658                 return true;
9659             }
9660             // we might need to allow it for textareas.. and some other items.
9661             // check e.getTarget().
9662
9663             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664                 return true;
9665             }
9666
9667             Roo.log("keypress blocked");
9668
9669             e.preventDefault();
9670             return false;
9671         });
9672         
9673     },
9674     // private
9675     onSubmit : function(e){
9676         e.stopEvent();
9677     },
9678
9679      /**
9680      * Returns true if client-side validation on the form is successful.
9681      * @return Boolean
9682      */
9683     isValid : function(){
9684         var items = this.getItems();
9685         var valid = true;
9686         var target = false;
9687         
9688         items.each(function(f){
9689             
9690             if(f.validate()){
9691                 return;
9692             }
9693             
9694             Roo.log('invalid field: ' + f.name);
9695             
9696             valid = false;
9697
9698             if(!target && f.el.isVisible(true)){
9699                 target = f;
9700             }
9701            
9702         });
9703         
9704         if(this.errorMask && !valid){
9705             Roo.bootstrap.Form.popover.mask(this, target);
9706         }
9707         
9708         return valid;
9709     },
9710     
9711     /**
9712      * Returns true if any fields in this form have changed since their original load.
9713      * @return Boolean
9714      */
9715     isDirty : function(){
9716         var dirty = false;
9717         var items = this.getItems();
9718         items.each(function(f){
9719            if(f.isDirty()){
9720                dirty = true;
9721                return false;
9722            }
9723            return true;
9724         });
9725         return dirty;
9726     },
9727      /**
9728      * Performs a predefined action (submit or load) or custom actions you define on this form.
9729      * @param {String} actionName The name of the action type
9730      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9731      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732      * accept other config options):
9733      * <pre>
9734 Property          Type             Description
9735 ----------------  ---------------  ----------------------------------------------------------------------------------
9736 url               String           The url for the action (defaults to the form's url)
9737 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9738 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9740                                    validate the form on the client (defaults to false)
9741      * </pre>
9742      * @return {BasicForm} this
9743      */
9744     doAction : function(action, options){
9745         if(typeof action == 'string'){
9746             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9747         }
9748         if(this.fireEvent('beforeaction', this, action) !== false){
9749             this.beforeAction(action);
9750             action.run.defer(100, action);
9751         }
9752         return this;
9753     },
9754
9755     // private
9756     beforeAction : function(action){
9757         var o = action.options;
9758         
9759         if(this.loadMask){
9760             
9761             if(this.maskBody){
9762                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9763             } else {
9764                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9765             }
9766         }
9767         // not really supported yet.. ??
9768
9769         //if(this.waitMsgTarget === true){
9770         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else if(this.waitMsgTarget){
9772         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else {
9775         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9776        // }
9777
9778     },
9779
9780     // private
9781     afterAction : function(action, success){
9782         this.activeAction = null;
9783         var o = action.options;
9784
9785         if(this.loadMask){
9786             
9787             if(this.maskBody){
9788                 Roo.get(document.body).unmask();
9789             } else {
9790                 this.el.unmask();
9791             }
9792         }
9793         
9794         //if(this.waitMsgTarget === true){
9795 //            this.el.unmask();
9796         //}else if(this.waitMsgTarget){
9797         //    this.waitMsgTarget.unmask();
9798         //}else{
9799         //    Roo.MessageBox.updateProgress(1);
9800         //    Roo.MessageBox.hide();
9801        // }
9802         //
9803         if(success){
9804             if(o.reset){
9805                 this.reset();
9806             }
9807             Roo.callback(o.success, o.scope, [this, action]);
9808             this.fireEvent('actioncomplete', this, action);
9809
9810         }else{
9811
9812             // failure condition..
9813             // we have a scenario where updates need confirming.
9814             // eg. if a locking scenario exists..
9815             // we look for { errors : { needs_confirm : true }} in the response.
9816             if (
9817                 (typeof(action.result) != 'undefined')  &&
9818                 (typeof(action.result.errors) != 'undefined')  &&
9819                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9820            ){
9821                 var _t = this;
9822                 Roo.log("not supported yet");
9823                  /*
9824
9825                 Roo.MessageBox.confirm(
9826                     "Change requires confirmation",
9827                     action.result.errorMsg,
9828                     function(r) {
9829                         if (r != 'yes') {
9830                             return;
9831                         }
9832                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9833                     }
9834
9835                 );
9836                 */
9837
9838
9839                 return;
9840             }
9841
9842             Roo.callback(o.failure, o.scope, [this, action]);
9843             // show an error message if no failed handler is set..
9844             if (!this.hasListener('actionfailed')) {
9845                 Roo.log("need to add dialog support");
9846                 /*
9847                 Roo.MessageBox.alert("Error",
9848                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849                         action.result.errorMsg :
9850                         "Saving Failed, please check your entries or try again"
9851                 );
9852                 */
9853             }
9854
9855             this.fireEvent('actionfailed', this, action);
9856         }
9857
9858     },
9859     /**
9860      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861      * @param {String} id The value to search for
9862      * @return Field
9863      */
9864     findField : function(id){
9865         var items = this.getItems();
9866         var field = items.get(id);
9867         if(!field){
9868              items.each(function(f){
9869                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9870                     field = f;
9871                     return false;
9872                 }
9873                 return true;
9874             });
9875         }
9876         return field || null;
9877     },
9878      /**
9879      * Mark fields in this form invalid in bulk.
9880      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881      * @return {BasicForm} this
9882      */
9883     markInvalid : function(errors){
9884         if(errors instanceof Array){
9885             for(var i = 0, len = errors.length; i < len; i++){
9886                 var fieldError = errors[i];
9887                 var f = this.findField(fieldError.id);
9888                 if(f){
9889                     f.markInvalid(fieldError.msg);
9890                 }
9891             }
9892         }else{
9893             var field, id;
9894             for(id in errors){
9895                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896                     field.markInvalid(errors[id]);
9897                 }
9898             }
9899         }
9900         //Roo.each(this.childForms || [], function (f) {
9901         //    f.markInvalid(errors);
9902         //});
9903
9904         return this;
9905     },
9906
9907     /**
9908      * Set values for fields in this form in bulk.
9909      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910      * @return {BasicForm} this
9911      */
9912     setValues : function(values){
9913         if(values instanceof Array){ // array of objects
9914             for(var i = 0, len = values.length; i < len; i++){
9915                 var v = values[i];
9916                 var f = this.findField(v.id);
9917                 if(f){
9918                     f.setValue(v.value);
9919                     if(this.trackResetOnLoad){
9920                         f.originalValue = f.getValue();
9921                     }
9922                 }
9923             }
9924         }else{ // object hash
9925             var field, id;
9926             for(id in values){
9927                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9928
9929                     if (field.setFromData &&
9930                         field.valueField &&
9931                         field.displayField &&
9932                         // combos' with local stores can
9933                         // be queried via setValue()
9934                         // to set their value..
9935                         (field.store && !field.store.isLocal)
9936                         ) {
9937                         // it's a combo
9938                         var sd = { };
9939                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941                         field.setFromData(sd);
9942
9943                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9944                         
9945                         field.setFromData(values);
9946                         
9947                     } else {
9948                         field.setValue(values[id]);
9949                     }
9950
9951
9952                     if(this.trackResetOnLoad){
9953                         field.originalValue = field.getValue();
9954                     }
9955                 }
9956             }
9957         }
9958
9959         //Roo.each(this.childForms || [], function (f) {
9960         //    f.setValues(values);
9961         //});
9962
9963         return this;
9964     },
9965
9966     /**
9967      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968      * they are returned as an array.
9969      * @param {Boolean} asString
9970      * @return {Object}
9971      */
9972     getValues : function(asString){
9973         //if (this.childForms) {
9974             // copy values from the child forms
9975         //    Roo.each(this.childForms, function (f) {
9976         //        this.setValues(f.getValues());
9977         //    }, this);
9978         //}
9979
9980
9981
9982         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983         if(asString === true){
9984             return fs;
9985         }
9986         return Roo.urlDecode(fs);
9987     },
9988
9989     /**
9990      * Returns the fields in this form as an object with key/value pairs.
9991      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9992      * @return {Object}
9993      */
9994     getFieldValues : function(with_hidden)
9995     {
9996         var items = this.getItems();
9997         var ret = {};
9998         items.each(function(f){
9999             
10000             if (!f.getName()) {
10001                 return;
10002             }
10003             
10004             var v = f.getValue();
10005             
10006             if (f.inputType =='radio') {
10007                 if (typeof(ret[f.getName()]) == 'undefined') {
10008                     ret[f.getName()] = ''; // empty..
10009                 }
10010
10011                 if (!f.el.dom.checked) {
10012                     return;
10013
10014                 }
10015                 v = f.el.dom.value;
10016
10017             }
10018             
10019             if(f.xtype == 'MoneyField'){
10020                 ret[f.currencyName] = f.getCurrency();
10021             }
10022
10023             // not sure if this supported any more..
10024             if ((typeof(v) == 'object') && f.getRawValue) {
10025                 v = f.getRawValue() ; // dates..
10026             }
10027             // combo boxes where name != hiddenName...
10028             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029                 ret[f.name] = f.getRawValue();
10030             }
10031             ret[f.getName()] = v;
10032         });
10033
10034         return ret;
10035     },
10036
10037     /**
10038      * Clears all invalid messages in this form.
10039      * @return {BasicForm} this
10040      */
10041     clearInvalid : function(){
10042         var items = this.getItems();
10043
10044         items.each(function(f){
10045            f.clearInvalid();
10046         });
10047
10048         return this;
10049     },
10050
10051     /**
10052      * Resets this form.
10053      * @return {BasicForm} this
10054      */
10055     reset : function(){
10056         var items = this.getItems();
10057         items.each(function(f){
10058             f.reset();
10059         });
10060
10061         Roo.each(this.childForms || [], function (f) {
10062             f.reset();
10063         });
10064
10065
10066         return this;
10067     },
10068     
10069     getItems : function()
10070     {
10071         var r=new Roo.util.MixedCollection(false, function(o){
10072             return o.id || (o.id = Roo.id());
10073         });
10074         var iter = function(el) {
10075             if (el.inputEl) {
10076                 r.add(el);
10077             }
10078             if (!el.items) {
10079                 return;
10080             }
10081             Roo.each(el.items,function(e) {
10082                 iter(e);
10083             });
10084         };
10085
10086         iter(this);
10087         return r;
10088     },
10089     
10090     hideFields : function(items)
10091     {
10092         Roo.each(items, function(i){
10093             
10094             var f = this.findField(i);
10095             
10096             if(!f){
10097                 return;
10098             }
10099             
10100             f.hide();
10101             
10102         }, this);
10103     },
10104     
10105     showFields : function(items)
10106     {
10107         Roo.each(items, function(i){
10108             
10109             var f = this.findField(i);
10110             
10111             if(!f){
10112                 return;
10113             }
10114             
10115             f.show();
10116             
10117         }, this);
10118     }
10119
10120 });
10121
10122 Roo.apply(Roo.bootstrap.Form, {
10123     
10124     popover : {
10125         
10126         padding : 5,
10127         
10128         isApplied : false,
10129         
10130         isMasked : false,
10131         
10132         form : false,
10133         
10134         target : false,
10135         
10136         toolTip : false,
10137         
10138         intervalID : false,
10139         
10140         maskEl : false,
10141         
10142         apply : function()
10143         {
10144             if(this.isApplied){
10145                 return;
10146             }
10147             
10148             this.maskEl = {
10149                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10153             };
10154             
10155             this.maskEl.top.enableDisplayMode("block");
10156             this.maskEl.left.enableDisplayMode("block");
10157             this.maskEl.bottom.enableDisplayMode("block");
10158             this.maskEl.right.enableDisplayMode("block");
10159             
10160             this.toolTip = new Roo.bootstrap.Tooltip({
10161                 cls : 'roo-form-error-popover',
10162                 alignment : {
10163                     'left' : ['r-l', [-2,0], 'right'],
10164                     'right' : ['l-r', [2,0], 'left'],
10165                     'bottom' : ['tl-bl', [0,2], 'top'],
10166                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10167                 }
10168             });
10169             
10170             this.toolTip.render(Roo.get(document.body));
10171
10172             this.toolTip.el.enableDisplayMode("block");
10173             
10174             Roo.get(document.body).on('click', function(){
10175                 this.unmask();
10176             }, this);
10177             
10178             Roo.get(document.body).on('touchstart', function(){
10179                 this.unmask();
10180             }, this);
10181             
10182             this.isApplied = true
10183         },
10184         
10185         mask : function(form, target)
10186         {
10187             this.form = form;
10188             
10189             this.target = target;
10190             
10191             if(!this.form.errorMask || !target.el){
10192                 return;
10193             }
10194             
10195             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10196             
10197             Roo.log(scrollable);
10198             
10199             var ot = this.target.el.calcOffsetsTo(scrollable);
10200             
10201             var scrollTo = ot[1] - this.form.maskOffset;
10202             
10203             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10204             
10205             scrollable.scrollTo('top', scrollTo);
10206             
10207             var box = this.target.el.getBox();
10208             Roo.log(box);
10209             var zIndex = Roo.bootstrap.Modal.zIndex++;
10210
10211             
10212             this.maskEl.top.setStyle('position', 'absolute');
10213             this.maskEl.top.setStyle('z-index', zIndex);
10214             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215             this.maskEl.top.setLeft(0);
10216             this.maskEl.top.setTop(0);
10217             this.maskEl.top.show();
10218             
10219             this.maskEl.left.setStyle('position', 'absolute');
10220             this.maskEl.left.setStyle('z-index', zIndex);
10221             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222             this.maskEl.left.setLeft(0);
10223             this.maskEl.left.setTop(box.y - this.padding);
10224             this.maskEl.left.show();
10225
10226             this.maskEl.bottom.setStyle('position', 'absolute');
10227             this.maskEl.bottom.setStyle('z-index', zIndex);
10228             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229             this.maskEl.bottom.setLeft(0);
10230             this.maskEl.bottom.setTop(box.bottom + this.padding);
10231             this.maskEl.bottom.show();
10232
10233             this.maskEl.right.setStyle('position', 'absolute');
10234             this.maskEl.right.setStyle('z-index', zIndex);
10235             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236             this.maskEl.right.setLeft(box.right + this.padding);
10237             this.maskEl.right.setTop(box.y - this.padding);
10238             this.maskEl.right.show();
10239
10240             this.toolTip.bindEl = this.target.el;
10241
10242             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10243
10244             var tip = this.target.blankText;
10245
10246             if(this.target.getValue() !== '' ) {
10247                 
10248                 if (this.target.invalidText.length) {
10249                     tip = this.target.invalidText;
10250                 } else if (this.target.regexText.length){
10251                     tip = this.target.regexText;
10252                 }
10253             }
10254
10255             this.toolTip.show(tip);
10256
10257             this.intervalID = window.setInterval(function() {
10258                 Roo.bootstrap.Form.popover.unmask();
10259             }, 10000);
10260
10261             window.onwheel = function(){ return false;};
10262             
10263             (function(){ this.isMasked = true; }).defer(500, this);
10264             
10265         },
10266         
10267         unmask : function()
10268         {
10269             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270                 return;
10271             }
10272             
10273             this.maskEl.top.setStyle('position', 'absolute');
10274             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275             this.maskEl.top.hide();
10276
10277             this.maskEl.left.setStyle('position', 'absolute');
10278             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279             this.maskEl.left.hide();
10280
10281             this.maskEl.bottom.setStyle('position', 'absolute');
10282             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283             this.maskEl.bottom.hide();
10284
10285             this.maskEl.right.setStyle('position', 'absolute');
10286             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287             this.maskEl.right.hide();
10288             
10289             this.toolTip.hide();
10290             
10291             this.toolTip.el.hide();
10292             
10293             window.onwheel = function(){ return true;};
10294             
10295             if(this.intervalID){
10296                 window.clearInterval(this.intervalID);
10297                 this.intervalID = false;
10298             }
10299             
10300             this.isMasked = false;
10301             
10302         }
10303         
10304     }
10305     
10306 });
10307
10308 /*
10309  * Based on:
10310  * Ext JS Library 1.1.1
10311  * Copyright(c) 2006-2007, Ext JS, LLC.
10312  *
10313  * Originally Released Under LGPL - original licence link has changed is not relivant.
10314  *
10315  * Fork - LGPL
10316  * <script type="text/javascript">
10317  */
10318 /**
10319  * @class Roo.form.VTypes
10320  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10321  * @singleton
10322  */
10323 Roo.form.VTypes = function(){
10324     // closure these in so they are only created once.
10325     var alpha = /^[a-zA-Z_]+$/;
10326     var alphanum = /^[a-zA-Z0-9_]+$/;
10327     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10329
10330     // All these messages and functions are configurable
10331     return {
10332         /**
10333          * The function used to validate email addresses
10334          * @param {String} value The email address
10335          */
10336         'email' : function(v){
10337             return email.test(v);
10338         },
10339         /**
10340          * The error text to display when the email validation function returns false
10341          * @type String
10342          */
10343         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10344         /**
10345          * The keystroke filter mask to be applied on email input
10346          * @type RegExp
10347          */
10348         'emailMask' : /[a-z0-9_\.\-@]/i,
10349
10350         /**
10351          * The function used to validate URLs
10352          * @param {String} value The URL
10353          */
10354         'url' : function(v){
10355             return url.test(v);
10356         },
10357         /**
10358          * The error text to display when the url validation function returns false
10359          * @type String
10360          */
10361         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10362         
10363         /**
10364          * The function used to validate alpha values
10365          * @param {String} value The value
10366          */
10367         'alpha' : function(v){
10368             return alpha.test(v);
10369         },
10370         /**
10371          * The error text to display when the alpha validation function returns false
10372          * @type String
10373          */
10374         'alphaText' : 'This field should only contain letters and _',
10375         /**
10376          * The keystroke filter mask to be applied on alpha input
10377          * @type RegExp
10378          */
10379         'alphaMask' : /[a-z_]/i,
10380
10381         /**
10382          * The function used to validate alphanumeric values
10383          * @param {String} value The value
10384          */
10385         'alphanum' : function(v){
10386             return alphanum.test(v);
10387         },
10388         /**
10389          * The error text to display when the alphanumeric validation function returns false
10390          * @type String
10391          */
10392         'alphanumText' : 'This field should only contain letters, numbers and _',
10393         /**
10394          * The keystroke filter mask to be applied on alphanumeric input
10395          * @type RegExp
10396          */
10397         'alphanumMask' : /[a-z0-9_]/i
10398     };
10399 }();/*
10400  * - LGPL
10401  *
10402  * Input
10403  * 
10404  */
10405
10406 /**
10407  * @class Roo.bootstrap.Input
10408  * @extends Roo.bootstrap.Component
10409  * Bootstrap Input class
10410  * @cfg {Boolean} disabled is it disabled
10411  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10412  * @cfg {String} name name of the input
10413  * @cfg {string} fieldLabel - the label associated
10414  * @cfg {string} placeholder - placeholder to put in text.
10415  * @cfg {string}  before - input group add on before
10416  * @cfg {string} after - input group add on after
10417  * @cfg {string} size - (lg|sm) or leave empty..
10418  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420  * @cfg {Number} md colspan out of 12 for computer-sized screens
10421  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422  * @cfg {string} value default value of the input
10423  * @cfg {Number} labelWidth set the width of label 
10424  * @cfg {Number} labellg set the width of label (1-12)
10425  * @cfg {Number} labelmd set the width of label (1-12)
10426  * @cfg {Number} labelsm set the width of label (1-12)
10427  * @cfg {Number} labelxs set the width of label (1-12)
10428  * @cfg {String} labelAlign (top|left)
10429  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431  * @cfg {String} indicatorpos (left|right) default left
10432  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10435
10436  * @cfg {String} align (left|center|right) Default left
10437  * @cfg {Boolean} forceFeedback (true|false) Default false
10438  * 
10439  * @constructor
10440  * Create a new Input
10441  * @param {Object} config The config object
10442  */
10443
10444 Roo.bootstrap.Input = function(config){
10445     
10446     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10447     
10448     this.addEvents({
10449         /**
10450          * @event focus
10451          * Fires when this field receives input focus.
10452          * @param {Roo.form.Field} this
10453          */
10454         focus : true,
10455         /**
10456          * @event blur
10457          * Fires when this field loses input focus.
10458          * @param {Roo.form.Field} this
10459          */
10460         blur : true,
10461         /**
10462          * @event specialkey
10463          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10464          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465          * @param {Roo.form.Field} this
10466          * @param {Roo.EventObject} e The event object
10467          */
10468         specialkey : true,
10469         /**
10470          * @event change
10471          * Fires just before the field blurs if the field value has changed.
10472          * @param {Roo.form.Field} this
10473          * @param {Mixed} newValue The new value
10474          * @param {Mixed} oldValue The original value
10475          */
10476         change : true,
10477         /**
10478          * @event invalid
10479          * Fires after the field has been marked as invalid.
10480          * @param {Roo.form.Field} this
10481          * @param {String} msg The validation message
10482          */
10483         invalid : true,
10484         /**
10485          * @event valid
10486          * Fires after the field has been validated with no errors.
10487          * @param {Roo.form.Field} this
10488          */
10489         valid : true,
10490          /**
10491          * @event keyup
10492          * Fires after the key up
10493          * @param {Roo.form.Field} this
10494          * @param {Roo.EventObject}  e The event Object
10495          */
10496         keyup : true
10497     });
10498 };
10499
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10501      /**
10502      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503       automatic validation (defaults to "keyup").
10504      */
10505     validationEvent : "keyup",
10506      /**
10507      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10508      */
10509     validateOnBlur : true,
10510     /**
10511      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10512      */
10513     validationDelay : 250,
10514      /**
10515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10516      */
10517     focusClass : "x-form-focus",  // not needed???
10518     
10519        
10520     /**
10521      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     invalidClass : "has-warning",
10524     
10525     /**
10526      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10527      */
10528     validClass : "has-success",
10529     
10530     /**
10531      * @cfg {Boolean} hasFeedback (true|false) default true
10532      */
10533     hasFeedback : true,
10534     
10535     /**
10536      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     invalidFeedbackClass : "glyphicon-warning-sign",
10539     
10540     /**
10541      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10542      */
10543     validFeedbackClass : "glyphicon-ok",
10544     
10545     /**
10546      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10547      */
10548     selectOnFocus : false,
10549     
10550      /**
10551      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552      */
10553     maskRe : null,
10554        /**
10555      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10556      */
10557     vtype : null,
10558     
10559       /**
10560      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10561      */
10562     disableKeyFilter : false,
10563     
10564        /**
10565      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566      */
10567     disabled : false,
10568      /**
10569      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570      */
10571     allowBlank : true,
10572     /**
10573      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10574      */
10575     blankText : "Please complete this mandatory field",
10576     
10577      /**
10578      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579      */
10580     minLength : 0,
10581     /**
10582      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10583      */
10584     maxLength : Number.MAX_VALUE,
10585     /**
10586      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10587      */
10588     minLengthText : "The minimum length for this field is {0}",
10589     /**
10590      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10591      */
10592     maxLengthText : "The maximum length for this field is {0}",
10593   
10594     
10595     /**
10596      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597      * If available, this function will be called only after the basic validators all return true, and will be passed the
10598      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599      */
10600     validator : null,
10601     /**
10602      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10605      */
10606     regex : null,
10607     /**
10608      * @cfg {String} regexText -- Depricated - use Invalid Text
10609      */
10610     regexText : "",
10611     
10612     /**
10613      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10614      */
10615     invalidText : "",
10616     
10617     
10618     
10619     autocomplete: false,
10620     
10621     
10622     fieldLabel : '',
10623     inputType : 'text',
10624     
10625     name : false,
10626     placeholder: false,
10627     before : false,
10628     after : false,
10629     size : false,
10630     hasFocus : false,
10631     preventMark: false,
10632     isFormField : true,
10633     value : '',
10634     labelWidth : 2,
10635     labelAlign : false,
10636     readOnly : false,
10637     align : false,
10638     formatedValue : false,
10639     forceFeedback : false,
10640     
10641     indicatorpos : 'left',
10642     
10643     labellg : 0,
10644     labelmd : 0,
10645     labelsm : 0,
10646     labelxs : 0,
10647     
10648     capture : '',
10649     accept : '',
10650     
10651     parentLabelAlign : function()
10652     {
10653         var parent = this;
10654         while (parent.parent()) {
10655             parent = parent.parent();
10656             if (typeof(parent.labelAlign) !='undefined') {
10657                 return parent.labelAlign;
10658             }
10659         }
10660         return 'left';
10661         
10662     },
10663     
10664     getAutoCreate : function()
10665     {
10666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10667         
10668         var id = Roo.id();
10669         
10670         var cfg = {};
10671         
10672         if(this.inputType != 'hidden'){
10673             cfg.cls = 'form-group' //input-group
10674         }
10675         
10676         var input =  {
10677             tag: 'input',
10678             id : id,
10679             type : this.inputType,
10680             value : this.value,
10681             cls : 'form-control',
10682             placeholder : this.placeholder || '',
10683             autocomplete : this.autocomplete || 'new-password'
10684         };
10685         if (this.inputType == 'file') {
10686             input.style = 'overflow:hidden'; // why not in CSS?
10687         }
10688         
10689         if(this.capture.length){
10690             input.capture = this.capture;
10691         }
10692         
10693         if(this.accept.length){
10694             input.accept = this.accept + "/*";
10695         }
10696         
10697         if(this.align){
10698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10699         }
10700         
10701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702             input.maxLength = this.maxLength;
10703         }
10704         
10705         if (this.disabled) {
10706             input.disabled=true;
10707         }
10708         
10709         if (this.readOnly) {
10710             input.readonly=true;
10711         }
10712         
10713         if (this.name) {
10714             input.name = this.name;
10715         }
10716         
10717         if (this.size) {
10718             input.cls += ' input-' + this.size;
10719         }
10720         
10721         var settings=this;
10722         ['xs','sm','md','lg'].map(function(size){
10723             if (settings[size]) {
10724                 cfg.cls += ' col-' + size + '-' + settings[size];
10725             }
10726         });
10727         
10728         var inputblock = input;
10729         
10730         var feedback = {
10731             tag: 'span',
10732             cls: 'glyphicon form-control-feedback'
10733         };
10734             
10735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736             
10737             inputblock = {
10738                 cls : 'has-feedback',
10739                 cn :  [
10740                     input,
10741                     feedback
10742                 ] 
10743             };  
10744         }
10745         
10746         if (this.before || this.after) {
10747             
10748             inputblock = {
10749                 cls : 'input-group',
10750                 cn :  [] 
10751             };
10752             
10753             if (this.before && typeof(this.before) == 'string') {
10754                 
10755                 inputblock.cn.push({
10756                     tag :'span',
10757                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758                     html : this.before
10759                 });
10760             }
10761             if (this.before && typeof(this.before) == 'object') {
10762                 this.before = Roo.factory(this.before);
10763                 
10764                 inputblock.cn.push({
10765                     tag :'span',
10766                     cls : 'roo-input-before input-group-prepend   input-group-' +
10767                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10768                 });
10769             }
10770             
10771             inputblock.cn.push(input);
10772             
10773             if (this.after && typeof(this.after) == 'string') {
10774                 inputblock.cn.push({
10775                     tag :'span',
10776                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777                     html : this.after
10778                 });
10779             }
10780             if (this.after && typeof(this.after) == 'object') {
10781                 this.after = Roo.factory(this.after);
10782                 
10783                 inputblock.cn.push({
10784                     tag :'span',
10785                     cls : 'roo-input-after input-group-append  input-group-' +
10786                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10787                 });
10788             }
10789             
10790             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791                 inputblock.cls += ' has-feedback';
10792                 inputblock.cn.push(feedback);
10793             }
10794         };
10795         var indicator = {
10796             tag : 'i',
10797             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798             tooltip : 'This field is required'
10799         };
10800         if (this.allowBlank ) {
10801             indicator.style = this.allowBlank ? ' display:none' : '';
10802         }
10803         if (align ==='left' && this.fieldLabel.length) {
10804             
10805             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10806             
10807             cfg.cn = [
10808                 indicator,
10809                 {
10810                     tag: 'label',
10811                     'for' :  id,
10812                     cls : 'control-label col-form-label',
10813                     html : this.fieldLabel
10814
10815                 },
10816                 {
10817                     cls : "", 
10818                     cn: [
10819                         inputblock
10820                     ]
10821                 }
10822             ];
10823             
10824             var labelCfg = cfg.cn[1];
10825             var contentCfg = cfg.cn[2];
10826             
10827             if(this.indicatorpos == 'right'){
10828                 cfg.cn = [
10829                     {
10830                         tag: 'label',
10831                         'for' :  id,
10832                         cls : 'control-label col-form-label',
10833                         cn : [
10834                             {
10835                                 tag : 'span',
10836                                 html : this.fieldLabel
10837                             },
10838                             indicator
10839                         ]
10840                     },
10841                     {
10842                         cls : "",
10843                         cn: [
10844                             inputblock
10845                         ]
10846                     }
10847
10848                 ];
10849                 
10850                 labelCfg = cfg.cn[0];
10851                 contentCfg = cfg.cn[1];
10852             
10853             }
10854             
10855             if(this.labelWidth > 12){
10856                 labelCfg.style = "width: " + this.labelWidth + 'px';
10857             }
10858             
10859             if(this.labelWidth < 13 && this.labelmd == 0){
10860                 this.labelmd = this.labelWidth;
10861             }
10862             
10863             if(this.labellg > 0){
10864                 labelCfg.cls += ' col-lg-' + this.labellg;
10865                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10866             }
10867             
10868             if(this.labelmd > 0){
10869                 labelCfg.cls += ' col-md-' + this.labelmd;
10870                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10871             }
10872             
10873             if(this.labelsm > 0){
10874                 labelCfg.cls += ' col-sm-' + this.labelsm;
10875                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10876             }
10877             
10878             if(this.labelxs > 0){
10879                 labelCfg.cls += ' col-xs-' + this.labelxs;
10880                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881             }
10882             
10883             
10884         } else if ( this.fieldLabel.length) {
10885                 
10886             
10887             
10888             cfg.cn = [
10889                 {
10890                     tag : 'i',
10891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892                     tooltip : 'This field is required',
10893                     style : this.allowBlank ? ' display:none' : '' 
10894                 },
10895                 {
10896                     tag: 'label',
10897                    //cls : 'input-group-addon',
10898                     html : this.fieldLabel
10899
10900                 },
10901
10902                inputblock
10903
10904            ];
10905            
10906            if(this.indicatorpos == 'right'){
10907        
10908                 cfg.cn = [
10909                     {
10910                         tag: 'label',
10911                        //cls : 'input-group-addon',
10912                         html : this.fieldLabel
10913
10914                     },
10915                     {
10916                         tag : 'i',
10917                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918                         tooltip : 'This field is required',
10919                         style : this.allowBlank ? ' display:none' : '' 
10920                     },
10921
10922                    inputblock
10923
10924                ];
10925
10926             }
10927
10928         } else {
10929             
10930             cfg.cn = [
10931
10932                     inputblock
10933
10934             ];
10935                 
10936                 
10937         };
10938         
10939         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10940            cfg.cls += ' navbar-form';
10941         }
10942         
10943         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944             // on BS4 we do this only if not form 
10945             cfg.cls += ' navbar-form';
10946             cfg.tag = 'li';
10947         }
10948         
10949         return cfg;
10950         
10951     },
10952     /**
10953      * return the real input element.
10954      */
10955     inputEl: function ()
10956     {
10957         return this.el.select('input.form-control',true).first();
10958     },
10959     
10960     tooltipEl : function()
10961     {
10962         return this.inputEl();
10963     },
10964     
10965     indicatorEl : function()
10966     {
10967         if (Roo.bootstrap.version == 4) {
10968             return false; // not enabled in v4 yet.
10969         }
10970         
10971         var indicator = this.el.select('i.roo-required-indicator',true).first();
10972         
10973         if(!indicator){
10974             return false;
10975         }
10976         
10977         return indicator;
10978         
10979     },
10980     
10981     setDisabled : function(v)
10982     {
10983         var i  = this.inputEl().dom;
10984         if (!v) {
10985             i.removeAttribute('disabled');
10986             return;
10987             
10988         }
10989         i.setAttribute('disabled','true');
10990     },
10991     initEvents : function()
10992     {
10993           
10994         this.inputEl().on("keydown" , this.fireKey,  this);
10995         this.inputEl().on("focus", this.onFocus,  this);
10996         this.inputEl().on("blur", this.onBlur,  this);
10997         
10998         this.inputEl().relayEvent('keyup', this);
10999         
11000         this.indicator = this.indicatorEl();
11001         
11002         if(this.indicator){
11003             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11004         }
11005  
11006         // reference to original value for reset
11007         this.originalValue = this.getValue();
11008         //Roo.form.TextField.superclass.initEvents.call(this);
11009         if(this.validationEvent == 'keyup'){
11010             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011             this.inputEl().on('keyup', this.filterValidation, this);
11012         }
11013         else if(this.validationEvent !== false){
11014             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11015         }
11016         
11017         if(this.selectOnFocus){
11018             this.on("focus", this.preFocus, this);
11019             
11020         }
11021         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022             this.inputEl().on("keypress", this.filterKeys, this);
11023         } else {
11024             this.inputEl().relayEvent('keypress', this);
11025         }
11026        /* if(this.grow){
11027             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11028             this.el.on("click", this.autoSize,  this);
11029         }
11030         */
11031         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11033         }
11034         
11035         if (typeof(this.before) == 'object') {
11036             this.before.render(this.el.select('.roo-input-before',true).first());
11037         }
11038         if (typeof(this.after) == 'object') {
11039             this.after.render(this.el.select('.roo-input-after',true).first());
11040         }
11041         
11042         this.inputEl().on('change', this.onChange, this);
11043         
11044     },
11045     filterValidation : function(e){
11046         if(!e.isNavKeyPress()){
11047             this.validationTask.delay(this.validationDelay);
11048         }
11049     },
11050      /**
11051      * Validates the field value
11052      * @return {Boolean} True if the value is valid, else false
11053      */
11054     validate : function(){
11055         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056         if(this.disabled || this.validateValue(this.getRawValue())){
11057             this.markValid();
11058             return true;
11059         }
11060         
11061         this.markInvalid();
11062         return false;
11063     },
11064     
11065     
11066     /**
11067      * Validates a value according to the field's validation rules and marks the field as invalid
11068      * if the validation fails
11069      * @param {Mixed} value The value to validate
11070      * @return {Boolean} True if the value is valid, else false
11071      */
11072     validateValue : function(value)
11073     {
11074         if(this.getVisibilityEl().hasClass('hidden')){
11075             return true;
11076         }
11077         
11078         if(value.length < 1)  { // if it's blank
11079             if(this.allowBlank){
11080                 return true;
11081             }
11082             return false;
11083         }
11084         
11085         if(value.length < this.minLength){
11086             return false;
11087         }
11088         if(value.length > this.maxLength){
11089             return false;
11090         }
11091         if(this.vtype){
11092             var vt = Roo.form.VTypes;
11093             if(!vt[this.vtype](value, this)){
11094                 return false;
11095             }
11096         }
11097         if(typeof this.validator == "function"){
11098             var msg = this.validator(value);
11099             if(msg !== true){
11100                 return false;
11101             }
11102             if (typeof(msg) == 'string') {
11103                 this.invalidText = msg;
11104             }
11105         }
11106         
11107         if(this.regex && !this.regex.test(value)){
11108             return false;
11109         }
11110         
11111         return true;
11112     },
11113     
11114      // private
11115     fireKey : function(e){
11116         //Roo.log('field ' + e.getKey());
11117         if(e.isNavKeyPress()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121     focus : function (selectText){
11122         if(this.rendered){
11123             this.inputEl().focus();
11124             if(selectText === true){
11125                 this.inputEl().dom.select();
11126             }
11127         }
11128         return this;
11129     } ,
11130     
11131     onFocus : function(){
11132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133            // this.el.addClass(this.focusClass);
11134         }
11135         if(!this.hasFocus){
11136             this.hasFocus = true;
11137             this.startValue = this.getValue();
11138             this.fireEvent("focus", this);
11139         }
11140     },
11141     
11142     beforeBlur : Roo.emptyFn,
11143
11144     
11145     // private
11146     onBlur : function(){
11147         this.beforeBlur();
11148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149             //this.el.removeClass(this.focusClass);
11150         }
11151         this.hasFocus = false;
11152         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11153             this.validate();
11154         }
11155         var v = this.getValue();
11156         if(String(v) !== String(this.startValue)){
11157             this.fireEvent('change', this, v, this.startValue);
11158         }
11159         this.fireEvent("blur", this);
11160     },
11161     
11162     onChange : function(e)
11163     {
11164         var v = this.getValue();
11165         if(String(v) !== String(this.startValue)){
11166             this.fireEvent('change', this, v, this.startValue);
11167         }
11168         
11169     },
11170     
11171     /**
11172      * Resets the current field value to the originally loaded value and clears any validation messages
11173      */
11174     reset : function(){
11175         this.setValue(this.originalValue);
11176         this.validate();
11177     },
11178      /**
11179      * Returns the name of the field
11180      * @return {Mixed} name The name field
11181      */
11182     getName: function(){
11183         return this.name;
11184     },
11185      /**
11186      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11187      * @return {Mixed} value The field value
11188      */
11189     getValue : function(){
11190         
11191         var v = this.inputEl().getValue();
11192         
11193         return v;
11194     },
11195     /**
11196      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11197      * @return {Mixed} value The field value
11198      */
11199     getRawValue : function(){
11200         var v = this.inputEl().getValue();
11201         
11202         return v;
11203     },
11204     
11205     /**
11206      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11207      * @param {Mixed} value The value to set
11208      */
11209     setRawValue : function(v){
11210         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11211     },
11212     
11213     selectText : function(start, end){
11214         var v = this.getRawValue();
11215         if(v.length > 0){
11216             start = start === undefined ? 0 : start;
11217             end = end === undefined ? v.length : end;
11218             var d = this.inputEl().dom;
11219             if(d.setSelectionRange){
11220                 d.setSelectionRange(start, end);
11221             }else if(d.createTextRange){
11222                 var range = d.createTextRange();
11223                 range.moveStart("character", start);
11224                 range.moveEnd("character", v.length-end);
11225                 range.select();
11226             }
11227         }
11228     },
11229     
11230     /**
11231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11232      * @param {Mixed} value The value to set
11233      */
11234     setValue : function(v){
11235         this.value = v;
11236         if(this.rendered){
11237             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11238             this.validate();
11239         }
11240     },
11241     
11242     /*
11243     processValue : function(value){
11244         if(this.stripCharsRe){
11245             var newValue = value.replace(this.stripCharsRe, '');
11246             if(newValue !== value){
11247                 this.setRawValue(newValue);
11248                 return newValue;
11249             }
11250         }
11251         return value;
11252     },
11253   */
11254     preFocus : function(){
11255         
11256         if(this.selectOnFocus){
11257             this.inputEl().dom.select();
11258         }
11259     },
11260     filterKeys : function(e){
11261         var k = e.getKey();
11262         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11263             return;
11264         }
11265         var c = e.getCharCode(), cc = String.fromCharCode(c);
11266         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11267             return;
11268         }
11269         if(!this.maskRe.test(cc)){
11270             e.stopEvent();
11271         }
11272     },
11273      /**
11274      * Clear any invalid styles/messages for this field
11275      */
11276     clearInvalid : function(){
11277         
11278         if(!this.el || this.preventMark){ // not rendered
11279             return;
11280         }
11281         
11282         
11283         this.el.removeClass([this.invalidClass, 'is-invalid']);
11284         
11285         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11286             
11287             var feedback = this.el.select('.form-control-feedback', true).first();
11288             
11289             if(feedback){
11290                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11291             }
11292             
11293         }
11294         
11295         if(this.indicator){
11296             this.indicator.removeClass('visible');
11297             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11298         }
11299         
11300         this.fireEvent('valid', this);
11301     },
11302     
11303      /**
11304      * Mark this field as valid
11305      */
11306     markValid : function()
11307     {
11308         if(!this.el  || this.preventMark){ // not rendered...
11309             return;
11310         }
11311         
11312         this.el.removeClass([this.invalidClass, this.validClass]);
11313         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11314
11315         var feedback = this.el.select('.form-control-feedback', true).first();
11316             
11317         if(feedback){
11318             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11319         }
11320         
11321         if(this.indicator){
11322             this.indicator.removeClass('visible');
11323             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11324         }
11325         
11326         if(this.disabled){
11327             return;
11328         }
11329         
11330            
11331         if(this.allowBlank && !this.getRawValue().length){
11332             return;
11333         }
11334         if (Roo.bootstrap.version == 3) {
11335             this.el.addClass(this.validClass);
11336         } else {
11337             this.inputEl().addClass('is-valid');
11338         }
11339
11340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11341             
11342             var feedback = this.el.select('.form-control-feedback', true).first();
11343             
11344             if(feedback){
11345                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11347             }
11348             
11349         }
11350         
11351         this.fireEvent('valid', this);
11352     },
11353     
11354      /**
11355      * Mark this field as invalid
11356      * @param {String} msg The validation message
11357      */
11358     markInvalid : function(msg)
11359     {
11360         if(!this.el  || this.preventMark){ // not rendered
11361             return;
11362         }
11363         
11364         this.el.removeClass([this.invalidClass, this.validClass]);
11365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11366         
11367         var feedback = this.el.select('.form-control-feedback', true).first();
11368             
11369         if(feedback){
11370             this.el.select('.form-control-feedback', true).first().removeClass(
11371                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11372         }
11373
11374         if(this.disabled){
11375             return;
11376         }
11377         
11378         if(this.allowBlank && !this.getRawValue().length){
11379             return;
11380         }
11381         
11382         if(this.indicator){
11383             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384             this.indicator.addClass('visible');
11385         }
11386         if (Roo.bootstrap.version == 3) {
11387             this.el.addClass(this.invalidClass);
11388         } else {
11389             this.inputEl().addClass('is-invalid');
11390         }
11391         
11392         
11393         
11394         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11395             
11396             var feedback = this.el.select('.form-control-feedback', true).first();
11397             
11398             if(feedback){
11399                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400                 
11401                 if(this.getValue().length || this.forceFeedback){
11402                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11403                 }
11404                 
11405             }
11406             
11407         }
11408         
11409         this.fireEvent('invalid', this, msg);
11410     },
11411     // private
11412     SafariOnKeyDown : function(event)
11413     {
11414         // this is a workaround for a password hang bug on chrome/ webkit.
11415         if (this.inputEl().dom.type != 'password') {
11416             return;
11417         }
11418         
11419         var isSelectAll = false;
11420         
11421         if(this.inputEl().dom.selectionEnd > 0){
11422             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11423         }
11424         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425             event.preventDefault();
11426             this.setValue('');
11427             return;
11428         }
11429         
11430         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11431             
11432             event.preventDefault();
11433             // this is very hacky as keydown always get's upper case.
11434             //
11435             var cc = String.fromCharCode(event.getCharCode());
11436             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11437             
11438         }
11439     },
11440     adjustWidth : function(tag, w){
11441         tag = tag.toLowerCase();
11442         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444                 if(tag == 'input'){
11445                     return w + 2;
11446                 }
11447                 if(tag == 'textarea'){
11448                     return w-2;
11449                 }
11450             }else if(Roo.isOpera){
11451                 if(tag == 'input'){
11452                     return w + 2;
11453                 }
11454                 if(tag == 'textarea'){
11455                     return w-2;
11456                 }
11457             }
11458         }
11459         return w;
11460     },
11461     
11462     setFieldLabel : function(v)
11463     {
11464         if(!this.rendered){
11465             return;
11466         }
11467         
11468         if(this.indicatorEl()){
11469             var ar = this.el.select('label > span',true);
11470             
11471             if (ar.elements.length) {
11472                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473                 this.fieldLabel = v;
11474                 return;
11475             }
11476             
11477             var br = this.el.select('label',true);
11478             
11479             if(br.elements.length) {
11480                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481                 this.fieldLabel = v;
11482                 return;
11483             }
11484             
11485             Roo.log('Cannot Found any of label > span || label in input');
11486             return;
11487         }
11488         
11489         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490         this.fieldLabel = v;
11491         
11492         
11493     }
11494 });
11495
11496  
11497 /*
11498  * - LGPL
11499  *
11500  * Input
11501  * 
11502  */
11503
11504 /**
11505  * @class Roo.bootstrap.TextArea
11506  * @extends Roo.bootstrap.Input
11507  * Bootstrap TextArea class
11508  * @cfg {Number} cols Specifies the visible width of a text area
11509  * @cfg {Number} rows Specifies the visible number of lines in a text area
11510  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512  * @cfg {string} html text
11513  * 
11514  * @constructor
11515  * Create a new TextArea
11516  * @param {Object} config The config object
11517  */
11518
11519 Roo.bootstrap.TextArea = function(config){
11520     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521    
11522 };
11523
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11525      
11526     cols : false,
11527     rows : 5,
11528     readOnly : false,
11529     warp : 'soft',
11530     resize : false,
11531     value: false,
11532     html: false,
11533     
11534     getAutoCreate : function(){
11535         
11536         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11537         
11538         var id = Roo.id();
11539         
11540         var cfg = {};
11541         
11542         if(this.inputType != 'hidden'){
11543             cfg.cls = 'form-group' //input-group
11544         }
11545         
11546         var input =  {
11547             tag: 'textarea',
11548             id : id,
11549             warp : this.warp,
11550             rows : this.rows,
11551             value : this.value || '',
11552             html: this.html || '',
11553             cls : 'form-control',
11554             placeholder : this.placeholder || '' 
11555             
11556         };
11557         
11558         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559             input.maxLength = this.maxLength;
11560         }
11561         
11562         if(this.resize){
11563             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564         }
11565         
11566         if(this.cols){
11567             input.cols = this.cols;
11568         }
11569         
11570         if (this.readOnly) {
11571             input.readonly = true;
11572         }
11573         
11574         if (this.name) {
11575             input.name = this.name;
11576         }
11577         
11578         if (this.size) {
11579             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580         }
11581         
11582         var settings=this;
11583         ['xs','sm','md','lg'].map(function(size){
11584             if (settings[size]) {
11585                 cfg.cls += ' col-' + size + '-' + settings[size];
11586             }
11587         });
11588         
11589         var inputblock = input;
11590         
11591         if(this.hasFeedback && !this.allowBlank){
11592             
11593             var feedback = {
11594                 tag: 'span',
11595                 cls: 'glyphicon form-control-feedback'
11596             };
11597
11598             inputblock = {
11599                 cls : 'has-feedback',
11600                 cn :  [
11601                     input,
11602                     feedback
11603                 ] 
11604             };  
11605         }
11606         
11607         
11608         if (this.before || this.after) {
11609             
11610             inputblock = {
11611                 cls : 'input-group',
11612                 cn :  [] 
11613             };
11614             if (this.before) {
11615                 inputblock.cn.push({
11616                     tag :'span',
11617                     cls : 'input-group-addon',
11618                     html : this.before
11619                 });
11620             }
11621             
11622             inputblock.cn.push(input);
11623             
11624             if(this.hasFeedback && !this.allowBlank){
11625                 inputblock.cls += ' has-feedback';
11626                 inputblock.cn.push(feedback);
11627             }
11628             
11629             if (this.after) {
11630                 inputblock.cn.push({
11631                     tag :'span',
11632                     cls : 'input-group-addon',
11633                     html : this.after
11634                 });
11635             }
11636             
11637         }
11638         
11639         if (align ==='left' && this.fieldLabel.length) {
11640             cfg.cn = [
11641                 {
11642                     tag: 'label',
11643                     'for' :  id,
11644                     cls : 'control-label',
11645                     html : this.fieldLabel
11646                 },
11647                 {
11648                     cls : "",
11649                     cn: [
11650                         inputblock
11651                     ]
11652                 }
11653
11654             ];
11655             
11656             if(this.labelWidth > 12){
11657                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11658             }
11659
11660             if(this.labelWidth < 13 && this.labelmd == 0){
11661                 this.labelmd = this.labelWidth;
11662             }
11663
11664             if(this.labellg > 0){
11665                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11667             }
11668
11669             if(this.labelmd > 0){
11670                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11672             }
11673
11674             if(this.labelsm > 0){
11675                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11677             }
11678
11679             if(this.labelxs > 0){
11680                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11682             }
11683             
11684         } else if ( this.fieldLabel.length) {
11685             cfg.cn = [
11686
11687                {
11688                    tag: 'label',
11689                    //cls : 'input-group-addon',
11690                    html : this.fieldLabel
11691
11692                },
11693
11694                inputblock
11695
11696            ];
11697
11698         } else {
11699
11700             cfg.cn = [
11701
11702                 inputblock
11703
11704             ];
11705                 
11706         }
11707         
11708         if (this.disabled) {
11709             input.disabled=true;
11710         }
11711         
11712         return cfg;
11713         
11714     },
11715     /**
11716      * return the real textarea element.
11717      */
11718     inputEl: function ()
11719     {
11720         return this.el.select('textarea.form-control',true).first();
11721     },
11722     
11723     /**
11724      * Clear any invalid styles/messages for this field
11725      */
11726     clearInvalid : function()
11727     {
11728         
11729         if(!this.el || this.preventMark){ // not rendered
11730             return;
11731         }
11732         
11733         var label = this.el.select('label', true).first();
11734         var icon = this.el.select('i.fa-star', true).first();
11735         
11736         if(label && icon){
11737             icon.remove();
11738         }
11739         this.el.removeClass( this.validClass);
11740         this.inputEl().removeClass('is-invalid');
11741          
11742         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11743             
11744             var feedback = this.el.select('.form-control-feedback', true).first();
11745             
11746             if(feedback){
11747                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11748             }
11749             
11750         }
11751         
11752         this.fireEvent('valid', this);
11753     },
11754     
11755      /**
11756      * Mark this field as valid
11757      */
11758     markValid : function()
11759     {
11760         if(!this.el  || this.preventMark){ // not rendered
11761             return;
11762         }
11763         
11764         this.el.removeClass([this.invalidClass, this.validClass]);
11765         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11766         
11767         var feedback = this.el.select('.form-control-feedback', true).first();
11768             
11769         if(feedback){
11770             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11771         }
11772
11773         if(this.disabled || this.allowBlank){
11774             return;
11775         }
11776         
11777         var label = this.el.select('label', true).first();
11778         var icon = this.el.select('i.fa-star', true).first();
11779         
11780         if(label && icon){
11781             icon.remove();
11782         }
11783         if (Roo.bootstrap.version == 3) {
11784             this.el.addClass(this.validClass);
11785         } else {
11786             this.inputEl().addClass('is-valid');
11787         }
11788         
11789         
11790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11791             
11792             var feedback = this.el.select('.form-control-feedback', true).first();
11793             
11794             if(feedback){
11795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11797             }
11798             
11799         }
11800         
11801         this.fireEvent('valid', this);
11802     },
11803     
11804      /**
11805      * Mark this field as invalid
11806      * @param {String} msg The validation message
11807      */
11808     markInvalid : function(msg)
11809     {
11810         if(!this.el  || this.preventMark){ // not rendered
11811             return;
11812         }
11813         
11814         this.el.removeClass([this.invalidClass, this.validClass]);
11815         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11816         
11817         var feedback = this.el.select('.form-control-feedback', true).first();
11818             
11819         if(feedback){
11820             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821         }
11822
11823         if(this.disabled || this.allowBlank){
11824             return;
11825         }
11826         
11827         var label = this.el.select('label', true).first();
11828         var icon = this.el.select('i.fa-star', true).first();
11829         
11830         if(!this.getValue().length && label && !icon){
11831             this.el.createChild({
11832                 tag : 'i',
11833                 cls : 'text-danger fa fa-lg fa-star',
11834                 tooltip : 'This field is required',
11835                 style : 'margin-right:5px;'
11836             }, label, true);
11837         }
11838         
11839         if (Roo.bootstrap.version == 3) {
11840             this.el.addClass(this.invalidClass);
11841         } else {
11842             this.inputEl().addClass('is-invalid');
11843         }
11844         
11845         // fixme ... this may be depricated need to test..
11846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11847             
11848             var feedback = this.el.select('.form-control-feedback', true).first();
11849             
11850             if(feedback){
11851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852                 
11853                 if(this.getValue().length || this.forceFeedback){
11854                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11855                 }
11856                 
11857             }
11858             
11859         }
11860         
11861         this.fireEvent('invalid', this, msg);
11862     }
11863 });
11864
11865  
11866 /*
11867  * - LGPL
11868  *
11869  * trigger field - base class for combo..
11870  * 
11871  */
11872  
11873 /**
11874  * @class Roo.bootstrap.TriggerField
11875  * @extends Roo.bootstrap.Input
11876  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879  * for which you can provide a custom implementation.  For example:
11880  * <pre><code>
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11884 </code></pre>
11885  *
11886  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11889  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11891
11892  * @constructor
11893  * Create a new TriggerField.
11894  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895  * to the base TextField)
11896  */
11897 Roo.bootstrap.TriggerField = function(config){
11898     this.mimicing = false;
11899     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11900 };
11901
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11903     /**
11904      * @cfg {String} triggerClass A CSS class to apply to the trigger
11905      */
11906      /**
11907      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11908      */
11909     hideTrigger:false,
11910
11911     /**
11912      * @cfg {Boolean} removable (true|false) special filter default false
11913      */
11914     removable : false,
11915     
11916     /** @cfg {Boolean} grow @hide */
11917     /** @cfg {Number} growMin @hide */
11918     /** @cfg {Number} growMax @hide */
11919
11920     /**
11921      * @hide 
11922      * @method
11923      */
11924     autoSize: Roo.emptyFn,
11925     // private
11926     monitorTab : true,
11927     // private
11928     deferHeight : true,
11929
11930     
11931     actionMode : 'wrap',
11932     
11933     caret : false,
11934     
11935     
11936     getAutoCreate : function(){
11937        
11938         var align = this.labelAlign || this.parentLabelAlign();
11939         
11940         var id = Roo.id();
11941         
11942         var cfg = {
11943             cls: 'form-group' //input-group
11944         };
11945         
11946         
11947         var input =  {
11948             tag: 'input',
11949             id : id,
11950             type : this.inputType,
11951             cls : 'form-control',
11952             autocomplete: 'new-password',
11953             placeholder : this.placeholder || '' 
11954             
11955         };
11956         if (this.name) {
11957             input.name = this.name;
11958         }
11959         if (this.size) {
11960             input.cls += ' input-' + this.size;
11961         }
11962         
11963         if (this.disabled) {
11964             input.disabled=true;
11965         }
11966         
11967         var inputblock = input;
11968         
11969         if(this.hasFeedback && !this.allowBlank){
11970             
11971             var feedback = {
11972                 tag: 'span',
11973                 cls: 'glyphicon form-control-feedback'
11974             };
11975             
11976             if(this.removable && !this.editable  ){
11977                 inputblock = {
11978                     cls : 'has-feedback',
11979                     cn :  [
11980                         inputblock,
11981                         {
11982                             tag: 'button',
11983                             html : 'x',
11984                             cls : 'roo-combo-removable-btn close'
11985                         },
11986                         feedback
11987                     ] 
11988                 };
11989             } else {
11990                 inputblock = {
11991                     cls : 'has-feedback',
11992                     cn :  [
11993                         inputblock,
11994                         feedback
11995                     ] 
11996                 };
11997             }
11998
11999         } else {
12000             if(this.removable && !this.editable ){
12001                 inputblock = {
12002                     cls : 'roo-removable',
12003                     cn :  [
12004                         inputblock,
12005                         {
12006                             tag: 'button',
12007                             html : 'x',
12008                             cls : 'roo-combo-removable-btn close'
12009                         }
12010                     ] 
12011                 };
12012             }
12013         }
12014         
12015         if (this.before || this.after) {
12016             
12017             inputblock = {
12018                 cls : 'input-group',
12019                 cn :  [] 
12020             };
12021             if (this.before) {
12022                 inputblock.cn.push({
12023                     tag :'span',
12024                     cls : 'input-group-addon input-group-prepend input-group-text',
12025                     html : this.before
12026                 });
12027             }
12028             
12029             inputblock.cn.push(input);
12030             
12031             if(this.hasFeedback && !this.allowBlank){
12032                 inputblock.cls += ' has-feedback';
12033                 inputblock.cn.push(feedback);
12034             }
12035             
12036             if (this.after) {
12037                 inputblock.cn.push({
12038                     tag :'span',
12039                     cls : 'input-group-addon input-group-append input-group-text',
12040                     html : this.after
12041                 });
12042             }
12043             
12044         };
12045         
12046       
12047         
12048         var ibwrap = inputblock;
12049         
12050         if(this.multiple){
12051             ibwrap = {
12052                 tag: 'ul',
12053                 cls: 'roo-select2-choices',
12054                 cn:[
12055                     {
12056                         tag: 'li',
12057                         cls: 'roo-select2-search-field',
12058                         cn: [
12059
12060                             inputblock
12061                         ]
12062                     }
12063                 ]
12064             };
12065                 
12066         }
12067         
12068         var combobox = {
12069             cls: 'roo-select2-container input-group',
12070             cn: [
12071                  {
12072                     tag: 'input',
12073                     type : 'hidden',
12074                     cls: 'form-hidden-field'
12075                 },
12076                 ibwrap
12077             ]
12078         };
12079         
12080         if(!this.multiple && this.showToggleBtn){
12081             
12082             var caret = {
12083                         tag: 'span',
12084                         cls: 'caret'
12085              };
12086             if (this.caret != false) {
12087                 caret = {
12088                      tag: 'i',
12089                      cls: 'fa fa-' + this.caret
12090                 };
12091                 
12092             }
12093             
12094             combobox.cn.push({
12095                 tag :'span',
12096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12097                 cn : [
12098                     Roo.bootstrap.version == 3 ? caret : '',
12099                     {
12100                         tag: 'span',
12101                         cls: 'combobox-clear',
12102                         cn  : [
12103                             {
12104                                 tag : 'i',
12105                                 cls: 'icon-remove'
12106                             }
12107                         ]
12108                     }
12109                 ]
12110
12111             })
12112         }
12113         
12114         if(this.multiple){
12115             combobox.cls += ' roo-select2-container-multi';
12116         }
12117          var indicator = {
12118             tag : 'i',
12119             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120             tooltip : 'This field is required'
12121         };
12122         if (Roo.bootstrap.version == 4) {
12123             indicator = {
12124                 tag : 'i',
12125                 style : 'display:none'
12126             };
12127         }
12128         
12129         
12130         if (align ==='left' && this.fieldLabel.length) {
12131             
12132             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12133
12134             cfg.cn = [
12135                 indicator,
12136                 {
12137                     tag: 'label',
12138                     'for' :  id,
12139                     cls : 'control-label',
12140                     html : this.fieldLabel
12141
12142                 },
12143                 {
12144                     cls : "", 
12145                     cn: [
12146                         combobox
12147                     ]
12148                 }
12149
12150             ];
12151             
12152             var labelCfg = cfg.cn[1];
12153             var contentCfg = cfg.cn[2];
12154             
12155             if(this.indicatorpos == 'right'){
12156                 cfg.cn = [
12157                     {
12158                         tag: 'label',
12159                         'for' :  id,
12160                         cls : 'control-label',
12161                         cn : [
12162                             {
12163                                 tag : 'span',
12164                                 html : this.fieldLabel
12165                             },
12166                             indicator
12167                         ]
12168                     },
12169                     {
12170                         cls : "", 
12171                         cn: [
12172                             combobox
12173                         ]
12174                     }
12175
12176                 ];
12177                 
12178                 labelCfg = cfg.cn[0];
12179                 contentCfg = cfg.cn[1];
12180             }
12181             
12182             if(this.labelWidth > 12){
12183                 labelCfg.style = "width: " + this.labelWidth + 'px';
12184             }
12185             
12186             if(this.labelWidth < 13 && this.labelmd == 0){
12187                 this.labelmd = this.labelWidth;
12188             }
12189             
12190             if(this.labellg > 0){
12191                 labelCfg.cls += ' col-lg-' + this.labellg;
12192                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12193             }
12194             
12195             if(this.labelmd > 0){
12196                 labelCfg.cls += ' col-md-' + this.labelmd;
12197                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12198             }
12199             
12200             if(this.labelsm > 0){
12201                 labelCfg.cls += ' col-sm-' + this.labelsm;
12202                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12203             }
12204             
12205             if(this.labelxs > 0){
12206                 labelCfg.cls += ' col-xs-' + this.labelxs;
12207                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12208             }
12209             
12210         } else if ( this.fieldLabel.length) {
12211 //                Roo.log(" label");
12212             cfg.cn = [
12213                 indicator,
12214                {
12215                    tag: 'label',
12216                    //cls : 'input-group-addon',
12217                    html : this.fieldLabel
12218
12219                },
12220
12221                combobox
12222
12223             ];
12224             
12225             if(this.indicatorpos == 'right'){
12226                 
12227                 cfg.cn = [
12228                     {
12229                        tag: 'label',
12230                        cn : [
12231                            {
12232                                tag : 'span',
12233                                html : this.fieldLabel
12234                            },
12235                            indicator
12236                        ]
12237
12238                     },
12239                     combobox
12240
12241                 ];
12242
12243             }
12244
12245         } else {
12246             
12247 //                Roo.log(" no label && no align");
12248                 cfg = combobox
12249                      
12250                 
12251         }
12252         
12253         var settings=this;
12254         ['xs','sm','md','lg'].map(function(size){
12255             if (settings[size]) {
12256                 cfg.cls += ' col-' + size + '-' + settings[size];
12257             }
12258         });
12259         
12260         return cfg;
12261         
12262     },
12263     
12264     
12265     
12266     // private
12267     onResize : function(w, h){
12268 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 //        if(typeof w == 'number'){
12270 //            var x = w - this.trigger.getWidth();
12271 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12272 //            this.trigger.setStyle('left', x+'px');
12273 //        }
12274     },
12275
12276     // private
12277     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12278
12279     // private
12280     getResizeEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     getPositionEl : function(){
12286         return this.inputEl();
12287     },
12288
12289     // private
12290     alignErrorIcon : function(){
12291         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292     },
12293
12294     // private
12295     initEvents : function(){
12296         
12297         this.createList();
12298         
12299         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301         if(!this.multiple && this.showToggleBtn){
12302             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303             if(this.hideTrigger){
12304                 this.trigger.setDisplayed(false);
12305             }
12306             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307         }
12308         
12309         if(this.multiple){
12310             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12311         }
12312         
12313         if(this.removable && !this.editable && !this.tickable){
12314             var close = this.closeTriggerEl();
12315             
12316             if(close){
12317                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318                 close.on('click', this.removeBtnClick, this, close);
12319             }
12320         }
12321         
12322         //this.trigger.addClassOnOver('x-form-trigger-over');
12323         //this.trigger.addClassOnClick('x-form-trigger-click');
12324         
12325         //if(!this.width){
12326         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327         //}
12328     },
12329     
12330     closeTriggerEl : function()
12331     {
12332         var close = this.el.select('.roo-combo-removable-btn', true).first();
12333         return close ? close : false;
12334     },
12335     
12336     removeBtnClick : function(e, h, el)
12337     {
12338         e.preventDefault();
12339         
12340         if(this.fireEvent("remove", this) !== false){
12341             this.reset();
12342             this.fireEvent("afterremove", this)
12343         }
12344     },
12345     
12346     createList : function()
12347     {
12348         this.list = Roo.get(document.body).createChild({
12349             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350             cls: 'typeahead typeahead-long dropdown-menu shadow',
12351             style: 'display:none'
12352         });
12353         
12354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12355         
12356     },
12357
12358     // private
12359     initTrigger : function(){
12360        
12361     },
12362
12363     // private
12364     onDestroy : function(){
12365         if(this.trigger){
12366             this.trigger.removeAllListeners();
12367           //  this.trigger.remove();
12368         }
12369         //if(this.wrap){
12370         //    this.wrap.remove();
12371         //}
12372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373     },
12374
12375     // private
12376     onFocus : function(){
12377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12378         /*
12379         if(!this.mimicing){
12380             this.wrap.addClass('x-trigger-wrap-focus');
12381             this.mimicing = true;
12382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383             if(this.monitorTab){
12384                 this.el.on("keydown", this.checkTab, this);
12385             }
12386         }
12387         */
12388     },
12389
12390     // private
12391     checkTab : function(e){
12392         if(e.getKey() == e.TAB){
12393             this.triggerBlur();
12394         }
12395     },
12396
12397     // private
12398     onBlur : function(){
12399         // do nothing
12400     },
12401
12402     // private
12403     mimicBlur : function(e, t){
12404         /*
12405         if(!this.wrap.contains(t) && this.validateBlur()){
12406             this.triggerBlur();
12407         }
12408         */
12409     },
12410
12411     // private
12412     triggerBlur : function(){
12413         this.mimicing = false;
12414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415         if(this.monitorTab){
12416             this.el.un("keydown", this.checkTab, this);
12417         }
12418         //this.wrap.removeClass('x-trigger-wrap-focus');
12419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420     },
12421
12422     // private
12423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424     validateBlur : function(e, t){
12425         return true;
12426     },
12427
12428     // private
12429     onDisable : function(){
12430         this.inputEl().dom.disabled = true;
12431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12432         //if(this.wrap){
12433         //    this.wrap.addClass('x-item-disabled');
12434         //}
12435     },
12436
12437     // private
12438     onEnable : function(){
12439         this.inputEl().dom.disabled = false;
12440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12441         //if(this.wrap){
12442         //    this.el.removeClass('x-item-disabled');
12443         //}
12444     },
12445
12446     // private
12447     onShow : function(){
12448         var ae = this.getActionEl();
12449         
12450         if(ae){
12451             ae.dom.style.display = '';
12452             ae.dom.style.visibility = 'visible';
12453         }
12454     },
12455
12456     // private
12457     
12458     onHide : function(){
12459         var ae = this.getActionEl();
12460         ae.dom.style.display = 'none';
12461     },
12462
12463     /**
12464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12465      * by an implementing function.
12466      * @method
12467      * @param {EventObject} e
12468      */
12469     onTriggerClick : Roo.emptyFn
12470 });
12471  
12472 /*
12473 * Licence: LGPL
12474 */
12475
12476 /**
12477  * @class Roo.bootstrap.CardUploader
12478  * @extends Roo.bootstrap.Button
12479  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480  * @cfg {Number} errorTimeout default 3000
12481  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12482  * @cfg {Array}  html The button text.
12483
12484  *
12485  * @constructor
12486  * Create a new CardUploader
12487  * @param {Object} config The config object
12488  */
12489
12490 Roo.bootstrap.CardUploader = function(config){
12491     
12492  
12493     
12494     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12495     
12496     
12497     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12498         return r.data.id
12499         });
12500     
12501     
12502 };
12503
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12505     
12506      
12507     errorTimeout : 3000,
12508      
12509     images : false,
12510    
12511     fileCollection : false,
12512     allowBlank : true,
12513     
12514     getAutoCreate : function()
12515     {
12516         
12517         var cfg =  {
12518             cls :'form-group' ,
12519             cn : [
12520                
12521                 {
12522                     tag: 'label',
12523                    //cls : 'input-group-addon',
12524                     html : this.fieldLabel
12525
12526                 },
12527
12528                 {
12529                     tag: 'input',
12530                     type : 'hidden',
12531                     value : this.value,
12532                     cls : 'd-none  form-control'
12533                 },
12534                 
12535                 {
12536                     tag: 'input',
12537                     multiple : 'multiple',
12538                     type : 'file',
12539                     cls : 'd-none  roo-card-upload-selector'
12540                 },
12541                 
12542                 {
12543                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12544                 },
12545                 {
12546                     cls : 'card-columns roo-card-uploader-container'
12547                 }
12548
12549             ]
12550         };
12551            
12552          
12553         return cfg;
12554     },
12555     
12556     getChildContainer : function() /// what children are added to.
12557     {
12558         return this.containerEl;
12559     },
12560    
12561     getButtonContainer : function() /// what children are added to.
12562     {
12563         return this.el.select(".roo-card-uploader-button-container").first();
12564     },
12565    
12566     initEvents : function()
12567     {
12568         
12569         Roo.bootstrap.Input.prototype.initEvents.call(this);
12570         
12571         var t = this;
12572         this.addxtype({
12573             xns: Roo.bootstrap,
12574
12575             xtype : 'Button',
12576             container_method : 'getButtonContainer' ,            
12577             html :  this.html, // fix changable?
12578             cls : 'w-100 ',
12579             listeners : {
12580                 'click' : function(btn, e) {
12581                     t.onClick(e);
12582                 }
12583             }
12584         });
12585         
12586         
12587         
12588         
12589         this.urlAPI = (window.createObjectURL && window) || 
12590                                 (window.URL && URL.revokeObjectURL && URL) || 
12591                                 (window.webkitURL && webkitURL);
12592                         
12593          
12594          
12595          
12596         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12597         
12598         this.selectorEl.on('change', this.onFileSelected, this);
12599         if (this.images) {
12600             var t = this;
12601             this.images.forEach(function(img) {
12602                 t.addCard(img)
12603             });
12604             this.images = false;
12605         }
12606         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12607          
12608        
12609     },
12610     
12611    
12612     onClick : function(e)
12613     {
12614         e.preventDefault();
12615          
12616         this.selectorEl.dom.click();
12617          
12618     },
12619     
12620     onFileSelected : function(e)
12621     {
12622         e.preventDefault();
12623         
12624         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12625             return;
12626         }
12627         
12628         Roo.each(this.selectorEl.dom.files, function(file){    
12629             this.addFile(file);
12630         }, this);
12631          
12632     },
12633     
12634       
12635     
12636       
12637     
12638     addFile : function(file)
12639     {
12640            
12641         if(typeof(file) === 'string'){
12642             throw "Add file by name?"; // should not happen
12643             return;
12644         }
12645         
12646         if(!file || !this.urlAPI){
12647             return;
12648         }
12649         
12650         // file;
12651         // file.type;
12652         
12653         var _this = this;
12654         
12655         
12656         var url = _this.urlAPI.createObjectURL( file);
12657            
12658         this.addCard({
12659             id : Roo.bootstrap.CardUploader.ID--,
12660             is_uploaded : false,
12661             src : url,
12662             title : file.name,
12663             mimetype : file.type,
12664             preview : false,
12665             is_deleted : 0
12666         })
12667         
12668     },
12669     
12670     addCard : function (data)
12671     {
12672         // hidden input element?
12673         // if the file is not an image...
12674         //then we need to use something other that and header_image
12675         var t = this;
12676         //   remove.....
12677         var footer = [
12678             {
12679                 xns : Roo.bootstrap,
12680                 xtype : 'CardFooter',
12681                 items: [
12682                     {
12683                         xns : Roo.bootstrap,
12684                         xtype : 'Element',
12685                         cls : 'd-flex',
12686                         items : [
12687                             
12688                             {
12689                                 xns : Roo.bootstrap,
12690                                 xtype : 'Button',
12691                                 html : String.format("<small>{0}</small>", data.title),
12692                                 cls : 'col-11 text-left',
12693                                 size: 'sm',
12694                                 weight: 'link',
12695                                 fa : 'download',
12696                                 listeners : {
12697                                     click : function() {
12698                                         this.downloadCard(data.id)
12699                                     }
12700                                 }
12701                             },
12702                           
12703                             {
12704                                 xns : Roo.bootstrap,
12705                                 xtype : 'Button',
12706                                 
12707                                 size : 'sm',
12708                                 weight: 'danger',
12709                                 cls : 'col-1',
12710                                 fa : 'times',
12711                                 listeners : {
12712                                     click : function() {
12713                                         t.removeCard(data.id)
12714                                     }
12715                                 }
12716                             }
12717                         ]
12718                     }
12719                     
12720                 ] 
12721             }
12722             
12723         ];
12724
12725         var cn = this.addxtype(
12726             {
12727                  
12728                 xns : Roo.bootstrap,
12729                 xtype : 'Card',
12730                 closeable : true,
12731                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12732                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12733                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12734                 data : data,
12735                 html : false,
12736                  
12737                 items : footer,
12738                 initEvents : function() {
12739                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12740                     this.imgEl = this.el.select('.card-img-top').first();
12741                     if (this.imgEl) {
12742                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12743                         this.imgEl.set({ 'pointer' : 'cursor' });
12744                                   
12745                     }
12746                     
12747                   
12748                 }
12749                 
12750             }
12751         );
12752         // dont' really need ot update items.
12753         // this.items.push(cn);
12754         this.fileCollection.add(cn);
12755         this.updateInput();
12756         
12757     },
12758     removeCard : function(id)
12759     {
12760         
12761         var card  = this.fileCollection.get(id);
12762         card.data.is_deleted = 1;
12763         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12764         this.fileCollection.remove(card);
12765         //this.items = this.items.filter(function(e) { return e != card });
12766         // dont' really need ot update items.
12767         card.el.dom.parentNode.removeChild(card.el.dom);
12768         
12769     },
12770     reset: function()
12771     {
12772         this.fileCollection.each(function(card) {
12773             card.el.dom.parentNode.removeChild(card.el.dom);    
12774         });
12775         this.fileCollection.clear();
12776         this.updateInput();
12777     },
12778     
12779     updateInput : function()
12780     {
12781         var data = [];
12782         this.fileCollection.each(function(e) {
12783             data.push(e.data);
12784         });
12785         
12786         this.inputEl().dom.value = JSON.stringify(data);
12787     }
12788     
12789     
12790 });
12791
12792
12793 Roo.bootstrap.CardUploader.ID = -1;/*
12794  * Based on:
12795  * Ext JS Library 1.1.1
12796  * Copyright(c) 2006-2007, Ext JS, LLC.
12797  *
12798  * Originally Released Under LGPL - original licence link has changed is not relivant.
12799  *
12800  * Fork - LGPL
12801  * <script type="text/javascript">
12802  */
12803
12804
12805 /**
12806  * @class Roo.data.SortTypes
12807  * @singleton
12808  * Defines the default sorting (casting?) comparison functions used when sorting data.
12809  */
12810 Roo.data.SortTypes = {
12811     /**
12812      * Default sort that does nothing
12813      * @param {Mixed} s The value being converted
12814      * @return {Mixed} The comparison value
12815      */
12816     none : function(s){
12817         return s;
12818     },
12819     
12820     /**
12821      * The regular expression used to strip tags
12822      * @type {RegExp}
12823      * @property
12824      */
12825     stripTagsRE : /<\/?[^>]+>/gi,
12826     
12827     /**
12828      * Strips all HTML tags to sort on text only
12829      * @param {Mixed} s The value being converted
12830      * @return {String} The comparison value
12831      */
12832     asText : function(s){
12833         return String(s).replace(this.stripTagsRE, "");
12834     },
12835     
12836     /**
12837      * Strips all HTML tags to sort on text only - Case insensitive
12838      * @param {Mixed} s The value being converted
12839      * @return {String} The comparison value
12840      */
12841     asUCText : function(s){
12842         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12843     },
12844     
12845     /**
12846      * Case insensitive string
12847      * @param {Mixed} s The value being converted
12848      * @return {String} The comparison value
12849      */
12850     asUCString : function(s) {
12851         return String(s).toUpperCase();
12852     },
12853     
12854     /**
12855      * Date sorting
12856      * @param {Mixed} s The value being converted
12857      * @return {Number} The comparison value
12858      */
12859     asDate : function(s) {
12860         if(!s){
12861             return 0;
12862         }
12863         if(s instanceof Date){
12864             return s.getTime();
12865         }
12866         return Date.parse(String(s));
12867     },
12868     
12869     /**
12870      * Float sorting
12871      * @param {Mixed} s The value being converted
12872      * @return {Float} The comparison value
12873      */
12874     asFloat : function(s) {
12875         var val = parseFloat(String(s).replace(/,/g, ""));
12876         if(isNaN(val)) {
12877             val = 0;
12878         }
12879         return val;
12880     },
12881     
12882     /**
12883      * Integer sorting
12884      * @param {Mixed} s The value being converted
12885      * @return {Number} The comparison value
12886      */
12887     asInt : function(s) {
12888         var val = parseInt(String(s).replace(/,/g, ""));
12889         if(isNaN(val)) {
12890             val = 0;
12891         }
12892         return val;
12893     }
12894 };/*
12895  * Based on:
12896  * Ext JS Library 1.1.1
12897  * Copyright(c) 2006-2007, Ext JS, LLC.
12898  *
12899  * Originally Released Under LGPL - original licence link has changed is not relivant.
12900  *
12901  * Fork - LGPL
12902  * <script type="text/javascript">
12903  */
12904
12905 /**
12906 * @class Roo.data.Record
12907  * Instances of this class encapsulate both record <em>definition</em> information, and record
12908  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12909  * to access Records cached in an {@link Roo.data.Store} object.<br>
12910  * <p>
12911  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12912  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12913  * objects.<br>
12914  * <p>
12915  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12916  * @constructor
12917  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12918  * {@link #create}. The parameters are the same.
12919  * @param {Array} data An associative Array of data values keyed by the field name.
12920  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12921  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12922  * not specified an integer id is generated.
12923  */
12924 Roo.data.Record = function(data, id){
12925     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12926     this.data = data;
12927 };
12928
12929 /**
12930  * Generate a constructor for a specific record layout.
12931  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12932  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12933  * Each field definition object may contain the following properties: <ul>
12934  * <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,
12935  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12936  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12937  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12938  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12939  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12940  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12941  * this may be omitted.</p></li>
12942  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12943  * <ul><li>auto (Default, implies no conversion)</li>
12944  * <li>string</li>
12945  * <li>int</li>
12946  * <li>float</li>
12947  * <li>boolean</li>
12948  * <li>date</li></ul></p></li>
12949  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12950  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12951  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12952  * by the Reader into an object that will be stored in the Record. It is passed the
12953  * following parameters:<ul>
12954  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12955  * </ul></p></li>
12956  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12957  * </ul>
12958  * <br>usage:<br><pre><code>
12959 var TopicRecord = Roo.data.Record.create(
12960     {name: 'title', mapping: 'topic_title'},
12961     {name: 'author', mapping: 'username'},
12962     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12963     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12964     {name: 'lastPoster', mapping: 'user2'},
12965     {name: 'excerpt', mapping: 'post_text'}
12966 );
12967
12968 var myNewRecord = new TopicRecord({
12969     title: 'Do my job please',
12970     author: 'noobie',
12971     totalPosts: 1,
12972     lastPost: new Date(),
12973     lastPoster: 'Animal',
12974     excerpt: 'No way dude!'
12975 });
12976 myStore.add(myNewRecord);
12977 </code></pre>
12978  * @method create
12979  * @static
12980  */
12981 Roo.data.Record.create = function(o){
12982     var f = function(){
12983         f.superclass.constructor.apply(this, arguments);
12984     };
12985     Roo.extend(f, Roo.data.Record);
12986     var p = f.prototype;
12987     p.fields = new Roo.util.MixedCollection(false, function(field){
12988         return field.name;
12989     });
12990     for(var i = 0, len = o.length; i < len; i++){
12991         p.fields.add(new Roo.data.Field(o[i]));
12992     }
12993     f.getField = function(name){
12994         return p.fields.get(name);  
12995     };
12996     return f;
12997 };
12998
12999 Roo.data.Record.AUTO_ID = 1000;
13000 Roo.data.Record.EDIT = 'edit';
13001 Roo.data.Record.REJECT = 'reject';
13002 Roo.data.Record.COMMIT = 'commit';
13003
13004 Roo.data.Record.prototype = {
13005     /**
13006      * Readonly flag - true if this record has been modified.
13007      * @type Boolean
13008      */
13009     dirty : false,
13010     editing : false,
13011     error: null,
13012     modified: null,
13013
13014     // private
13015     join : function(store){
13016         this.store = store;
13017     },
13018
13019     /**
13020      * Set the named field to the specified value.
13021      * @param {String} name The name of the field to set.
13022      * @param {Object} value The value to set the field to.
13023      */
13024     set : function(name, value){
13025         if(this.data[name] == value){
13026             return;
13027         }
13028         this.dirty = true;
13029         if(!this.modified){
13030             this.modified = {};
13031         }
13032         if(typeof this.modified[name] == 'undefined'){
13033             this.modified[name] = this.data[name];
13034         }
13035         this.data[name] = value;
13036         if(!this.editing && this.store){
13037             this.store.afterEdit(this);
13038         }       
13039     },
13040
13041     /**
13042      * Get the value of the named field.
13043      * @param {String} name The name of the field to get the value of.
13044      * @return {Object} The value of the field.
13045      */
13046     get : function(name){
13047         return this.data[name]; 
13048     },
13049
13050     // private
13051     beginEdit : function(){
13052         this.editing = true;
13053         this.modified = {}; 
13054     },
13055
13056     // private
13057     cancelEdit : function(){
13058         this.editing = false;
13059         delete this.modified;
13060     },
13061
13062     // private
13063     endEdit : function(){
13064         this.editing = false;
13065         if(this.dirty && this.store){
13066             this.store.afterEdit(this);
13067         }
13068     },
13069
13070     /**
13071      * Usually called by the {@link Roo.data.Store} which owns the Record.
13072      * Rejects all changes made to the Record since either creation, or the last commit operation.
13073      * Modified fields are reverted to their original values.
13074      * <p>
13075      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13076      * of reject operations.
13077      */
13078     reject : function(){
13079         var m = this.modified;
13080         for(var n in m){
13081             if(typeof m[n] != "function"){
13082                 this.data[n] = m[n];
13083             }
13084         }
13085         this.dirty = false;
13086         delete this.modified;
13087         this.editing = false;
13088         if(this.store){
13089             this.store.afterReject(this);
13090         }
13091     },
13092
13093     /**
13094      * Usually called by the {@link Roo.data.Store} which owns the Record.
13095      * Commits all changes made to the Record since either creation, or the last commit operation.
13096      * <p>
13097      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13098      * of commit operations.
13099      */
13100     commit : function(){
13101         this.dirty = false;
13102         delete this.modified;
13103         this.editing = false;
13104         if(this.store){
13105             this.store.afterCommit(this);
13106         }
13107     },
13108
13109     // private
13110     hasError : function(){
13111         return this.error != null;
13112     },
13113
13114     // private
13115     clearError : function(){
13116         this.error = null;
13117     },
13118
13119     /**
13120      * Creates a copy of this record.
13121      * @param {String} id (optional) A new record id if you don't want to use this record's id
13122      * @return {Record}
13123      */
13124     copy : function(newId) {
13125         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13126     }
13127 };/*
13128  * Based on:
13129  * Ext JS Library 1.1.1
13130  * Copyright(c) 2006-2007, Ext JS, LLC.
13131  *
13132  * Originally Released Under LGPL - original licence link has changed is not relivant.
13133  *
13134  * Fork - LGPL
13135  * <script type="text/javascript">
13136  */
13137
13138
13139
13140 /**
13141  * @class Roo.data.Store
13142  * @extends Roo.util.Observable
13143  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13144  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13145  * <p>
13146  * 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
13147  * has no knowledge of the format of the data returned by the Proxy.<br>
13148  * <p>
13149  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13150  * instances from the data object. These records are cached and made available through accessor functions.
13151  * @constructor
13152  * Creates a new Store.
13153  * @param {Object} config A config object containing the objects needed for the Store to access data,
13154  * and read the data into Records.
13155  */
13156 Roo.data.Store = function(config){
13157     this.data = new Roo.util.MixedCollection(false);
13158     this.data.getKey = function(o){
13159         return o.id;
13160     };
13161     this.baseParams = {};
13162     // private
13163     this.paramNames = {
13164         "start" : "start",
13165         "limit" : "limit",
13166         "sort" : "sort",
13167         "dir" : "dir",
13168         "multisort" : "_multisort"
13169     };
13170
13171     if(config && config.data){
13172         this.inlineData = config.data;
13173         delete config.data;
13174     }
13175
13176     Roo.apply(this, config);
13177     
13178     if(this.reader){ // reader passed
13179         this.reader = Roo.factory(this.reader, Roo.data);
13180         this.reader.xmodule = this.xmodule || false;
13181         if(!this.recordType){
13182             this.recordType = this.reader.recordType;
13183         }
13184         if(this.reader.onMetaChange){
13185             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13186         }
13187     }
13188
13189     if(this.recordType){
13190         this.fields = this.recordType.prototype.fields;
13191     }
13192     this.modified = [];
13193
13194     this.addEvents({
13195         /**
13196          * @event datachanged
13197          * Fires when the data cache has changed, and a widget which is using this Store
13198          * as a Record cache should refresh its view.
13199          * @param {Store} this
13200          */
13201         datachanged : true,
13202         /**
13203          * @event metachange
13204          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13205          * @param {Store} this
13206          * @param {Object} meta The JSON metadata
13207          */
13208         metachange : true,
13209         /**
13210          * @event add
13211          * Fires when Records have been added to the Store
13212          * @param {Store} this
13213          * @param {Roo.data.Record[]} records The array of Records added
13214          * @param {Number} index The index at which the record(s) were added
13215          */
13216         add : true,
13217         /**
13218          * @event remove
13219          * Fires when a Record has been removed from the Store
13220          * @param {Store} this
13221          * @param {Roo.data.Record} record The Record that was removed
13222          * @param {Number} index The index at which the record was removed
13223          */
13224         remove : true,
13225         /**
13226          * @event update
13227          * Fires when a Record has been updated
13228          * @param {Store} this
13229          * @param {Roo.data.Record} record The Record that was updated
13230          * @param {String} operation The update operation being performed.  Value may be one of:
13231          * <pre><code>
13232  Roo.data.Record.EDIT
13233  Roo.data.Record.REJECT
13234  Roo.data.Record.COMMIT
13235          * </code></pre>
13236          */
13237         update : true,
13238         /**
13239          * @event clear
13240          * Fires when the data cache has been cleared.
13241          * @param {Store} this
13242          */
13243         clear : true,
13244         /**
13245          * @event beforeload
13246          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13247          * the load action will be canceled.
13248          * @param {Store} this
13249          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13250          */
13251         beforeload : true,
13252         /**
13253          * @event beforeloadadd
13254          * Fires after a new set of Records has been loaded.
13255          * @param {Store} this
13256          * @param {Roo.data.Record[]} records The Records that were loaded
13257          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13258          */
13259         beforeloadadd : true,
13260         /**
13261          * @event load
13262          * Fires after a new set of Records has been loaded, before they are added to the store.
13263          * @param {Store} this
13264          * @param {Roo.data.Record[]} records The Records that were loaded
13265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13266          * @params {Object} return from reader
13267          */
13268         load : true,
13269         /**
13270          * @event loadexception
13271          * Fires if an exception occurs in the Proxy during loading.
13272          * Called with the signature of the Proxy's "loadexception" event.
13273          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13274          * 
13275          * @param {Proxy} 
13276          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13277          * @param {Object} load options 
13278          * @param {Object} jsonData from your request (normally this contains the Exception)
13279          */
13280         loadexception : true
13281     });
13282     
13283     if(this.proxy){
13284         this.proxy = Roo.factory(this.proxy, Roo.data);
13285         this.proxy.xmodule = this.xmodule || false;
13286         this.relayEvents(this.proxy,  ["loadexception"]);
13287     }
13288     this.sortToggle = {};
13289     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13290
13291     Roo.data.Store.superclass.constructor.call(this);
13292
13293     if(this.inlineData){
13294         this.loadData(this.inlineData);
13295         delete this.inlineData;
13296     }
13297 };
13298
13299 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13300      /**
13301     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13302     * without a remote query - used by combo/forms at present.
13303     */
13304     
13305     /**
13306     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13307     */
13308     /**
13309     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13310     */
13311     /**
13312     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13313     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13314     */
13315     /**
13316     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13317     * on any HTTP request
13318     */
13319     /**
13320     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13321     */
13322     /**
13323     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13324     */
13325     multiSort: false,
13326     /**
13327     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13328     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13329     */
13330     remoteSort : false,
13331
13332     /**
13333     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13334      * loaded or when a record is removed. (defaults to false).
13335     */
13336     pruneModifiedRecords : false,
13337
13338     // private
13339     lastOptions : null,
13340
13341     /**
13342      * Add Records to the Store and fires the add event.
13343      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13344      */
13345     add : function(records){
13346         records = [].concat(records);
13347         for(var i = 0, len = records.length; i < len; i++){
13348             records[i].join(this);
13349         }
13350         var index = this.data.length;
13351         this.data.addAll(records);
13352         this.fireEvent("add", this, records, index);
13353     },
13354
13355     /**
13356      * Remove a Record from the Store and fires the remove event.
13357      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13358      */
13359     remove : function(record){
13360         var index = this.data.indexOf(record);
13361         this.data.removeAt(index);
13362  
13363         if(this.pruneModifiedRecords){
13364             this.modified.remove(record);
13365         }
13366         this.fireEvent("remove", this, record, index);
13367     },
13368
13369     /**
13370      * Remove all Records from the Store and fires the clear event.
13371      */
13372     removeAll : function(){
13373         this.data.clear();
13374         if(this.pruneModifiedRecords){
13375             this.modified = [];
13376         }
13377         this.fireEvent("clear", this);
13378     },
13379
13380     /**
13381      * Inserts Records to the Store at the given index and fires the add event.
13382      * @param {Number} index The start index at which to insert the passed Records.
13383      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13384      */
13385     insert : function(index, records){
13386         records = [].concat(records);
13387         for(var i = 0, len = records.length; i < len; i++){
13388             this.data.insert(index, records[i]);
13389             records[i].join(this);
13390         }
13391         this.fireEvent("add", this, records, index);
13392     },
13393
13394     /**
13395      * Get the index within the cache of the passed Record.
13396      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13397      * @return {Number} The index of the passed Record. Returns -1 if not found.
13398      */
13399     indexOf : function(record){
13400         return this.data.indexOf(record);
13401     },
13402
13403     /**
13404      * Get the index within the cache of the Record with the passed id.
13405      * @param {String} id The id of the Record to find.
13406      * @return {Number} The index of the Record. Returns -1 if not found.
13407      */
13408     indexOfId : function(id){
13409         return this.data.indexOfKey(id);
13410     },
13411
13412     /**
13413      * Get the Record with the specified id.
13414      * @param {String} id The id of the Record to find.
13415      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13416      */
13417     getById : function(id){
13418         return this.data.key(id);
13419     },
13420
13421     /**
13422      * Get the Record at the specified index.
13423      * @param {Number} index The index of the Record to find.
13424      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13425      */
13426     getAt : function(index){
13427         return this.data.itemAt(index);
13428     },
13429
13430     /**
13431      * Returns a range of Records between specified indices.
13432      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13433      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13434      * @return {Roo.data.Record[]} An array of Records
13435      */
13436     getRange : function(start, end){
13437         return this.data.getRange(start, end);
13438     },
13439
13440     // private
13441     storeOptions : function(o){
13442         o = Roo.apply({}, o);
13443         delete o.callback;
13444         delete o.scope;
13445         this.lastOptions = o;
13446     },
13447
13448     /**
13449      * Loads the Record cache from the configured Proxy using the configured Reader.
13450      * <p>
13451      * If using remote paging, then the first load call must specify the <em>start</em>
13452      * and <em>limit</em> properties in the options.params property to establish the initial
13453      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13454      * <p>
13455      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13456      * and this call will return before the new data has been loaded. Perform any post-processing
13457      * in a callback function, or in a "load" event handler.</strong>
13458      * <p>
13459      * @param {Object} options An object containing properties which control loading options:<ul>
13460      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13461      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13462      * passed the following arguments:<ul>
13463      * <li>r : Roo.data.Record[]</li>
13464      * <li>options: Options object from the load call</li>
13465      * <li>success: Boolean success indicator</li></ul></li>
13466      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13467      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13468      * </ul>
13469      */
13470     load : function(options){
13471         options = options || {};
13472         if(this.fireEvent("beforeload", this, options) !== false){
13473             this.storeOptions(options);
13474             var p = Roo.apply(options.params || {}, this.baseParams);
13475             // if meta was not loaded from remote source.. try requesting it.
13476             if (!this.reader.metaFromRemote) {
13477                 p._requestMeta = 1;
13478             }
13479             if(this.sortInfo && this.remoteSort){
13480                 var pn = this.paramNames;
13481                 p[pn["sort"]] = this.sortInfo.field;
13482                 p[pn["dir"]] = this.sortInfo.direction;
13483             }
13484             if (this.multiSort) {
13485                 var pn = this.paramNames;
13486                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13487             }
13488             
13489             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13490         }
13491     },
13492
13493     /**
13494      * Reloads the Record cache from the configured Proxy using the configured Reader and
13495      * the options from the last load operation performed.
13496      * @param {Object} options (optional) An object containing properties which may override the options
13497      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13498      * the most recently used options are reused).
13499      */
13500     reload : function(options){
13501         this.load(Roo.applyIf(options||{}, this.lastOptions));
13502     },
13503
13504     // private
13505     // Called as a callback by the Reader during a load operation.
13506     loadRecords : function(o, options, success){
13507         if(!o || success === false){
13508             if(success !== false){
13509                 this.fireEvent("load", this, [], options, o);
13510             }
13511             if(options.callback){
13512                 options.callback.call(options.scope || this, [], options, false);
13513             }
13514             return;
13515         }
13516         // if data returned failure - throw an exception.
13517         if (o.success === false) {
13518             // show a message if no listener is registered.
13519             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13520                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13521             }
13522             // loadmask wil be hooked into this..
13523             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13524             return;
13525         }
13526         var r = o.records, t = o.totalRecords || r.length;
13527         
13528         this.fireEvent("beforeloadadd", this, r, options, o);
13529         
13530         if(!options || options.add !== true){
13531             if(this.pruneModifiedRecords){
13532                 this.modified = [];
13533             }
13534             for(var i = 0, len = r.length; i < len; i++){
13535                 r[i].join(this);
13536             }
13537             if(this.snapshot){
13538                 this.data = this.snapshot;
13539                 delete this.snapshot;
13540             }
13541             this.data.clear();
13542             this.data.addAll(r);
13543             this.totalLength = t;
13544             this.applySort();
13545             this.fireEvent("datachanged", this);
13546         }else{
13547             this.totalLength = Math.max(t, this.data.length+r.length);
13548             this.add(r);
13549         }
13550         
13551         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13552                 
13553             var e = new Roo.data.Record({});
13554
13555             e.set(this.parent.displayField, this.parent.emptyTitle);
13556             e.set(this.parent.valueField, '');
13557
13558             this.insert(0, e);
13559         }
13560             
13561         this.fireEvent("load", this, r, options, o);
13562         if(options.callback){
13563             options.callback.call(options.scope || this, r, options, true);
13564         }
13565     },
13566
13567
13568     /**
13569      * Loads data from a passed data block. A Reader which understands the format of the data
13570      * must have been configured in the constructor.
13571      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13572      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13573      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13574      */
13575     loadData : function(o, append){
13576         var r = this.reader.readRecords(o);
13577         this.loadRecords(r, {add: append}, true);
13578     },
13579     
13580      /**
13581      * using 'cn' the nested child reader read the child array into it's child stores.
13582      * @param {Object} rec The record with a 'children array
13583      */
13584     loadDataFromChildren : function(rec)
13585     {
13586         this.loadData(this.reader.toLoadData(rec));
13587     },
13588     
13589
13590     /**
13591      * Gets the number of cached records.
13592      * <p>
13593      * <em>If using paging, this may not be the total size of the dataset. If the data object
13594      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13595      * the data set size</em>
13596      */
13597     getCount : function(){
13598         return this.data.length || 0;
13599     },
13600
13601     /**
13602      * Gets the total number of records in the dataset as returned by the server.
13603      * <p>
13604      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13605      * the dataset size</em>
13606      */
13607     getTotalCount : function(){
13608         return this.totalLength || 0;
13609     },
13610
13611     /**
13612      * Returns the sort state of the Store as an object with two properties:
13613      * <pre><code>
13614  field {String} The name of the field by which the Records are sorted
13615  direction {String} The sort order, "ASC" or "DESC"
13616      * </code></pre>
13617      */
13618     getSortState : function(){
13619         return this.sortInfo;
13620     },
13621
13622     // private
13623     applySort : function(){
13624         if(this.sortInfo && !this.remoteSort){
13625             var s = this.sortInfo, f = s.field;
13626             var st = this.fields.get(f).sortType;
13627             var fn = function(r1, r2){
13628                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13629                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13630             };
13631             this.data.sort(s.direction, fn);
13632             if(this.snapshot && this.snapshot != this.data){
13633                 this.snapshot.sort(s.direction, fn);
13634             }
13635         }
13636     },
13637
13638     /**
13639      * Sets the default sort column and order to be used by the next load operation.
13640      * @param {String} fieldName The name of the field to sort by.
13641      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13642      */
13643     setDefaultSort : function(field, dir){
13644         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13645     },
13646
13647     /**
13648      * Sort the Records.
13649      * If remote sorting is used, the sort is performed on the server, and the cache is
13650      * reloaded. If local sorting is used, the cache is sorted internally.
13651      * @param {String} fieldName The name of the field to sort by.
13652      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13653      */
13654     sort : function(fieldName, dir){
13655         var f = this.fields.get(fieldName);
13656         if(!dir){
13657             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13658             
13659             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13660                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13661             }else{
13662                 dir = f.sortDir;
13663             }
13664         }
13665         this.sortToggle[f.name] = dir;
13666         this.sortInfo = {field: f.name, direction: dir};
13667         if(!this.remoteSort){
13668             this.applySort();
13669             this.fireEvent("datachanged", this);
13670         }else{
13671             this.load(this.lastOptions);
13672         }
13673     },
13674
13675     /**
13676      * Calls the specified function for each of the Records in the cache.
13677      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13678      * Returning <em>false</em> aborts and exits the iteration.
13679      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13680      */
13681     each : function(fn, scope){
13682         this.data.each(fn, scope);
13683     },
13684
13685     /**
13686      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13687      * (e.g., during paging).
13688      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13689      */
13690     getModifiedRecords : function(){
13691         return this.modified;
13692     },
13693
13694     // private
13695     createFilterFn : function(property, value, anyMatch){
13696         if(!value.exec){ // not a regex
13697             value = String(value);
13698             if(value.length == 0){
13699                 return false;
13700             }
13701             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13702         }
13703         return function(r){
13704             return value.test(r.data[property]);
13705         };
13706     },
13707
13708     /**
13709      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13710      * @param {String} property A field on your records
13711      * @param {Number} start The record index to start at (defaults to 0)
13712      * @param {Number} end The last record index to include (defaults to length - 1)
13713      * @return {Number} The sum
13714      */
13715     sum : function(property, start, end){
13716         var rs = this.data.items, v = 0;
13717         start = start || 0;
13718         end = (end || end === 0) ? end : rs.length-1;
13719
13720         for(var i = start; i <= end; i++){
13721             v += (rs[i].data[property] || 0);
13722         }
13723         return v;
13724     },
13725
13726     /**
13727      * Filter the records by a specified property.
13728      * @param {String} field A field on your records
13729      * @param {String/RegExp} value Either a string that the field
13730      * should start with or a RegExp to test against the field
13731      * @param {Boolean} anyMatch True to match any part not just the beginning
13732      */
13733     filter : function(property, value, anyMatch){
13734         var fn = this.createFilterFn(property, value, anyMatch);
13735         return fn ? this.filterBy(fn) : this.clearFilter();
13736     },
13737
13738     /**
13739      * Filter by a function. The specified function will be called with each
13740      * record in this data source. If the function returns true the record is included,
13741      * otherwise it is filtered.
13742      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13743      * @param {Object} scope (optional) The scope of the function (defaults to this)
13744      */
13745     filterBy : function(fn, scope){
13746         this.snapshot = this.snapshot || this.data;
13747         this.data = this.queryBy(fn, scope||this);
13748         this.fireEvent("datachanged", this);
13749     },
13750
13751     /**
13752      * Query the records by a specified property.
13753      * @param {String} field A field on your records
13754      * @param {String/RegExp} value Either a string that the field
13755      * should start with or a RegExp to test against the field
13756      * @param {Boolean} anyMatch True to match any part not just the beginning
13757      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13758      */
13759     query : function(property, value, anyMatch){
13760         var fn = this.createFilterFn(property, value, anyMatch);
13761         return fn ? this.queryBy(fn) : this.data.clone();
13762     },
13763
13764     /**
13765      * Query by a function. The specified function will be called with each
13766      * record in this data source. If the function returns true the record is included
13767      * in the results.
13768      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13769      * @param {Object} scope (optional) The scope of the function (defaults to this)
13770       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13771      **/
13772     queryBy : function(fn, scope){
13773         var data = this.snapshot || this.data;
13774         return data.filterBy(fn, scope||this);
13775     },
13776
13777     /**
13778      * Collects unique values for a particular dataIndex from this store.
13779      * @param {String} dataIndex The property to collect
13780      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13781      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13782      * @return {Array} An array of the unique values
13783      **/
13784     collect : function(dataIndex, allowNull, bypassFilter){
13785         var d = (bypassFilter === true && this.snapshot) ?
13786                 this.snapshot.items : this.data.items;
13787         var v, sv, r = [], l = {};
13788         for(var i = 0, len = d.length; i < len; i++){
13789             v = d[i].data[dataIndex];
13790             sv = String(v);
13791             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13792                 l[sv] = true;
13793                 r[r.length] = v;
13794             }
13795         }
13796         return r;
13797     },
13798
13799     /**
13800      * Revert to a view of the Record cache with no filtering applied.
13801      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13802      */
13803     clearFilter : function(suppressEvent){
13804         if(this.snapshot && this.snapshot != this.data){
13805             this.data = this.snapshot;
13806             delete this.snapshot;
13807             if(suppressEvent !== true){
13808                 this.fireEvent("datachanged", this);
13809             }
13810         }
13811     },
13812
13813     // private
13814     afterEdit : function(record){
13815         if(this.modified.indexOf(record) == -1){
13816             this.modified.push(record);
13817         }
13818         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13819     },
13820     
13821     // private
13822     afterReject : function(record){
13823         this.modified.remove(record);
13824         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13825     },
13826
13827     // private
13828     afterCommit : function(record){
13829         this.modified.remove(record);
13830         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13831     },
13832
13833     /**
13834      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13835      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13836      */
13837     commitChanges : function(){
13838         var m = this.modified.slice(0);
13839         this.modified = [];
13840         for(var i = 0, len = m.length; i < len; i++){
13841             m[i].commit();
13842         }
13843     },
13844
13845     /**
13846      * Cancel outstanding changes on all changed records.
13847      */
13848     rejectChanges : function(){
13849         var m = this.modified.slice(0);
13850         this.modified = [];
13851         for(var i = 0, len = m.length; i < len; i++){
13852             m[i].reject();
13853         }
13854     },
13855
13856     onMetaChange : function(meta, rtype, o){
13857         this.recordType = rtype;
13858         this.fields = rtype.prototype.fields;
13859         delete this.snapshot;
13860         this.sortInfo = meta.sortInfo || this.sortInfo;
13861         this.modified = [];
13862         this.fireEvent('metachange', this, this.reader.meta);
13863     },
13864     
13865     moveIndex : function(data, type)
13866     {
13867         var index = this.indexOf(data);
13868         
13869         var newIndex = index + type;
13870         
13871         this.remove(data);
13872         
13873         this.insert(newIndex, data);
13874         
13875     }
13876 });/*
13877  * Based on:
13878  * Ext JS Library 1.1.1
13879  * Copyright(c) 2006-2007, Ext JS, LLC.
13880  *
13881  * Originally Released Under LGPL - original licence link has changed is not relivant.
13882  *
13883  * Fork - LGPL
13884  * <script type="text/javascript">
13885  */
13886
13887 /**
13888  * @class Roo.data.SimpleStore
13889  * @extends Roo.data.Store
13890  * Small helper class to make creating Stores from Array data easier.
13891  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13892  * @cfg {Array} fields An array of field definition objects, or field name strings.
13893  * @cfg {Object} an existing reader (eg. copied from another store)
13894  * @cfg {Array} data The multi-dimensional array of data
13895  * @constructor
13896  * @param {Object} config
13897  */
13898 Roo.data.SimpleStore = function(config)
13899 {
13900     Roo.data.SimpleStore.superclass.constructor.call(this, {
13901         isLocal : true,
13902         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13903                 id: config.id
13904             },
13905             Roo.data.Record.create(config.fields)
13906         ),
13907         proxy : new Roo.data.MemoryProxy(config.data)
13908     });
13909     this.load();
13910 };
13911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13912  * Based on:
13913  * Ext JS Library 1.1.1
13914  * Copyright(c) 2006-2007, Ext JS, LLC.
13915  *
13916  * Originally Released Under LGPL - original licence link has changed is not relivant.
13917  *
13918  * Fork - LGPL
13919  * <script type="text/javascript">
13920  */
13921
13922 /**
13923 /**
13924  * @extends Roo.data.Store
13925  * @class Roo.data.JsonStore
13926  * Small helper class to make creating Stores for JSON data easier. <br/>
13927 <pre><code>
13928 var store = new Roo.data.JsonStore({
13929     url: 'get-images.php',
13930     root: 'images',
13931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13932 });
13933 </code></pre>
13934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13935  * JsonReader and HttpProxy (unless inline data is provided).</b>
13936  * @cfg {Array} fields An array of field definition objects, or field name strings.
13937  * @constructor
13938  * @param {Object} config
13939  */
13940 Roo.data.JsonStore = function(c){
13941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13943         reader: new Roo.data.JsonReader(c, c.fields)
13944     }));
13945 };
13946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13947  * Based on:
13948  * Ext JS Library 1.1.1
13949  * Copyright(c) 2006-2007, Ext JS, LLC.
13950  *
13951  * Originally Released Under LGPL - original licence link has changed is not relivant.
13952  *
13953  * Fork - LGPL
13954  * <script type="text/javascript">
13955  */
13956
13957  
13958 Roo.data.Field = function(config){
13959     if(typeof config == "string"){
13960         config = {name: config};
13961     }
13962     Roo.apply(this, config);
13963     
13964     if(!this.type){
13965         this.type = "auto";
13966     }
13967     
13968     var st = Roo.data.SortTypes;
13969     // named sortTypes are supported, here we look them up
13970     if(typeof this.sortType == "string"){
13971         this.sortType = st[this.sortType];
13972     }
13973     
13974     // set default sortType for strings and dates
13975     if(!this.sortType){
13976         switch(this.type){
13977             case "string":
13978                 this.sortType = st.asUCString;
13979                 break;
13980             case "date":
13981                 this.sortType = st.asDate;
13982                 break;
13983             default:
13984                 this.sortType = st.none;
13985         }
13986     }
13987
13988     // define once
13989     var stripRe = /[\$,%]/g;
13990
13991     // prebuilt conversion function for this field, instead of
13992     // switching every time we're reading a value
13993     if(!this.convert){
13994         var cv, dateFormat = this.dateFormat;
13995         switch(this.type){
13996             case "":
13997             case "auto":
13998             case undefined:
13999                 cv = function(v){ return v; };
14000                 break;
14001             case "string":
14002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14003                 break;
14004             case "int":
14005                 cv = function(v){
14006                     return v !== undefined && v !== null && v !== '' ?
14007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14008                     };
14009                 break;
14010             case "float":
14011                 cv = function(v){
14012                     return v !== undefined && v !== null && v !== '' ?
14013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14014                     };
14015                 break;
14016             case "bool":
14017             case "boolean":
14018                 cv = function(v){ return v === true || v === "true" || v == 1; };
14019                 break;
14020             case "date":
14021                 cv = function(v){
14022                     if(!v){
14023                         return '';
14024                     }
14025                     if(v instanceof Date){
14026                         return v;
14027                     }
14028                     if(dateFormat){
14029                         if(dateFormat == "timestamp"){
14030                             return new Date(v*1000);
14031                         }
14032                         return Date.parseDate(v, dateFormat);
14033                     }
14034                     var parsed = Date.parse(v);
14035                     return parsed ? new Date(parsed) : null;
14036                 };
14037              break;
14038             
14039         }
14040         this.convert = cv;
14041     }
14042 };
14043
14044 Roo.data.Field.prototype = {
14045     dateFormat: null,
14046     defaultValue: "",
14047     mapping: null,
14048     sortType : null,
14049     sortDir : "ASC"
14050 };/*
14051  * Based on:
14052  * Ext JS Library 1.1.1
14053  * Copyright(c) 2006-2007, Ext JS, LLC.
14054  *
14055  * Originally Released Under LGPL - original licence link has changed is not relivant.
14056  *
14057  * Fork - LGPL
14058  * <script type="text/javascript">
14059  */
14060  
14061 // Base class for reading structured data from a data source.  This class is intended to be
14062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14063
14064 /**
14065  * @class Roo.data.DataReader
14066  * Base class for reading structured data from a data source.  This class is intended to be
14067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14068  */
14069
14070 Roo.data.DataReader = function(meta, recordType){
14071     
14072     this.meta = meta;
14073     
14074     this.recordType = recordType instanceof Array ? 
14075         Roo.data.Record.create(recordType) : recordType;
14076 };
14077
14078 Roo.data.DataReader.prototype = {
14079     
14080     
14081     readerType : 'Data',
14082      /**
14083      * Create an empty record
14084      * @param {Object} data (optional) - overlay some values
14085      * @return {Roo.data.Record} record created.
14086      */
14087     newRow :  function(d) {
14088         var da =  {};
14089         this.recordType.prototype.fields.each(function(c) {
14090             switch( c.type) {
14091                 case 'int' : da[c.name] = 0; break;
14092                 case 'date' : da[c.name] = new Date(); break;
14093                 case 'float' : da[c.name] = 0.0; break;
14094                 case 'boolean' : da[c.name] = false; break;
14095                 default : da[c.name] = ""; break;
14096             }
14097             
14098         });
14099         return new this.recordType(Roo.apply(da, d));
14100     }
14101     
14102     
14103 };/*
14104  * Based on:
14105  * Ext JS Library 1.1.1
14106  * Copyright(c) 2006-2007, Ext JS, LLC.
14107  *
14108  * Originally Released Under LGPL - original licence link has changed is not relivant.
14109  *
14110  * Fork - LGPL
14111  * <script type="text/javascript">
14112  */
14113
14114 /**
14115  * @class Roo.data.DataProxy
14116  * @extends Roo.data.Observable
14117  * This class is an abstract base class for implementations which provide retrieval of
14118  * unformatted data objects.<br>
14119  * <p>
14120  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14121  * (of the appropriate type which knows how to parse the data object) to provide a block of
14122  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14123  * <p>
14124  * Custom implementations must implement the load method as described in
14125  * {@link Roo.data.HttpProxy#load}.
14126  */
14127 Roo.data.DataProxy = function(){
14128     this.addEvents({
14129         /**
14130          * @event beforeload
14131          * Fires before a network request is made to retrieve a data object.
14132          * @param {Object} This DataProxy object.
14133          * @param {Object} params The params parameter to the load function.
14134          */
14135         beforeload : true,
14136         /**
14137          * @event load
14138          * Fires before the load method's callback is called.
14139          * @param {Object} This DataProxy object.
14140          * @param {Object} o The data object.
14141          * @param {Object} arg The callback argument object passed to the load function.
14142          */
14143         load : true,
14144         /**
14145          * @event loadexception
14146          * Fires if an Exception occurs during data retrieval.
14147          * @param {Object} This DataProxy object.
14148          * @param {Object} o The data object.
14149          * @param {Object} arg The callback argument object passed to the load function.
14150          * @param {Object} e The Exception.
14151          */
14152         loadexception : true
14153     });
14154     Roo.data.DataProxy.superclass.constructor.call(this);
14155 };
14156
14157 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14158
14159     /**
14160      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14161      */
14162 /*
14163  * Based on:
14164  * Ext JS Library 1.1.1
14165  * Copyright(c) 2006-2007, Ext JS, LLC.
14166  *
14167  * Originally Released Under LGPL - original licence link has changed is not relivant.
14168  *
14169  * Fork - LGPL
14170  * <script type="text/javascript">
14171  */
14172 /**
14173  * @class Roo.data.MemoryProxy
14174  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14175  * to the Reader when its load method is called.
14176  * @constructor
14177  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14178  */
14179 Roo.data.MemoryProxy = function(data){
14180     if (data.data) {
14181         data = data.data;
14182     }
14183     Roo.data.MemoryProxy.superclass.constructor.call(this);
14184     this.data = data;
14185 };
14186
14187 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14188     
14189     /**
14190      * Load data from the requested source (in this case an in-memory
14191      * data object passed to the constructor), read the data object into
14192      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14193      * process that block using the passed callback.
14194      * @param {Object} params This parameter is not used by the MemoryProxy class.
14195      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14196      * object into a block of Roo.data.Records.
14197      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14198      * The function must be passed <ul>
14199      * <li>The Record block object</li>
14200      * <li>The "arg" argument from the load function</li>
14201      * <li>A boolean success indicator</li>
14202      * </ul>
14203      * @param {Object} scope The scope in which to call the callback
14204      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14205      */
14206     load : function(params, reader, callback, scope, arg){
14207         params = params || {};
14208         var result;
14209         try {
14210             result = reader.readRecords(params.data ? params.data :this.data);
14211         }catch(e){
14212             this.fireEvent("loadexception", this, arg, null, e);
14213             callback.call(scope, null, arg, false);
14214             return;
14215         }
14216         callback.call(scope, result, arg, true);
14217     },
14218     
14219     // private
14220     update : function(params, records){
14221         
14222     }
14223 });/*
14224  * Based on:
14225  * Ext JS Library 1.1.1
14226  * Copyright(c) 2006-2007, Ext JS, LLC.
14227  *
14228  * Originally Released Under LGPL - original licence link has changed is not relivant.
14229  *
14230  * Fork - LGPL
14231  * <script type="text/javascript">
14232  */
14233 /**
14234  * @class Roo.data.HttpProxy
14235  * @extends Roo.data.DataProxy
14236  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14237  * configured to reference a certain URL.<br><br>
14238  * <p>
14239  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14240  * from which the running page was served.<br><br>
14241  * <p>
14242  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14243  * <p>
14244  * Be aware that to enable the browser to parse an XML document, the server must set
14245  * the Content-Type header in the HTTP response to "text/xml".
14246  * @constructor
14247  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14248  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14249  * will be used to make the request.
14250  */
14251 Roo.data.HttpProxy = function(conn){
14252     Roo.data.HttpProxy.superclass.constructor.call(this);
14253     // is conn a conn config or a real conn?
14254     this.conn = conn;
14255     this.useAjax = !conn || !conn.events;
14256   
14257 };
14258
14259 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14260     // thse are take from connection...
14261     
14262     /**
14263      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14264      */
14265     /**
14266      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14267      * extra parameters to each request made by this object. (defaults to undefined)
14268      */
14269     /**
14270      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14271      *  to each request made by this object. (defaults to undefined)
14272      */
14273     /**
14274      * @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)
14275      */
14276     /**
14277      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14278      */
14279      /**
14280      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14281      * @type Boolean
14282      */
14283   
14284
14285     /**
14286      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14287      * @type Boolean
14288      */
14289     /**
14290      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14291      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14292      * a finer-grained basis than the DataProxy events.
14293      */
14294     getConnection : function(){
14295         return this.useAjax ? Roo.Ajax : this.conn;
14296     },
14297
14298     /**
14299      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14300      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14301      * process that block using the passed callback.
14302      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14303      * for the request to the remote server.
14304      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14305      * object into a block of Roo.data.Records.
14306      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14307      * The function must be passed <ul>
14308      * <li>The Record block object</li>
14309      * <li>The "arg" argument from the load function</li>
14310      * <li>A boolean success indicator</li>
14311      * </ul>
14312      * @param {Object} scope The scope in which to call the callback
14313      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14314      */
14315     load : function(params, reader, callback, scope, arg){
14316         if(this.fireEvent("beforeload", this, params) !== false){
14317             var  o = {
14318                 params : params || {},
14319                 request: {
14320                     callback : callback,
14321                     scope : scope,
14322                     arg : arg
14323                 },
14324                 reader: reader,
14325                 callback : this.loadResponse,
14326                 scope: this
14327             };
14328             if(this.useAjax){
14329                 Roo.applyIf(o, this.conn);
14330                 if(this.activeRequest){
14331                     Roo.Ajax.abort(this.activeRequest);
14332                 }
14333                 this.activeRequest = Roo.Ajax.request(o);
14334             }else{
14335                 this.conn.request(o);
14336             }
14337         }else{
14338             callback.call(scope||this, null, arg, false);
14339         }
14340     },
14341
14342     // private
14343     loadResponse : function(o, success, response){
14344         delete this.activeRequest;
14345         if(!success){
14346             this.fireEvent("loadexception", this, o, response);
14347             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14348             return;
14349         }
14350         var result;
14351         try {
14352             result = o.reader.read(response);
14353         }catch(e){
14354             this.fireEvent("loadexception", this, o, response, e);
14355             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14356             return;
14357         }
14358         
14359         this.fireEvent("load", this, o, o.request.arg);
14360         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14361     },
14362
14363     // private
14364     update : function(dataSet){
14365
14366     },
14367
14368     // private
14369     updateResponse : function(dataSet){
14370
14371     }
14372 });/*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382
14383 /**
14384  * @class Roo.data.ScriptTagProxy
14385  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14386  * other than the originating domain of the running page.<br><br>
14387  * <p>
14388  * <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
14389  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14390  * <p>
14391  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14392  * source code that is used as the source inside a &lt;script> tag.<br><br>
14393  * <p>
14394  * In order for the browser to process the returned data, the server must wrap the data object
14395  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14396  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14397  * depending on whether the callback name was passed:
14398  * <p>
14399  * <pre><code>
14400 boolean scriptTag = false;
14401 String cb = request.getParameter("callback");
14402 if (cb != null) {
14403     scriptTag = true;
14404     response.setContentType("text/javascript");
14405 } else {
14406     response.setContentType("application/x-json");
14407 }
14408 Writer out = response.getWriter();
14409 if (scriptTag) {
14410     out.write(cb + "(");
14411 }
14412 out.print(dataBlock.toJsonString());
14413 if (scriptTag) {
14414     out.write(");");
14415 }
14416 </pre></code>
14417  *
14418  * @constructor
14419  * @param {Object} config A configuration object.
14420  */
14421 Roo.data.ScriptTagProxy = function(config){
14422     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14423     Roo.apply(this, config);
14424     this.head = document.getElementsByTagName("head")[0];
14425 };
14426
14427 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14428
14429 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14430     /**
14431      * @cfg {String} url The URL from which to request the data object.
14432      */
14433     /**
14434      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14435      */
14436     timeout : 30000,
14437     /**
14438      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14439      * the server the name of the callback function set up by the load call to process the returned data object.
14440      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14441      * javascript output which calls this named function passing the data object as its only parameter.
14442      */
14443     callbackParam : "callback",
14444     /**
14445      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14446      * name to the request.
14447      */
14448     nocache : true,
14449
14450     /**
14451      * Load data from the configured URL, read the data object into
14452      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14453      * process that block using the passed callback.
14454      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14455      * for the request to the remote server.
14456      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14457      * object into a block of Roo.data.Records.
14458      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14459      * The function must be passed <ul>
14460      * <li>The Record block object</li>
14461      * <li>The "arg" argument from the load function</li>
14462      * <li>A boolean success indicator</li>
14463      * </ul>
14464      * @param {Object} scope The scope in which to call the callback
14465      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14466      */
14467     load : function(params, reader, callback, scope, arg){
14468         if(this.fireEvent("beforeload", this, params) !== false){
14469
14470             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14471
14472             var url = this.url;
14473             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14474             if(this.nocache){
14475                 url += "&_dc=" + (new Date().getTime());
14476             }
14477             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14478             var trans = {
14479                 id : transId,
14480                 cb : "stcCallback"+transId,
14481                 scriptId : "stcScript"+transId,
14482                 params : params,
14483                 arg : arg,
14484                 url : url,
14485                 callback : callback,
14486                 scope : scope,
14487                 reader : reader
14488             };
14489             var conn = this;
14490
14491             window[trans.cb] = function(o){
14492                 conn.handleResponse(o, trans);
14493             };
14494
14495             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14496
14497             if(this.autoAbort !== false){
14498                 this.abort();
14499             }
14500
14501             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14502
14503             var script = document.createElement("script");
14504             script.setAttribute("src", url);
14505             script.setAttribute("type", "text/javascript");
14506             script.setAttribute("id", trans.scriptId);
14507             this.head.appendChild(script);
14508
14509             this.trans = trans;
14510         }else{
14511             callback.call(scope||this, null, arg, false);
14512         }
14513     },
14514
14515     // private
14516     isLoading : function(){
14517         return this.trans ? true : false;
14518     },
14519
14520     /**
14521      * Abort the current server request.
14522      */
14523     abort : function(){
14524         if(this.isLoading()){
14525             this.destroyTrans(this.trans);
14526         }
14527     },
14528
14529     // private
14530     destroyTrans : function(trans, isLoaded){
14531         this.head.removeChild(document.getElementById(trans.scriptId));
14532         clearTimeout(trans.timeoutId);
14533         if(isLoaded){
14534             window[trans.cb] = undefined;
14535             try{
14536                 delete window[trans.cb];
14537             }catch(e){}
14538         }else{
14539             // if hasn't been loaded, wait for load to remove it to prevent script error
14540             window[trans.cb] = function(){
14541                 window[trans.cb] = undefined;
14542                 try{
14543                     delete window[trans.cb];
14544                 }catch(e){}
14545             };
14546         }
14547     },
14548
14549     // private
14550     handleResponse : function(o, trans){
14551         this.trans = false;
14552         this.destroyTrans(trans, true);
14553         var result;
14554         try {
14555             result = trans.reader.readRecords(o);
14556         }catch(e){
14557             this.fireEvent("loadexception", this, o, trans.arg, e);
14558             trans.callback.call(trans.scope||window, null, trans.arg, false);
14559             return;
14560         }
14561         this.fireEvent("load", this, o, trans.arg);
14562         trans.callback.call(trans.scope||window, result, trans.arg, true);
14563     },
14564
14565     // private
14566     handleFailure : function(trans){
14567         this.trans = false;
14568         this.destroyTrans(trans, false);
14569         this.fireEvent("loadexception", this, null, trans.arg);
14570         trans.callback.call(trans.scope||window, null, trans.arg, false);
14571     }
14572 });/*
14573  * Based on:
14574  * Ext JS Library 1.1.1
14575  * Copyright(c) 2006-2007, Ext JS, LLC.
14576  *
14577  * Originally Released Under LGPL - original licence link has changed is not relivant.
14578  *
14579  * Fork - LGPL
14580  * <script type="text/javascript">
14581  */
14582
14583 /**
14584  * @class Roo.data.JsonReader
14585  * @extends Roo.data.DataReader
14586  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14587  * based on mappings in a provided Roo.data.Record constructor.
14588  * 
14589  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14590  * in the reply previously. 
14591  * 
14592  * <p>
14593  * Example code:
14594  * <pre><code>
14595 var RecordDef = Roo.data.Record.create([
14596     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14597     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14598 ]);
14599 var myReader = new Roo.data.JsonReader({
14600     totalProperty: "results",    // The property which contains the total dataset size (optional)
14601     root: "rows",                // The property which contains an Array of row objects
14602     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14603 }, RecordDef);
14604 </code></pre>
14605  * <p>
14606  * This would consume a JSON file like this:
14607  * <pre><code>
14608 { 'results': 2, 'rows': [
14609     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14610     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14611 }
14612 </code></pre>
14613  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14614  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14615  * paged from the remote server.
14616  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14617  * @cfg {String} root name of the property which contains the Array of row objects.
14618  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14619  * @cfg {Array} fields Array of field definition objects
14620  * @constructor
14621  * Create a new JsonReader
14622  * @param {Object} meta Metadata configuration options
14623  * @param {Object} recordType Either an Array of field definition objects,
14624  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14625  */
14626 Roo.data.JsonReader = function(meta, recordType){
14627     
14628     meta = meta || {};
14629     // set some defaults:
14630     Roo.applyIf(meta, {
14631         totalProperty: 'total',
14632         successProperty : 'success',
14633         root : 'data',
14634         id : 'id'
14635     });
14636     
14637     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14638 };
14639 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14640     
14641     readerType : 'Json',
14642     
14643     /**
14644      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14645      * Used by Store query builder to append _requestMeta to params.
14646      * 
14647      */
14648     metaFromRemote : false,
14649     /**
14650      * This method is only used by a DataProxy which has retrieved data from a remote server.
14651      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14652      * @return {Object} data A data block which is used by an Roo.data.Store object as
14653      * a cache of Roo.data.Records.
14654      */
14655     read : function(response){
14656         var json = response.responseText;
14657        
14658         var o = /* eval:var:o */ eval("("+json+")");
14659         if(!o) {
14660             throw {message: "JsonReader.read: Json object not found"};
14661         }
14662         
14663         if(o.metaData){
14664             
14665             delete this.ef;
14666             this.metaFromRemote = true;
14667             this.meta = o.metaData;
14668             this.recordType = Roo.data.Record.create(o.metaData.fields);
14669             this.onMetaChange(this.meta, this.recordType, o);
14670         }
14671         return this.readRecords(o);
14672     },
14673
14674     // private function a store will implement
14675     onMetaChange : function(meta, recordType, o){
14676
14677     },
14678
14679     /**
14680          * @ignore
14681          */
14682     simpleAccess: function(obj, subsc) {
14683         return obj[subsc];
14684     },
14685
14686         /**
14687          * @ignore
14688          */
14689     getJsonAccessor: function(){
14690         var re = /[\[\.]/;
14691         return function(expr) {
14692             try {
14693                 return(re.test(expr))
14694                     ? new Function("obj", "return obj." + expr)
14695                     : function(obj){
14696                         return obj[expr];
14697                     };
14698             } catch(e){}
14699             return Roo.emptyFn;
14700         };
14701     }(),
14702
14703     /**
14704      * Create a data block containing Roo.data.Records from an XML document.
14705      * @param {Object} o An object which contains an Array of row objects in the property specified
14706      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14707      * which contains the total size of the dataset.
14708      * @return {Object} data A data block which is used by an Roo.data.Store object as
14709      * a cache of Roo.data.Records.
14710      */
14711     readRecords : function(o){
14712         /**
14713          * After any data loads, the raw JSON data is available for further custom processing.
14714          * @type Object
14715          */
14716         this.o = o;
14717         var s = this.meta, Record = this.recordType,
14718             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14719
14720 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14721         if (!this.ef) {
14722             if(s.totalProperty) {
14723                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14724                 }
14725                 if(s.successProperty) {
14726                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14727                 }
14728                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14729                 if (s.id) {
14730                         var g = this.getJsonAccessor(s.id);
14731                         this.getId = function(rec) {
14732                                 var r = g(rec);  
14733                                 return (r === undefined || r === "") ? null : r;
14734                         };
14735                 } else {
14736                         this.getId = function(){return null;};
14737                 }
14738             this.ef = [];
14739             for(var jj = 0; jj < fl; jj++){
14740                 f = fi[jj];
14741                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14742                 this.ef[jj] = this.getJsonAccessor(map);
14743             }
14744         }
14745
14746         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14747         if(s.totalProperty){
14748             var vt = parseInt(this.getTotal(o), 10);
14749             if(!isNaN(vt)){
14750                 totalRecords = vt;
14751             }
14752         }
14753         if(s.successProperty){
14754             var vs = this.getSuccess(o);
14755             if(vs === false || vs === 'false'){
14756                 success = false;
14757             }
14758         }
14759         var records = [];
14760         for(var i = 0; i < c; i++){
14761                 var n = root[i];
14762             var values = {};
14763             var id = this.getId(n);
14764             for(var j = 0; j < fl; j++){
14765                 f = fi[j];
14766             var v = this.ef[j](n);
14767             if (!f.convert) {
14768                 Roo.log('missing convert for ' + f.name);
14769                 Roo.log(f);
14770                 continue;
14771             }
14772             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14773             }
14774             var record = new Record(values, id);
14775             record.json = n;
14776             records[i] = record;
14777         }
14778         return {
14779             raw : o,
14780             success : success,
14781             records : records,
14782             totalRecords : totalRecords
14783         };
14784     },
14785     // used when loading children.. @see loadDataFromChildren
14786     toLoadData: function(rec)
14787     {
14788         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14789         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14790         return { data : data, total : data.length };
14791         
14792     }
14793 });/*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803
14804 /**
14805  * @class Roo.data.ArrayReader
14806  * @extends Roo.data.DataReader
14807  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14808  * Each element of that Array represents a row of data fields. The
14809  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14810  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14811  * <p>
14812  * Example code:.
14813  * <pre><code>
14814 var RecordDef = Roo.data.Record.create([
14815     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14816     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14817 ]);
14818 var myReader = new Roo.data.ArrayReader({
14819     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14820 }, RecordDef);
14821 </code></pre>
14822  * <p>
14823  * This would consume an Array like this:
14824  * <pre><code>
14825 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14826   </code></pre>
14827  
14828  * @constructor
14829  * Create a new JsonReader
14830  * @param {Object} meta Metadata configuration options.
14831  * @param {Object|Array} recordType Either an Array of field definition objects
14832  * 
14833  * @cfg {Array} fields Array of field definition objects
14834  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14835  * as specified to {@link Roo.data.Record#create},
14836  * or an {@link Roo.data.Record} object
14837  *
14838  * 
14839  * created using {@link Roo.data.Record#create}.
14840  */
14841 Roo.data.ArrayReader = function(meta, recordType)
14842 {    
14843     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14844 };
14845
14846 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14847     
14848       /**
14849      * Create a data block containing Roo.data.Records from an XML document.
14850      * @param {Object} o An Array of row objects which represents the dataset.
14851      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14852      * a cache of Roo.data.Records.
14853      */
14854     readRecords : function(o)
14855     {
14856         var sid = this.meta ? this.meta.id : null;
14857         var recordType = this.recordType, fields = recordType.prototype.fields;
14858         var records = [];
14859         var root = o;
14860         for(var i = 0; i < root.length; i++){
14861                 var n = root[i];
14862             var values = {};
14863             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14864             for(var j = 0, jlen = fields.length; j < jlen; j++){
14865                 var f = fields.items[j];
14866                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14867                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14868                 v = f.convert(v);
14869                 values[f.name] = v;
14870             }
14871             var record = new recordType(values, id);
14872             record.json = n;
14873             records[records.length] = record;
14874         }
14875         return {
14876             records : records,
14877             totalRecords : records.length
14878         };
14879     },
14880     // used when loading children.. @see loadDataFromChildren
14881     toLoadData: function(rec)
14882     {
14883         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14884         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14885         
14886     }
14887     
14888     
14889 });/*
14890  * - LGPL
14891  * * 
14892  */
14893
14894 /**
14895  * @class Roo.bootstrap.ComboBox
14896  * @extends Roo.bootstrap.TriggerField
14897  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14898  * @cfg {Boolean} append (true|false) default false
14899  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14900  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14901  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14902  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14903  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14904  * @cfg {Boolean} animate default true
14905  * @cfg {Boolean} emptyResultText only for touch device
14906  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14907  * @cfg {String} emptyTitle default ''
14908  * @cfg {Number} width fixed with? experimental
14909  * @constructor
14910  * Create a new ComboBox.
14911  * @param {Object} config Configuration options
14912  */
14913 Roo.bootstrap.ComboBox = function(config){
14914     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14915     this.addEvents({
14916         /**
14917          * @event expand
14918          * Fires when the dropdown list is expanded
14919         * @param {Roo.bootstrap.ComboBox} combo This combo box
14920         */
14921         'expand' : true,
14922         /**
14923          * @event collapse
14924          * Fires when the dropdown list is collapsed
14925         * @param {Roo.bootstrap.ComboBox} combo This combo box
14926         */
14927         'collapse' : true,
14928         /**
14929          * @event beforeselect
14930          * Fires before a list item is selected. Return false to cancel the selection.
14931         * @param {Roo.bootstrap.ComboBox} combo This combo box
14932         * @param {Roo.data.Record} record The data record returned from the underlying store
14933         * @param {Number} index The index of the selected item in the dropdown list
14934         */
14935         'beforeselect' : true,
14936         /**
14937          * @event select
14938          * Fires when a list item is selected
14939         * @param {Roo.bootstrap.ComboBox} combo This combo box
14940         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14941         * @param {Number} index The index of the selected item in the dropdown list
14942         */
14943         'select' : true,
14944         /**
14945          * @event beforequery
14946          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14947          * The event object passed has these properties:
14948         * @param {Roo.bootstrap.ComboBox} combo This combo box
14949         * @param {String} query The query
14950         * @param {Boolean} forceAll true to force "all" query
14951         * @param {Boolean} cancel true to cancel the query
14952         * @param {Object} e The query event object
14953         */
14954         'beforequery': true,
14955          /**
14956          * @event add
14957          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14958         * @param {Roo.bootstrap.ComboBox} combo This combo box
14959         */
14960         'add' : true,
14961         /**
14962          * @event edit
14963          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14964         * @param {Roo.bootstrap.ComboBox} combo This combo box
14965         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14966         */
14967         'edit' : true,
14968         /**
14969          * @event remove
14970          * Fires when the remove value from the combobox array
14971         * @param {Roo.bootstrap.ComboBox} combo This combo box
14972         */
14973         'remove' : true,
14974         /**
14975          * @event afterremove
14976          * Fires when the remove value from the combobox array
14977         * @param {Roo.bootstrap.ComboBox} combo This combo box
14978         */
14979         'afterremove' : true,
14980         /**
14981          * @event specialfilter
14982          * Fires when specialfilter
14983             * @param {Roo.bootstrap.ComboBox} combo This combo box
14984             */
14985         'specialfilter' : true,
14986         /**
14987          * @event tick
14988          * Fires when tick the element
14989             * @param {Roo.bootstrap.ComboBox} combo This combo box
14990             */
14991         'tick' : true,
14992         /**
14993          * @event touchviewdisplay
14994          * Fires when touch view require special display (default is using displayField)
14995             * @param {Roo.bootstrap.ComboBox} combo This combo box
14996             * @param {Object} cfg set html .
14997             */
14998         'touchviewdisplay' : true
14999         
15000     });
15001     
15002     this.item = [];
15003     this.tickItems = [];
15004     
15005     this.selectedIndex = -1;
15006     if(this.mode == 'local'){
15007         if(config.queryDelay === undefined){
15008             this.queryDelay = 10;
15009         }
15010         if(config.minChars === undefined){
15011             this.minChars = 0;
15012         }
15013     }
15014 };
15015
15016 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15017      
15018     /**
15019      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15020      * rendering into an Roo.Editor, defaults to false)
15021      */
15022     /**
15023      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15024      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15025      */
15026     /**
15027      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15028      */
15029     /**
15030      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15031      * the dropdown list (defaults to undefined, with no header element)
15032      */
15033
15034      /**
15035      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15036      */
15037      
15038      /**
15039      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15040      */
15041     listWidth: undefined,
15042     /**
15043      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15044      * mode = 'remote' or 'text' if mode = 'local')
15045      */
15046     displayField: undefined,
15047     
15048     /**
15049      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15050      * mode = 'remote' or 'value' if mode = 'local'). 
15051      * Note: use of a valueField requires the user make a selection
15052      * in order for a value to be mapped.
15053      */
15054     valueField: undefined,
15055     /**
15056      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15057      */
15058     modalTitle : '',
15059     
15060     /**
15061      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15062      * field's data value (defaults to the underlying DOM element's name)
15063      */
15064     hiddenName: undefined,
15065     /**
15066      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15067      */
15068     listClass: '',
15069     /**
15070      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15071      */
15072     selectedClass: 'active',
15073     
15074     /**
15075      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15076      */
15077     shadow:'sides',
15078     /**
15079      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15080      * anchor positions (defaults to 'tl-bl')
15081      */
15082     listAlign: 'tl-bl?',
15083     /**
15084      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15085      */
15086     maxHeight: 300,
15087     /**
15088      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15089      * query specified by the allQuery config option (defaults to 'query')
15090      */
15091     triggerAction: 'query',
15092     /**
15093      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15094      * (defaults to 4, does not apply if editable = false)
15095      */
15096     minChars : 4,
15097     /**
15098      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15099      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15100      */
15101     typeAhead: false,
15102     /**
15103      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15104      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15105      */
15106     queryDelay: 500,
15107     /**
15108      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15109      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15110      */
15111     pageSize: 0,
15112     /**
15113      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15114      * when editable = true (defaults to false)
15115      */
15116     selectOnFocus:false,
15117     /**
15118      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15119      */
15120     queryParam: 'query',
15121     /**
15122      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15123      * when mode = 'remote' (defaults to 'Loading...')
15124      */
15125     loadingText: 'Loading...',
15126     /**
15127      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15128      */
15129     resizable: false,
15130     /**
15131      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15132      */
15133     handleHeight : 8,
15134     /**
15135      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15136      * traditional select (defaults to true)
15137      */
15138     editable: true,
15139     /**
15140      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15141      */
15142     allQuery: '',
15143     /**
15144      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15145      */
15146     mode: 'remote',
15147     /**
15148      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15149      * listWidth has a higher value)
15150      */
15151     minListWidth : 70,
15152     /**
15153      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15154      * allow the user to set arbitrary text into the field (defaults to false)
15155      */
15156     forceSelection:false,
15157     /**
15158      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15159      * if typeAhead = true (defaults to 250)
15160      */
15161     typeAheadDelay : 250,
15162     /**
15163      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15164      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15165      */
15166     valueNotFoundText : undefined,
15167     /**
15168      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15169      */
15170     blockFocus : false,
15171     
15172     /**
15173      * @cfg {Boolean} disableClear Disable showing of clear button.
15174      */
15175     disableClear : false,
15176     /**
15177      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15178      */
15179     alwaysQuery : false,
15180     
15181     /**
15182      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15183      */
15184     multiple : false,
15185     
15186     /**
15187      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     invalidClass : "has-warning",
15190     
15191     /**
15192      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15193      */
15194     validClass : "has-success",
15195     
15196     /**
15197      * @cfg {Boolean} specialFilter (true|false) special filter default false
15198      */
15199     specialFilter : false,
15200     
15201     /**
15202      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15203      */
15204     mobileTouchView : true,
15205     
15206     /**
15207      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15208      */
15209     useNativeIOS : false,
15210     
15211     /**
15212      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15213      */
15214     mobile_restrict_height : false,
15215     
15216     ios_options : false,
15217     
15218     //private
15219     addicon : false,
15220     editicon: false,
15221     
15222     page: 0,
15223     hasQuery: false,
15224     append: false,
15225     loadNext: false,
15226     autoFocus : true,
15227     tickable : false,
15228     btnPosition : 'right',
15229     triggerList : true,
15230     showToggleBtn : true,
15231     animate : true,
15232     emptyResultText: 'Empty',
15233     triggerText : 'Select',
15234     emptyTitle : '',
15235     width : false,
15236     
15237     // element that contains real text value.. (when hidden is used..)
15238     
15239     getAutoCreate : function()
15240     {   
15241         var cfg = false;
15242         //render
15243         /*
15244          * Render classic select for iso
15245          */
15246         
15247         if(Roo.isIOS && this.useNativeIOS){
15248             cfg = this.getAutoCreateNativeIOS();
15249             return cfg;
15250         }
15251         
15252         /*
15253          * Touch Devices
15254          */
15255         
15256         if(Roo.isTouch && this.mobileTouchView){
15257             cfg = this.getAutoCreateTouchView();
15258             return cfg;;
15259         }
15260         
15261         /*
15262          *  Normal ComboBox
15263          */
15264         if(!this.tickable){
15265             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15266             return cfg;
15267         }
15268         
15269         /*
15270          *  ComboBox with tickable selections
15271          */
15272              
15273         var align = this.labelAlign || this.parentLabelAlign();
15274         
15275         cfg = {
15276             cls : 'form-group roo-combobox-tickable' //input-group
15277         };
15278         
15279         var btn_text_select = '';
15280         var btn_text_done = '';
15281         var btn_text_cancel = '';
15282         
15283         if (this.btn_text_show) {
15284             btn_text_select = 'Select';
15285             btn_text_done = 'Done';
15286             btn_text_cancel = 'Cancel'; 
15287         }
15288         
15289         var buttons = {
15290             tag : 'div',
15291             cls : 'tickable-buttons',
15292             cn : [
15293                 {
15294                     tag : 'button',
15295                     type : 'button',
15296                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15297                     //html : this.triggerText
15298                     html: btn_text_select
15299                 },
15300                 {
15301                     tag : 'button',
15302                     type : 'button',
15303                     name : 'ok',
15304                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15305                     //html : 'Done'
15306                     html: btn_text_done
15307                 },
15308                 {
15309                     tag : 'button',
15310                     type : 'button',
15311                     name : 'cancel',
15312                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15313                     //html : 'Cancel'
15314                     html: btn_text_cancel
15315                 }
15316             ]
15317         };
15318         
15319         if(this.editable){
15320             buttons.cn.unshift({
15321                 tag: 'input',
15322                 cls: 'roo-select2-search-field-input'
15323             });
15324         }
15325         
15326         var _this = this;
15327         
15328         Roo.each(buttons.cn, function(c){
15329             if (_this.size) {
15330                 c.cls += ' btn-' + _this.size;
15331             }
15332
15333             if (_this.disabled) {
15334                 c.disabled = true;
15335             }
15336         });
15337         
15338         var box = {
15339             tag: 'div',
15340             style : 'display: contents',
15341             cn: [
15342                 {
15343                     tag: 'input',
15344                     type : 'hidden',
15345                     cls: 'form-hidden-field'
15346                 },
15347                 {
15348                     tag: 'ul',
15349                     cls: 'roo-select2-choices',
15350                     cn:[
15351                         {
15352                             tag: 'li',
15353                             cls: 'roo-select2-search-field',
15354                             cn: [
15355                                 buttons
15356                             ]
15357                         }
15358                     ]
15359                 }
15360             ]
15361         };
15362         
15363         var combobox = {
15364             cls: 'roo-select2-container input-group roo-select2-container-multi',
15365             cn: [
15366                 
15367                 box
15368 //                {
15369 //                    tag: 'ul',
15370 //                    cls: 'typeahead typeahead-long dropdown-menu',
15371 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15372 //                }
15373             ]
15374         };
15375         
15376         if(this.hasFeedback && !this.allowBlank){
15377             
15378             var feedback = {
15379                 tag: 'span',
15380                 cls: 'glyphicon form-control-feedback'
15381             };
15382
15383             combobox.cn.push(feedback);
15384         }
15385         
15386         
15387         
15388         var indicator = {
15389             tag : 'i',
15390             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15391             tooltip : 'This field is required'
15392         };
15393         if (Roo.bootstrap.version == 4) {
15394             indicator = {
15395                 tag : 'i',
15396                 style : 'display:none'
15397             };
15398         }
15399         if (align ==='left' && this.fieldLabel.length) {
15400             
15401             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15402             
15403             cfg.cn = [
15404                 indicator,
15405                 {
15406                     tag: 'label',
15407                     'for' :  id,
15408                     cls : 'control-label col-form-label',
15409                     html : this.fieldLabel
15410
15411                 },
15412                 {
15413                     cls : "", 
15414                     cn: [
15415                         combobox
15416                     ]
15417                 }
15418
15419             ];
15420             
15421             var labelCfg = cfg.cn[1];
15422             var contentCfg = cfg.cn[2];
15423             
15424
15425             if(this.indicatorpos == 'right'){
15426                 
15427                 cfg.cn = [
15428                     {
15429                         tag: 'label',
15430                         'for' :  id,
15431                         cls : 'control-label col-form-label',
15432                         cn : [
15433                             {
15434                                 tag : 'span',
15435                                 html : this.fieldLabel
15436                             },
15437                             indicator
15438                         ]
15439                     },
15440                     {
15441                         cls : "",
15442                         cn: [
15443                             combobox
15444                         ]
15445                     }
15446
15447                 ];
15448                 
15449                 
15450                 
15451                 labelCfg = cfg.cn[0];
15452                 contentCfg = cfg.cn[1];
15453             
15454             }
15455             
15456             if(this.labelWidth > 12){
15457                 labelCfg.style = "width: " + this.labelWidth + 'px';
15458             }
15459             if(this.width * 1 > 0){
15460                 contentCfg.style = "width: " + this.width + 'px';
15461             }
15462             if(this.labelWidth < 13 && this.labelmd == 0){
15463                 this.labelmd = this.labelWidth;
15464             }
15465             
15466             if(this.labellg > 0){
15467                 labelCfg.cls += ' col-lg-' + this.labellg;
15468                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15469             }
15470             
15471             if(this.labelmd > 0){
15472                 labelCfg.cls += ' col-md-' + this.labelmd;
15473                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15474             }
15475             
15476             if(this.labelsm > 0){
15477                 labelCfg.cls += ' col-sm-' + this.labelsm;
15478                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15479             }
15480             
15481             if(this.labelxs > 0){
15482                 labelCfg.cls += ' col-xs-' + this.labelxs;
15483                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15484             }
15485                 
15486                 
15487         } else if ( this.fieldLabel.length) {
15488 //                Roo.log(" label");
15489                  cfg.cn = [
15490                    indicator,
15491                     {
15492                         tag: 'label',
15493                         //cls : 'input-group-addon',
15494                         html : this.fieldLabel
15495                     },
15496                     combobox
15497                 ];
15498                 
15499                 if(this.indicatorpos == 'right'){
15500                     cfg.cn = [
15501                         {
15502                             tag: 'label',
15503                             //cls : 'input-group-addon',
15504                             html : this.fieldLabel
15505                         },
15506                         indicator,
15507                         combobox
15508                     ];
15509                     
15510                 }
15511
15512         } else {
15513             
15514 //                Roo.log(" no label && no align");
15515                 cfg = combobox
15516                      
15517                 
15518         }
15519          
15520         var settings=this;
15521         ['xs','sm','md','lg'].map(function(size){
15522             if (settings[size]) {
15523                 cfg.cls += ' col-' + size + '-' + settings[size];
15524             }
15525         });
15526         
15527         return cfg;
15528         
15529     },
15530     
15531     _initEventsCalled : false,
15532     
15533     // private
15534     initEvents: function()
15535     {   
15536         if (this._initEventsCalled) { // as we call render... prevent looping...
15537             return;
15538         }
15539         this._initEventsCalled = true;
15540         
15541         if (!this.store) {
15542             throw "can not find store for combo";
15543         }
15544         
15545         this.indicator = this.indicatorEl();
15546         
15547         this.store = Roo.factory(this.store, Roo.data);
15548         this.store.parent = this;
15549         
15550         // if we are building from html. then this element is so complex, that we can not really
15551         // use the rendered HTML.
15552         // so we have to trash and replace the previous code.
15553         if (Roo.XComponent.build_from_html) {
15554             // remove this element....
15555             var e = this.el.dom, k=0;
15556             while (e ) { e = e.previousSibling;  ++k;}
15557
15558             this.el.remove();
15559             
15560             this.el=false;
15561             this.rendered = false;
15562             
15563             this.render(this.parent().getChildContainer(true), k);
15564         }
15565         
15566         if(Roo.isIOS && this.useNativeIOS){
15567             this.initIOSView();
15568             return;
15569         }
15570         
15571         /*
15572          * Touch Devices
15573          */
15574         
15575         if(Roo.isTouch && this.mobileTouchView){
15576             this.initTouchView();
15577             return;
15578         }
15579         
15580         if(this.tickable){
15581             this.initTickableEvents();
15582             return;
15583         }
15584         
15585         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15586         
15587         if(this.hiddenName){
15588             
15589             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15590             
15591             this.hiddenField.dom.value =
15592                 this.hiddenValue !== undefined ? this.hiddenValue :
15593                 this.value !== undefined ? this.value : '';
15594
15595             // prevent input submission
15596             this.el.dom.removeAttribute('name');
15597             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15598              
15599              
15600         }
15601         //if(Roo.isGecko){
15602         //    this.el.dom.setAttribute('autocomplete', 'off');
15603         //}
15604         
15605         var cls = 'x-combo-list';
15606         
15607         //this.list = new Roo.Layer({
15608         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15609         //});
15610         
15611         var _this = this;
15612         
15613         (function(){
15614             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15615             _this.list.setWidth(lw);
15616         }).defer(100);
15617         
15618         this.list.on('mouseover', this.onViewOver, this);
15619         this.list.on('mousemove', this.onViewMove, this);
15620         this.list.on('scroll', this.onViewScroll, this);
15621         
15622         /*
15623         this.list.swallowEvent('mousewheel');
15624         this.assetHeight = 0;
15625
15626         if(this.title){
15627             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15628             this.assetHeight += this.header.getHeight();
15629         }
15630
15631         this.innerList = this.list.createChild({cls:cls+'-inner'});
15632         this.innerList.on('mouseover', this.onViewOver, this);
15633         this.innerList.on('mousemove', this.onViewMove, this);
15634         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15635         
15636         if(this.allowBlank && !this.pageSize && !this.disableClear){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.Toolbar(this.footer);
15639            
15640         }
15641         if(this.pageSize){
15642             this.footer = this.list.createChild({cls:cls+'-ft'});
15643             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15644                     {pageSize: this.pageSize});
15645             
15646         }
15647         
15648         if (this.pageTb && this.allowBlank && !this.disableClear) {
15649             var _this = this;
15650             this.pageTb.add(new Roo.Toolbar.Fill(), {
15651                 cls: 'x-btn-icon x-btn-clear',
15652                 text: '&#160;',
15653                 handler: function()
15654                 {
15655                     _this.collapse();
15656                     _this.clearValue();
15657                     _this.onSelect(false, -1);
15658                 }
15659             });
15660         }
15661         if (this.footer) {
15662             this.assetHeight += this.footer.getHeight();
15663         }
15664         */
15665             
15666         if(!this.tpl){
15667             this.tpl = Roo.bootstrap.version == 4 ?
15668                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15669                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15670         }
15671
15672         this.view = new Roo.View(this.list, this.tpl, {
15673             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15674         });
15675         //this.view.wrapEl.setDisplayed(false);
15676         this.view.on('click', this.onViewClick, this);
15677         
15678         
15679         this.store.on('beforeload', this.onBeforeLoad, this);
15680         this.store.on('load', this.onLoad, this);
15681         this.store.on('loadexception', this.onLoadException, this);
15682         /*
15683         if(this.resizable){
15684             this.resizer = new Roo.Resizable(this.list,  {
15685                pinned:true, handles:'se'
15686             });
15687             this.resizer.on('resize', function(r, w, h){
15688                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15689                 this.listWidth = w;
15690                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15691                 this.restrictHeight();
15692             }, this);
15693             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15694         }
15695         */
15696         if(!this.editable){
15697             this.editable = true;
15698             this.setEditable(false);
15699         }
15700         
15701         /*
15702         
15703         if (typeof(this.events.add.listeners) != 'undefined') {
15704             
15705             this.addicon = this.wrap.createChild(
15706                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15707        
15708             this.addicon.on('click', function(e) {
15709                 this.fireEvent('add', this);
15710             }, this);
15711         }
15712         if (typeof(this.events.edit.listeners) != 'undefined') {
15713             
15714             this.editicon = this.wrap.createChild(
15715                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15716             if (this.addicon) {
15717                 this.editicon.setStyle('margin-left', '40px');
15718             }
15719             this.editicon.on('click', function(e) {
15720                 
15721                 // we fire even  if inothing is selected..
15722                 this.fireEvent('edit', this, this.lastData );
15723                 
15724             }, this);
15725         }
15726         */
15727         
15728         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15729             "up" : function(e){
15730                 this.inKeyMode = true;
15731                 this.selectPrev();
15732             },
15733
15734             "down" : function(e){
15735                 if(!this.isExpanded()){
15736                     this.onTriggerClick();
15737                 }else{
15738                     this.inKeyMode = true;
15739                     this.selectNext();
15740                 }
15741             },
15742
15743             "enter" : function(e){
15744 //                this.onViewClick();
15745                 //return true;
15746                 this.collapse();
15747                 
15748                 if(this.fireEvent("specialkey", this, e)){
15749                     this.onViewClick(false);
15750                 }
15751                 
15752                 return true;
15753             },
15754
15755             "esc" : function(e){
15756                 this.collapse();
15757             },
15758
15759             "tab" : function(e){
15760                 this.collapse();
15761                 
15762                 if(this.fireEvent("specialkey", this, e)){
15763                     this.onViewClick(false);
15764                 }
15765                 
15766                 return true;
15767             },
15768
15769             scope : this,
15770
15771             doRelay : function(foo, bar, hname){
15772                 if(hname == 'down' || this.scope.isExpanded()){
15773                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15774                 }
15775                 return true;
15776             },
15777
15778             forceKeyDown: true
15779         });
15780         
15781         
15782         this.queryDelay = Math.max(this.queryDelay || 10,
15783                 this.mode == 'local' ? 10 : 250);
15784         
15785         
15786         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15787         
15788         if(this.typeAhead){
15789             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15790         }
15791         if(this.editable !== false){
15792             this.inputEl().on("keyup", this.onKeyUp, this);
15793         }
15794         if(this.forceSelection){
15795             this.inputEl().on('blur', this.doForce, this);
15796         }
15797         
15798         if(this.multiple){
15799             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15800             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15801         }
15802     },
15803     
15804     initTickableEvents: function()
15805     {   
15806         this.createList();
15807         
15808         if(this.hiddenName){
15809             
15810             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15811             
15812             this.hiddenField.dom.value =
15813                 this.hiddenValue !== undefined ? this.hiddenValue :
15814                 this.value !== undefined ? this.value : '';
15815
15816             // prevent input submission
15817             this.el.dom.removeAttribute('name');
15818             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15819              
15820              
15821         }
15822         
15823 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15824         
15825         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15826         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15827         if(this.triggerList){
15828             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15829         }
15830          
15831         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15832         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15833         
15834         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15835         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15836         
15837         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15838         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15839         
15840         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15841         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15842         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15843         
15844         this.okBtn.hide();
15845         this.cancelBtn.hide();
15846         
15847         var _this = this;
15848         
15849         (function(){
15850             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15851             _this.list.setWidth(lw);
15852         }).defer(100);
15853         
15854         this.list.on('mouseover', this.onViewOver, this);
15855         this.list.on('mousemove', this.onViewMove, this);
15856         
15857         this.list.on('scroll', this.onViewScroll, this);
15858         
15859         if(!this.tpl){
15860             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15861                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15862         }
15863
15864         this.view = new Roo.View(this.list, this.tpl, {
15865             singleSelect:true,
15866             tickable:true,
15867             parent:this,
15868             store: this.store,
15869             selectedClass: this.selectedClass
15870         });
15871         
15872         //this.view.wrapEl.setDisplayed(false);
15873         this.view.on('click', this.onViewClick, this);
15874         
15875         
15876         
15877         this.store.on('beforeload', this.onBeforeLoad, this);
15878         this.store.on('load', this.onLoad, this);
15879         this.store.on('loadexception', this.onLoadException, this);
15880         
15881         if(this.editable){
15882             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15883                 "up" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectPrev();
15886                 },
15887
15888                 "down" : function(e){
15889                     this.inKeyMode = true;
15890                     this.selectNext();
15891                 },
15892
15893                 "enter" : function(e){
15894                     if(this.fireEvent("specialkey", this, e)){
15895                         this.onViewClick(false);
15896                     }
15897                     
15898                     return true;
15899                 },
15900
15901                 "esc" : function(e){
15902                     this.onTickableFooterButtonClick(e, false, false);
15903                 },
15904
15905                 "tab" : function(e){
15906                     this.fireEvent("specialkey", this, e);
15907                     
15908                     this.onTickableFooterButtonClick(e, false, false);
15909                     
15910                     return true;
15911                 },
15912
15913                 scope : this,
15914
15915                 doRelay : function(e, fn, key){
15916                     if(this.scope.isExpanded()){
15917                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15918                     }
15919                     return true;
15920                 },
15921
15922                 forceKeyDown: true
15923             });
15924         }
15925         
15926         this.queryDelay = Math.max(this.queryDelay || 10,
15927                 this.mode == 'local' ? 10 : 250);
15928         
15929         
15930         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15931         
15932         if(this.typeAhead){
15933             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15934         }
15935         
15936         if(this.editable !== false){
15937             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15938         }
15939         
15940         this.indicator = this.indicatorEl();
15941         
15942         if(this.indicator){
15943             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15944             this.indicator.hide();
15945         }
15946         
15947     },
15948
15949     onDestroy : function(){
15950         if(this.view){
15951             this.view.setStore(null);
15952             this.view.el.removeAllListeners();
15953             this.view.el.remove();
15954             this.view.purgeListeners();
15955         }
15956         if(this.list){
15957             this.list.dom.innerHTML  = '';
15958         }
15959         
15960         if(this.store){
15961             this.store.un('beforeload', this.onBeforeLoad, this);
15962             this.store.un('load', this.onLoad, this);
15963             this.store.un('loadexception', this.onLoadException, this);
15964         }
15965         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15966     },
15967
15968     // private
15969     fireKey : function(e){
15970         if(e.isNavKeyPress() && !this.list.isVisible()){
15971             this.fireEvent("specialkey", this, e);
15972         }
15973     },
15974
15975     // private
15976     onResize: function(w, h)
15977     {
15978         
15979         
15980 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15981 //        
15982 //        if(typeof w != 'number'){
15983 //            // we do not handle it!?!?
15984 //            return;
15985 //        }
15986 //        var tw = this.trigger.getWidth();
15987 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15988 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15989 //        var x = w - tw;
15990 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15991 //            
15992 //        //this.trigger.setStyle('left', x+'px');
15993 //        
15994 //        if(this.list && this.listWidth === undefined){
15995 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15996 //            this.list.setWidth(lw);
15997 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15998 //        }
15999         
16000     
16001         
16002     },
16003
16004     /**
16005      * Allow or prevent the user from directly editing the field text.  If false is passed,
16006      * the user will only be able to select from the items defined in the dropdown list.  This method
16007      * is the runtime equivalent of setting the 'editable' config option at config time.
16008      * @param {Boolean} value True to allow the user to directly edit the field text
16009      */
16010     setEditable : function(value){
16011         if(value == this.editable){
16012             return;
16013         }
16014         this.editable = value;
16015         if(!value){
16016             this.inputEl().dom.setAttribute('readOnly', true);
16017             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16018             this.inputEl().addClass('x-combo-noedit');
16019         }else{
16020             this.inputEl().dom.setAttribute('readOnly', false);
16021             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16022             this.inputEl().removeClass('x-combo-noedit');
16023         }
16024     },
16025
16026     // private
16027     
16028     onBeforeLoad : function(combo,opts){
16029         if(!this.hasFocus){
16030             return;
16031         }
16032          if (!opts.add) {
16033             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16034          }
16035         this.restrictHeight();
16036         this.selectedIndex = -1;
16037     },
16038
16039     // private
16040     onLoad : function(){
16041         
16042         this.hasQuery = false;
16043         
16044         if(!this.hasFocus){
16045             return;
16046         }
16047         
16048         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16049             this.loading.hide();
16050         }
16051         
16052         if(this.store.getCount() > 0){
16053             
16054             this.expand();
16055             this.restrictHeight();
16056             if(this.lastQuery == this.allQuery){
16057                 if(this.editable && !this.tickable){
16058                     this.inputEl().dom.select();
16059                 }
16060                 
16061                 if(
16062                     !this.selectByValue(this.value, true) &&
16063                     this.autoFocus && 
16064                     (
16065                         !this.store.lastOptions ||
16066                         typeof(this.store.lastOptions.add) == 'undefined' || 
16067                         this.store.lastOptions.add != true
16068                     )
16069                 ){
16070                     this.select(0, true);
16071                 }
16072             }else{
16073                 if(this.autoFocus){
16074                     this.selectNext();
16075                 }
16076                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16077                     this.taTask.delay(this.typeAheadDelay);
16078                 }
16079             }
16080         }else{
16081             this.onEmptyResults();
16082         }
16083         
16084         //this.el.focus();
16085     },
16086     // private
16087     onLoadException : function()
16088     {
16089         this.hasQuery = false;
16090         
16091         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16092             this.loading.hide();
16093         }
16094         
16095         if(this.tickable && this.editable){
16096             return;
16097         }
16098         
16099         this.collapse();
16100         // only causes errors at present
16101         //Roo.log(this.store.reader.jsonData);
16102         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16103             // fixme
16104             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16105         //}
16106         
16107         
16108     },
16109     // private
16110     onTypeAhead : function(){
16111         if(this.store.getCount() > 0){
16112             var r = this.store.getAt(0);
16113             var newValue = r.data[this.displayField];
16114             var len = newValue.length;
16115             var selStart = this.getRawValue().length;
16116             
16117             if(selStart != len){
16118                 this.setRawValue(newValue);
16119                 this.selectText(selStart, newValue.length);
16120             }
16121         }
16122     },
16123
16124     // private
16125     onSelect : function(record, index){
16126         
16127         if(this.fireEvent('beforeselect', this, record, index) !== false){
16128         
16129             this.setFromData(index > -1 ? record.data : false);
16130             
16131             this.collapse();
16132             this.fireEvent('select', this, record, index);
16133         }
16134     },
16135
16136     /**
16137      * Returns the currently selected field value or empty string if no value is set.
16138      * @return {String} value The selected value
16139      */
16140     getValue : function()
16141     {
16142         if(Roo.isIOS && this.useNativeIOS){
16143             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16144         }
16145         
16146         if(this.multiple){
16147             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16148         }
16149         
16150         if(this.valueField){
16151             return typeof this.value != 'undefined' ? this.value : '';
16152         }else{
16153             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16154         }
16155     },
16156     
16157     getRawValue : function()
16158     {
16159         if(Roo.isIOS && this.useNativeIOS){
16160             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16161         }
16162         
16163         var v = this.inputEl().getValue();
16164         
16165         return v;
16166     },
16167
16168     /**
16169      * Clears any text/value currently set in the field
16170      */
16171     clearValue : function(){
16172         
16173         if(this.hiddenField){
16174             this.hiddenField.dom.value = '';
16175         }
16176         this.value = '';
16177         this.setRawValue('');
16178         this.lastSelectionText = '';
16179         this.lastData = false;
16180         
16181         var close = this.closeTriggerEl();
16182         
16183         if(close){
16184             close.hide();
16185         }
16186         
16187         this.validate();
16188         
16189     },
16190
16191     /**
16192      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16193      * will be displayed in the field.  If the value does not match the data value of an existing item,
16194      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16195      * Otherwise the field will be blank (although the value will still be set).
16196      * @param {String} value The value to match
16197      */
16198     setValue : function(v)
16199     {
16200         if(Roo.isIOS && this.useNativeIOS){
16201             this.setIOSValue(v);
16202             return;
16203         }
16204         
16205         if(this.multiple){
16206             this.syncValue();
16207             return;
16208         }
16209         
16210         var text = v;
16211         if(this.valueField){
16212             var r = this.findRecord(this.valueField, v);
16213             if(r){
16214                 text = r.data[this.displayField];
16215             }else if(this.valueNotFoundText !== undefined){
16216                 text = this.valueNotFoundText;
16217             }
16218         }
16219         this.lastSelectionText = text;
16220         if(this.hiddenField){
16221             this.hiddenField.dom.value = v;
16222         }
16223         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16224         this.value = v;
16225         
16226         var close = this.closeTriggerEl();
16227         
16228         if(close){
16229             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16230         }
16231         
16232         this.validate();
16233     },
16234     /**
16235      * @property {Object} the last set data for the element
16236      */
16237     
16238     lastData : false,
16239     /**
16240      * Sets the value of the field based on a object which is related to the record format for the store.
16241      * @param {Object} value the value to set as. or false on reset?
16242      */
16243     setFromData : function(o){
16244         
16245         if(this.multiple){
16246             this.addItem(o);
16247             return;
16248         }
16249             
16250         var dv = ''; // display value
16251         var vv = ''; // value value..
16252         this.lastData = o;
16253         if (this.displayField) {
16254             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16255         } else {
16256             // this is an error condition!!!
16257             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16258         }
16259         
16260         if(this.valueField){
16261             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16262         }
16263         
16264         var close = this.closeTriggerEl();
16265         
16266         if(close){
16267             if(dv.length || vv * 1 > 0){
16268                 close.show() ;
16269                 this.blockFocus=true;
16270             } else {
16271                 close.hide();
16272             }             
16273         }
16274         
16275         if(this.hiddenField){
16276             this.hiddenField.dom.value = vv;
16277             
16278             this.lastSelectionText = dv;
16279             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16280             this.value = vv;
16281             return;
16282         }
16283         // no hidden field.. - we store the value in 'value', but still display
16284         // display field!!!!
16285         this.lastSelectionText = dv;
16286         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16287         this.value = vv;
16288         
16289         
16290         
16291     },
16292     // private
16293     reset : function(){
16294         // overridden so that last data is reset..
16295         
16296         if(this.multiple){
16297             this.clearItem();
16298             return;
16299         }
16300         
16301         this.setValue(this.originalValue);
16302         //this.clearInvalid();
16303         this.lastData = false;
16304         if (this.view) {
16305             this.view.clearSelections();
16306         }
16307         
16308         this.validate();
16309     },
16310     // private
16311     findRecord : function(prop, value){
16312         var record;
16313         if(this.store.getCount() > 0){
16314             this.store.each(function(r){
16315                 if(r.data[prop] == value){
16316                     record = r;
16317                     return false;
16318                 }
16319                 return true;
16320             });
16321         }
16322         return record;
16323     },
16324     
16325     getName: function()
16326     {
16327         // returns hidden if it's set..
16328         if (!this.rendered) {return ''};
16329         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16330         
16331     },
16332     // private
16333     onViewMove : function(e, t){
16334         this.inKeyMode = false;
16335     },
16336
16337     // private
16338     onViewOver : function(e, t){
16339         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16340             return;
16341         }
16342         var item = this.view.findItemFromChild(t);
16343         
16344         if(item){
16345             var index = this.view.indexOf(item);
16346             this.select(index, false);
16347         }
16348     },
16349
16350     // private
16351     onViewClick : function(view, doFocus, el, e)
16352     {
16353         var index = this.view.getSelectedIndexes()[0];
16354         
16355         var r = this.store.getAt(index);
16356         
16357         if(this.tickable){
16358             
16359             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16360                 return;
16361             }
16362             
16363             var rm = false;
16364             var _this = this;
16365             
16366             Roo.each(this.tickItems, function(v,k){
16367                 
16368                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16369                     Roo.log(v);
16370                     _this.tickItems.splice(k, 1);
16371                     
16372                     if(typeof(e) == 'undefined' && view == false){
16373                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16374                     }
16375                     
16376                     rm = true;
16377                     return;
16378                 }
16379             });
16380             
16381             if(rm){
16382                 return;
16383             }
16384             
16385             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16386                 this.tickItems.push(r.data);
16387             }
16388             
16389             if(typeof(e) == 'undefined' && view == false){
16390                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16391             }
16392                     
16393             return;
16394         }
16395         
16396         if(r){
16397             this.onSelect(r, index);
16398         }
16399         if(doFocus !== false && !this.blockFocus){
16400             this.inputEl().focus();
16401         }
16402     },
16403
16404     // private
16405     restrictHeight : function(){
16406         //this.innerList.dom.style.height = '';
16407         //var inner = this.innerList.dom;
16408         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16409         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16410         //this.list.beginUpdate();
16411         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16412         this.list.alignTo(this.inputEl(), this.listAlign);
16413         this.list.alignTo(this.inputEl(), this.listAlign);
16414         //this.list.endUpdate();
16415     },
16416
16417     // private
16418     onEmptyResults : function(){
16419         
16420         if(this.tickable && this.editable){
16421             this.hasFocus = false;
16422             this.restrictHeight();
16423             return;
16424         }
16425         
16426         this.collapse();
16427     },
16428
16429     /**
16430      * Returns true if the dropdown list is expanded, else false.
16431      */
16432     isExpanded : function(){
16433         return this.list.isVisible();
16434     },
16435
16436     /**
16437      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16438      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16439      * @param {String} value The data value of the item to select
16440      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16441      * selected item if it is not currently in view (defaults to true)
16442      * @return {Boolean} True if the value matched an item in the list, else false
16443      */
16444     selectByValue : function(v, scrollIntoView){
16445         if(v !== undefined && v !== null){
16446             var r = this.findRecord(this.valueField || this.displayField, v);
16447             if(r){
16448                 this.select(this.store.indexOf(r), scrollIntoView);
16449                 return true;
16450             }
16451         }
16452         return false;
16453     },
16454
16455     /**
16456      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16457      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16458      * @param {Number} index The zero-based index of the list item to select
16459      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16460      * selected item if it is not currently in view (defaults to true)
16461      */
16462     select : function(index, scrollIntoView){
16463         this.selectedIndex = index;
16464         this.view.select(index);
16465         if(scrollIntoView !== false){
16466             var el = this.view.getNode(index);
16467             /*
16468              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16469              */
16470             if(el){
16471                 this.list.scrollChildIntoView(el, false);
16472             }
16473         }
16474     },
16475
16476     // private
16477     selectNext : function(){
16478         var ct = this.store.getCount();
16479         if(ct > 0){
16480             if(this.selectedIndex == -1){
16481                 this.select(0);
16482             }else if(this.selectedIndex < ct-1){
16483                 this.select(this.selectedIndex+1);
16484             }
16485         }
16486     },
16487
16488     // private
16489     selectPrev : function(){
16490         var ct = this.store.getCount();
16491         if(ct > 0){
16492             if(this.selectedIndex == -1){
16493                 this.select(0);
16494             }else if(this.selectedIndex != 0){
16495                 this.select(this.selectedIndex-1);
16496             }
16497         }
16498     },
16499
16500     // private
16501     onKeyUp : function(e){
16502         if(this.editable !== false && !e.isSpecialKey()){
16503             this.lastKey = e.getKey();
16504             this.dqTask.delay(this.queryDelay);
16505         }
16506     },
16507
16508     // private
16509     validateBlur : function(){
16510         return !this.list || !this.list.isVisible();   
16511     },
16512
16513     // private
16514     initQuery : function(){
16515         
16516         var v = this.getRawValue();
16517         
16518         if(this.tickable && this.editable){
16519             v = this.tickableInputEl().getValue();
16520         }
16521         
16522         this.doQuery(v);
16523     },
16524
16525     // private
16526     doForce : function(){
16527         if(this.inputEl().dom.value.length > 0){
16528             this.inputEl().dom.value =
16529                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16530              
16531         }
16532     },
16533
16534     /**
16535      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16536      * query allowing the query action to be canceled if needed.
16537      * @param {String} query The SQL query to execute
16538      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16539      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16540      * saved in the current store (defaults to false)
16541      */
16542     doQuery : function(q, forceAll){
16543         
16544         if(q === undefined || q === null){
16545             q = '';
16546         }
16547         var qe = {
16548             query: q,
16549             forceAll: forceAll,
16550             combo: this,
16551             cancel:false
16552         };
16553         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16554             return false;
16555         }
16556         q = qe.query;
16557         
16558         forceAll = qe.forceAll;
16559         if(forceAll === true || (q.length >= this.minChars)){
16560             
16561             this.hasQuery = true;
16562             
16563             if(this.lastQuery != q || this.alwaysQuery){
16564                 this.lastQuery = q;
16565                 if(this.mode == 'local'){
16566                     this.selectedIndex = -1;
16567                     if(forceAll){
16568                         this.store.clearFilter();
16569                     }else{
16570                         
16571                         if(this.specialFilter){
16572                             this.fireEvent('specialfilter', this);
16573                             this.onLoad();
16574                             return;
16575                         }
16576                         
16577                         this.store.filter(this.displayField, q);
16578                     }
16579                     
16580                     this.store.fireEvent("datachanged", this.store);
16581                     
16582                     this.onLoad();
16583                     
16584                     
16585                 }else{
16586                     
16587                     this.store.baseParams[this.queryParam] = q;
16588                     
16589                     var options = {params : this.getParams(q)};
16590                     
16591                     if(this.loadNext){
16592                         options.add = true;
16593                         options.params.start = this.page * this.pageSize;
16594                     }
16595                     
16596                     this.store.load(options);
16597                     
16598                     /*
16599                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16600                      *  we should expand the list on onLoad
16601                      *  so command out it
16602                      */
16603 //                    this.expand();
16604                 }
16605             }else{
16606                 this.selectedIndex = -1;
16607                 this.onLoad();   
16608             }
16609         }
16610         
16611         this.loadNext = false;
16612     },
16613     
16614     // private
16615     getParams : function(q){
16616         var p = {};
16617         //p[this.queryParam] = q;
16618         
16619         if(this.pageSize){
16620             p.start = 0;
16621             p.limit = this.pageSize;
16622         }
16623         return p;
16624     },
16625
16626     /**
16627      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16628      */
16629     collapse : function(){
16630         if(!this.isExpanded()){
16631             return;
16632         }
16633         
16634         this.list.hide();
16635         
16636         this.hasFocus = false;
16637         
16638         if(this.tickable){
16639             this.okBtn.hide();
16640             this.cancelBtn.hide();
16641             this.trigger.show();
16642             
16643             if(this.editable){
16644                 this.tickableInputEl().dom.value = '';
16645                 this.tickableInputEl().blur();
16646             }
16647             
16648         }
16649         
16650         Roo.get(document).un('mousedown', this.collapseIf, this);
16651         Roo.get(document).un('mousewheel', this.collapseIf, this);
16652         if (!this.editable) {
16653             Roo.get(document).un('keydown', this.listKeyPress, this);
16654         }
16655         this.fireEvent('collapse', this);
16656         
16657         this.validate();
16658     },
16659
16660     // private
16661     collapseIf : function(e){
16662         var in_combo  = e.within(this.el);
16663         var in_list =  e.within(this.list);
16664         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16665         
16666         if (in_combo || in_list || is_list) {
16667             //e.stopPropagation();
16668             return;
16669         }
16670         
16671         if(this.tickable){
16672             this.onTickableFooterButtonClick(e, false, false);
16673         }
16674
16675         this.collapse();
16676         
16677     },
16678
16679     /**
16680      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16681      */
16682     expand : function(){
16683        
16684         if(this.isExpanded() || !this.hasFocus){
16685             return;
16686         }
16687         
16688         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16689         this.list.setWidth(lw);
16690         
16691         Roo.log('expand');
16692         
16693         this.list.show();
16694         
16695         this.restrictHeight();
16696         
16697         if(this.tickable){
16698             
16699             this.tickItems = Roo.apply([], this.item);
16700             
16701             this.okBtn.show();
16702             this.cancelBtn.show();
16703             this.trigger.hide();
16704             
16705             if(this.editable){
16706                 this.tickableInputEl().focus();
16707             }
16708             
16709         }
16710         
16711         Roo.get(document).on('mousedown', this.collapseIf, this);
16712         Roo.get(document).on('mousewheel', this.collapseIf, this);
16713         if (!this.editable) {
16714             Roo.get(document).on('keydown', this.listKeyPress, this);
16715         }
16716         
16717         this.fireEvent('expand', this);
16718     },
16719
16720     // private
16721     // Implements the default empty TriggerField.onTriggerClick function
16722     onTriggerClick : function(e)
16723     {
16724         Roo.log('trigger click');
16725         
16726         if(this.disabled || !this.triggerList){
16727             return;
16728         }
16729         
16730         this.page = 0;
16731         this.loadNext = false;
16732         
16733         if(this.isExpanded()){
16734             this.collapse();
16735             if (!this.blockFocus) {
16736                 this.inputEl().focus();
16737             }
16738             
16739         }else {
16740             this.hasFocus = true;
16741             if(this.triggerAction == 'all') {
16742                 this.doQuery(this.allQuery, true);
16743             } else {
16744                 this.doQuery(this.getRawValue());
16745             }
16746             if (!this.blockFocus) {
16747                 this.inputEl().focus();
16748             }
16749         }
16750     },
16751     
16752     onTickableTriggerClick : function(e)
16753     {
16754         if(this.disabled){
16755             return;
16756         }
16757         
16758         this.page = 0;
16759         this.loadNext = false;
16760         this.hasFocus = true;
16761         
16762         if(this.triggerAction == 'all') {
16763             this.doQuery(this.allQuery, true);
16764         } else {
16765             this.doQuery(this.getRawValue());
16766         }
16767     },
16768     
16769     onSearchFieldClick : function(e)
16770     {
16771         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16772             this.onTickableFooterButtonClick(e, false, false);
16773             return;
16774         }
16775         
16776         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16777             return;
16778         }
16779         
16780         this.page = 0;
16781         this.loadNext = false;
16782         this.hasFocus = true;
16783         
16784         if(this.triggerAction == 'all') {
16785             this.doQuery(this.allQuery, true);
16786         } else {
16787             this.doQuery(this.getRawValue());
16788         }
16789     },
16790     
16791     listKeyPress : function(e)
16792     {
16793         //Roo.log('listkeypress');
16794         // scroll to first matching element based on key pres..
16795         if (e.isSpecialKey()) {
16796             return false;
16797         }
16798         var k = String.fromCharCode(e.getKey()).toUpperCase();
16799         //Roo.log(k);
16800         var match  = false;
16801         var csel = this.view.getSelectedNodes();
16802         var cselitem = false;
16803         if (csel.length) {
16804             var ix = this.view.indexOf(csel[0]);
16805             cselitem  = this.store.getAt(ix);
16806             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16807                 cselitem = false;
16808             }
16809             
16810         }
16811         
16812         this.store.each(function(v) { 
16813             if (cselitem) {
16814                 // start at existing selection.
16815                 if (cselitem.id == v.id) {
16816                     cselitem = false;
16817                 }
16818                 return true;
16819             }
16820                 
16821             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16822                 match = this.store.indexOf(v);
16823                 return false;
16824             }
16825             return true;
16826         }, this);
16827         
16828         if (match === false) {
16829             return true; // no more action?
16830         }
16831         // scroll to?
16832         this.view.select(match);
16833         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16834         sn.scrollIntoView(sn.dom.parentNode, false);
16835     },
16836     
16837     onViewScroll : function(e, t){
16838         
16839         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){
16840             return;
16841         }
16842         
16843         this.hasQuery = true;
16844         
16845         this.loading = this.list.select('.loading', true).first();
16846         
16847         if(this.loading === null){
16848             this.list.createChild({
16849                 tag: 'div',
16850                 cls: 'loading roo-select2-more-results roo-select2-active',
16851                 html: 'Loading more results...'
16852             });
16853             
16854             this.loading = this.list.select('.loading', true).first();
16855             
16856             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16857             
16858             this.loading.hide();
16859         }
16860         
16861         this.loading.show();
16862         
16863         var _combo = this;
16864         
16865         this.page++;
16866         this.loadNext = true;
16867         
16868         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16869         
16870         return;
16871     },
16872     
16873     addItem : function(o)
16874     {   
16875         var dv = ''; // display value
16876         
16877         if (this.displayField) {
16878             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16879         } else {
16880             // this is an error condition!!!
16881             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16882         }
16883         
16884         if(!dv.length){
16885             return;
16886         }
16887         
16888         var choice = this.choices.createChild({
16889             tag: 'li',
16890             cls: 'roo-select2-search-choice',
16891             cn: [
16892                 {
16893                     tag: 'div',
16894                     html: dv
16895                 },
16896                 {
16897                     tag: 'a',
16898                     href: '#',
16899                     cls: 'roo-select2-search-choice-close fa fa-times',
16900                     tabindex: '-1'
16901                 }
16902             ]
16903             
16904         }, this.searchField);
16905         
16906         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16907         
16908         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16909         
16910         this.item.push(o);
16911         
16912         this.lastData = o;
16913         
16914         this.syncValue();
16915         
16916         this.inputEl().dom.value = '';
16917         
16918         this.validate();
16919     },
16920     
16921     onRemoveItem : function(e, _self, o)
16922     {
16923         e.preventDefault();
16924         
16925         this.lastItem = Roo.apply([], this.item);
16926         
16927         var index = this.item.indexOf(o.data) * 1;
16928         
16929         if( index < 0){
16930             Roo.log('not this item?!');
16931             return;
16932         }
16933         
16934         this.item.splice(index, 1);
16935         o.item.remove();
16936         
16937         this.syncValue();
16938         
16939         this.fireEvent('remove', this, e);
16940         
16941         this.validate();
16942         
16943     },
16944     
16945     syncValue : function()
16946     {
16947         if(!this.item.length){
16948             this.clearValue();
16949             return;
16950         }
16951             
16952         var value = [];
16953         var _this = this;
16954         Roo.each(this.item, function(i){
16955             if(_this.valueField){
16956                 value.push(i[_this.valueField]);
16957                 return;
16958             }
16959
16960             value.push(i);
16961         });
16962
16963         this.value = value.join(',');
16964
16965         if(this.hiddenField){
16966             this.hiddenField.dom.value = this.value;
16967         }
16968         
16969         this.store.fireEvent("datachanged", this.store);
16970         
16971         this.validate();
16972     },
16973     
16974     clearItem : function()
16975     {
16976         if(!this.multiple){
16977             return;
16978         }
16979         
16980         this.item = [];
16981         
16982         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16983            c.remove();
16984         });
16985         
16986         this.syncValue();
16987         
16988         this.validate();
16989         
16990         if(this.tickable && !Roo.isTouch){
16991             this.view.refresh();
16992         }
16993     },
16994     
16995     inputEl: function ()
16996     {
16997         if(Roo.isIOS && this.useNativeIOS){
16998             return this.el.select('select.roo-ios-select', true).first();
16999         }
17000         
17001         if(Roo.isTouch && this.mobileTouchView){
17002             return this.el.select('input.form-control',true).first();
17003         }
17004         
17005         if(this.tickable){
17006             return this.searchField;
17007         }
17008         
17009         return this.el.select('input.form-control',true).first();
17010     },
17011     
17012     onTickableFooterButtonClick : function(e, btn, el)
17013     {
17014         e.preventDefault();
17015         
17016         this.lastItem = Roo.apply([], this.item);
17017         
17018         if(btn && btn.name == 'cancel'){
17019             this.tickItems = Roo.apply([], this.item);
17020             this.collapse();
17021             return;
17022         }
17023         
17024         this.clearItem();
17025         
17026         var _this = this;
17027         
17028         Roo.each(this.tickItems, function(o){
17029             _this.addItem(o);
17030         });
17031         
17032         this.collapse();
17033         
17034     },
17035     
17036     validate : function()
17037     {
17038         if(this.getVisibilityEl().hasClass('hidden')){
17039             return true;
17040         }
17041         
17042         var v = this.getRawValue();
17043         
17044         if(this.multiple){
17045             v = this.getValue();
17046         }
17047         
17048         if(this.disabled || this.allowBlank || v.length){
17049             this.markValid();
17050             return true;
17051         }
17052         
17053         this.markInvalid();
17054         return false;
17055     },
17056     
17057     tickableInputEl : function()
17058     {
17059         if(!this.tickable || !this.editable){
17060             return this.inputEl();
17061         }
17062         
17063         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17064     },
17065     
17066     
17067     getAutoCreateTouchView : function()
17068     {
17069         var id = Roo.id();
17070         
17071         var cfg = {
17072             cls: 'form-group' //input-group
17073         };
17074         
17075         var input =  {
17076             tag: 'input',
17077             id : id,
17078             type : this.inputType,
17079             cls : 'form-control x-combo-noedit',
17080             autocomplete: 'new-password',
17081             placeholder : this.placeholder || '',
17082             readonly : true
17083         };
17084         
17085         if (this.name) {
17086             input.name = this.name;
17087         }
17088         
17089         if (this.size) {
17090             input.cls += ' input-' + this.size;
17091         }
17092         
17093         if (this.disabled) {
17094             input.disabled = true;
17095         }
17096         
17097         var inputblock = {
17098             cls : 'roo-combobox-wrap',
17099             cn : [
17100                 input
17101             ]
17102         };
17103         
17104         if(this.before){
17105             inputblock.cls += ' input-group';
17106             
17107             inputblock.cn.unshift({
17108                 tag :'span',
17109                 cls : 'input-group-addon input-group-prepend input-group-text',
17110                 html : this.before
17111             });
17112         }
17113         
17114         if(this.removable && !this.multiple){
17115             inputblock.cls += ' roo-removable';
17116             
17117             inputblock.cn.push({
17118                 tag: 'button',
17119                 html : 'x',
17120                 cls : 'roo-combo-removable-btn close'
17121             });
17122         }
17123
17124         if(this.hasFeedback && !this.allowBlank){
17125             
17126             inputblock.cls += ' has-feedback';
17127             
17128             inputblock.cn.push({
17129                 tag: 'span',
17130                 cls: 'glyphicon form-control-feedback'
17131             });
17132             
17133         }
17134         
17135         if (this.after) {
17136             
17137             inputblock.cls += (this.before) ? '' : ' input-group';
17138             
17139             inputblock.cn.push({
17140                 tag :'span',
17141                 cls : 'input-group-addon input-group-append input-group-text',
17142                 html : this.after
17143             });
17144         }
17145
17146         
17147         var ibwrap = inputblock;
17148         
17149         if(this.multiple){
17150             ibwrap = {
17151                 tag: 'ul',
17152                 cls: 'roo-select2-choices',
17153                 cn:[
17154                     {
17155                         tag: 'li',
17156                         cls: 'roo-select2-search-field',
17157                         cn: [
17158
17159                             inputblock
17160                         ]
17161                     }
17162                 ]
17163             };
17164         
17165             
17166         }
17167         
17168         var combobox = {
17169             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17170             cn: [
17171                 {
17172                     tag: 'input',
17173                     type : 'hidden',
17174                     cls: 'form-hidden-field'
17175                 },
17176                 ibwrap
17177             ]
17178         };
17179         
17180         if(!this.multiple && this.showToggleBtn){
17181             
17182             var caret = {
17183                 cls: 'caret'
17184             };
17185             
17186             if (this.caret != false) {
17187                 caret = {
17188                      tag: 'i',
17189                      cls: 'fa fa-' + this.caret
17190                 };
17191                 
17192             }
17193             
17194             combobox.cn.push({
17195                 tag :'span',
17196                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17197                 cn : [
17198                     Roo.bootstrap.version == 3 ? caret : '',
17199                     {
17200                         tag: 'span',
17201                         cls: 'combobox-clear',
17202                         cn  : [
17203                             {
17204                                 tag : 'i',
17205                                 cls: 'icon-remove'
17206                             }
17207                         ]
17208                     }
17209                 ]
17210
17211             })
17212         }
17213         
17214         if(this.multiple){
17215             combobox.cls += ' roo-select2-container-multi';
17216         }
17217         
17218         var align = this.labelAlign || this.parentLabelAlign();
17219         
17220         if (align ==='left' && this.fieldLabel.length) {
17221
17222             cfg.cn = [
17223                 {
17224                    tag : 'i',
17225                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17226                    tooltip : 'This field is required'
17227                 },
17228                 {
17229                     tag: 'label',
17230                     cls : 'control-label col-form-label',
17231                     html : this.fieldLabel
17232
17233                 },
17234                 {
17235                     cls : 'roo-combobox-wrap ', 
17236                     cn: [
17237                         combobox
17238                     ]
17239                 }
17240             ];
17241             
17242             var labelCfg = cfg.cn[1];
17243             var contentCfg = cfg.cn[2];
17244             
17245
17246             if(this.indicatorpos == 'right'){
17247                 cfg.cn = [
17248                     {
17249                         tag: 'label',
17250                         'for' :  id,
17251                         cls : 'control-label col-form-label',
17252                         cn : [
17253                             {
17254                                 tag : 'span',
17255                                 html : this.fieldLabel
17256                             },
17257                             {
17258                                 tag : 'i',
17259                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17260                                 tooltip : 'This field is required'
17261                             }
17262                         ]
17263                     },
17264                     {
17265                         cls : "roo-combobox-wrap ",
17266                         cn: [
17267                             combobox
17268                         ]
17269                     }
17270
17271                 ];
17272                 
17273                 labelCfg = cfg.cn[0];
17274                 contentCfg = cfg.cn[1];
17275             }
17276             
17277            
17278             
17279             if(this.labelWidth > 12){
17280                 labelCfg.style = "width: " + this.labelWidth + 'px';
17281             }
17282            
17283             if(this.labelWidth < 13 && this.labelmd == 0){
17284                 this.labelmd = this.labelWidth;
17285             }
17286             
17287             if(this.labellg > 0){
17288                 labelCfg.cls += ' col-lg-' + this.labellg;
17289                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17290             }
17291             
17292             if(this.labelmd > 0){
17293                 labelCfg.cls += ' col-md-' + this.labelmd;
17294                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17295             }
17296             
17297             if(this.labelsm > 0){
17298                 labelCfg.cls += ' col-sm-' + this.labelsm;
17299                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17300             }
17301             
17302             if(this.labelxs > 0){
17303                 labelCfg.cls += ' col-xs-' + this.labelxs;
17304                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17305             }
17306                 
17307                 
17308         } else if ( this.fieldLabel.length) {
17309             cfg.cn = [
17310                 {
17311                    tag : 'i',
17312                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17313                    tooltip : 'This field is required'
17314                 },
17315                 {
17316                     tag: 'label',
17317                     cls : 'control-label',
17318                     html : this.fieldLabel
17319
17320                 },
17321                 {
17322                     cls : '', 
17323                     cn: [
17324                         combobox
17325                     ]
17326                 }
17327             ];
17328             
17329             if(this.indicatorpos == 'right'){
17330                 cfg.cn = [
17331                     {
17332                         tag: 'label',
17333                         cls : 'control-label',
17334                         html : this.fieldLabel,
17335                         cn : [
17336                             {
17337                                tag : 'i',
17338                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17339                                tooltip : 'This field is required'
17340                             }
17341                         ]
17342                     },
17343                     {
17344                         cls : '', 
17345                         cn: [
17346                             combobox
17347                         ]
17348                     }
17349                 ];
17350             }
17351         } else {
17352             cfg.cn = combobox;    
17353         }
17354         
17355         
17356         var settings = this;
17357         
17358         ['xs','sm','md','lg'].map(function(size){
17359             if (settings[size]) {
17360                 cfg.cls += ' col-' + size + '-' + settings[size];
17361             }
17362         });
17363         
17364         return cfg;
17365     },
17366     
17367     initTouchView : function()
17368     {
17369         this.renderTouchView();
17370         
17371         this.touchViewEl.on('scroll', function(){
17372             this.el.dom.scrollTop = 0;
17373         }, this);
17374         
17375         this.originalValue = this.getValue();
17376         
17377         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17378         
17379         this.inputEl().on("click", this.showTouchView, this);
17380         if (this.triggerEl) {
17381             this.triggerEl.on("click", this.showTouchView, this);
17382         }
17383         
17384         
17385         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17386         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17387         
17388         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17389         
17390         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17391         this.store.on('load', this.onTouchViewLoad, this);
17392         this.store.on('loadexception', this.onTouchViewLoadException, this);
17393         
17394         if(this.hiddenName){
17395             
17396             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17397             
17398             this.hiddenField.dom.value =
17399                 this.hiddenValue !== undefined ? this.hiddenValue :
17400                 this.value !== undefined ? this.value : '';
17401         
17402             this.el.dom.removeAttribute('name');
17403             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17404         }
17405         
17406         if(this.multiple){
17407             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17408             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17409         }
17410         
17411         if(this.removable && !this.multiple){
17412             var close = this.closeTriggerEl();
17413             if(close){
17414                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17415                 close.on('click', this.removeBtnClick, this, close);
17416             }
17417         }
17418         /*
17419          * fix the bug in Safari iOS8
17420          */
17421         this.inputEl().on("focus", function(e){
17422             document.activeElement.blur();
17423         }, this);
17424         
17425         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17426         
17427         return;
17428         
17429         
17430     },
17431     
17432     renderTouchView : function()
17433     {
17434         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17435         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436         
17437         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17438         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439         
17440         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17441         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17442         this.touchViewBodyEl.setStyle('overflow', 'auto');
17443         
17444         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17445         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17446         
17447         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17448         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17449         
17450     },
17451     
17452     showTouchView : function()
17453     {
17454         if(this.disabled){
17455             return;
17456         }
17457         
17458         this.touchViewHeaderEl.hide();
17459
17460         if(this.modalTitle.length){
17461             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17462             this.touchViewHeaderEl.show();
17463         }
17464
17465         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17466         this.touchViewEl.show();
17467
17468         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17469         
17470         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17471         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17472
17473         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17474
17475         if(this.modalTitle.length){
17476             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17477         }
17478         
17479         this.touchViewBodyEl.setHeight(bodyHeight);
17480
17481         if(this.animate){
17482             var _this = this;
17483             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17484         }else{
17485             this.touchViewEl.addClass(['in','show']);
17486         }
17487         
17488         if(this._touchViewMask){
17489             Roo.get(document.body).addClass("x-body-masked");
17490             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17491             this._touchViewMask.setStyle('z-index', 10000);
17492             this._touchViewMask.addClass('show');
17493         }
17494         
17495         this.doTouchViewQuery();
17496         
17497     },
17498     
17499     hideTouchView : function()
17500     {
17501         this.touchViewEl.removeClass(['in','show']);
17502
17503         if(this.animate){
17504             var _this = this;
17505             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17506         }else{
17507             this.touchViewEl.setStyle('display', 'none');
17508         }
17509         
17510         if(this._touchViewMask){
17511             this._touchViewMask.removeClass('show');
17512             Roo.get(document.body).removeClass("x-body-masked");
17513         }
17514     },
17515     
17516     setTouchViewValue : function()
17517     {
17518         if(this.multiple){
17519             this.clearItem();
17520         
17521             var _this = this;
17522
17523             Roo.each(this.tickItems, function(o){
17524                 this.addItem(o);
17525             }, this);
17526         }
17527         
17528         this.hideTouchView();
17529     },
17530     
17531     doTouchViewQuery : function()
17532     {
17533         var qe = {
17534             query: '',
17535             forceAll: true,
17536             combo: this,
17537             cancel:false
17538         };
17539         
17540         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17541             return false;
17542         }
17543         
17544         if(!this.alwaysQuery || this.mode == 'local'){
17545             this.onTouchViewLoad();
17546             return;
17547         }
17548         
17549         this.store.load();
17550     },
17551     
17552     onTouchViewBeforeLoad : function(combo,opts)
17553     {
17554         return;
17555     },
17556
17557     // private
17558     onTouchViewLoad : function()
17559     {
17560         if(this.store.getCount() < 1){
17561             this.onTouchViewEmptyResults();
17562             return;
17563         }
17564         
17565         this.clearTouchView();
17566         
17567         var rawValue = this.getRawValue();
17568         
17569         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17570         
17571         this.tickItems = [];
17572         
17573         this.store.data.each(function(d, rowIndex){
17574             var row = this.touchViewListGroup.createChild(template);
17575             
17576             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17577                 row.addClass(d.data.cls);
17578             }
17579             
17580             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17581                 var cfg = {
17582                     data : d.data,
17583                     html : d.data[this.displayField]
17584                 };
17585                 
17586                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17587                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17588                 }
17589             }
17590             row.removeClass('selected');
17591             if(!this.multiple && this.valueField &&
17592                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17593             {
17594                 // radio buttons..
17595                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17596                 row.addClass('selected');
17597             }
17598             
17599             if(this.multiple && this.valueField &&
17600                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17601             {
17602                 
17603                 // checkboxes...
17604                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17605                 this.tickItems.push(d.data);
17606             }
17607             
17608             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17609             
17610         }, this);
17611         
17612         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17613         
17614         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17615
17616         if(this.modalTitle.length){
17617             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17618         }
17619
17620         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17621         
17622         if(this.mobile_restrict_height && listHeight < bodyHeight){
17623             this.touchViewBodyEl.setHeight(listHeight);
17624         }
17625         
17626         var _this = this;
17627         
17628         if(firstChecked && listHeight > bodyHeight){
17629             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17630         }
17631         
17632     },
17633     
17634     onTouchViewLoadException : function()
17635     {
17636         this.hideTouchView();
17637     },
17638     
17639     onTouchViewEmptyResults : function()
17640     {
17641         this.clearTouchView();
17642         
17643         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17644         
17645         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17646         
17647     },
17648     
17649     clearTouchView : function()
17650     {
17651         this.touchViewListGroup.dom.innerHTML = '';
17652     },
17653     
17654     onTouchViewClick : function(e, el, o)
17655     {
17656         e.preventDefault();
17657         
17658         var row = o.row;
17659         var rowIndex = o.rowIndex;
17660         
17661         var r = this.store.getAt(rowIndex);
17662         
17663         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17664             
17665             if(!this.multiple){
17666                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17667                     c.dom.removeAttribute('checked');
17668                 }, this);
17669
17670                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17671
17672                 this.setFromData(r.data);
17673
17674                 var close = this.closeTriggerEl();
17675
17676                 if(close){
17677                     close.show();
17678                 }
17679
17680                 this.hideTouchView();
17681
17682                 this.fireEvent('select', this, r, rowIndex);
17683
17684                 return;
17685             }
17686
17687             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17688                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17689                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17690                 return;
17691             }
17692
17693             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17694             this.addItem(r.data);
17695             this.tickItems.push(r.data);
17696         }
17697     },
17698     
17699     getAutoCreateNativeIOS : function()
17700     {
17701         var cfg = {
17702             cls: 'form-group' //input-group,
17703         };
17704         
17705         var combobox =  {
17706             tag: 'select',
17707             cls : 'roo-ios-select'
17708         };
17709         
17710         if (this.name) {
17711             combobox.name = this.name;
17712         }
17713         
17714         if (this.disabled) {
17715             combobox.disabled = true;
17716         }
17717         
17718         var settings = this;
17719         
17720         ['xs','sm','md','lg'].map(function(size){
17721             if (settings[size]) {
17722                 cfg.cls += ' col-' + size + '-' + settings[size];
17723             }
17724         });
17725         
17726         cfg.cn = combobox;
17727         
17728         return cfg;
17729         
17730     },
17731     
17732     initIOSView : function()
17733     {
17734         this.store.on('load', this.onIOSViewLoad, this);
17735         
17736         return;
17737     },
17738     
17739     onIOSViewLoad : function()
17740     {
17741         if(this.store.getCount() < 1){
17742             return;
17743         }
17744         
17745         this.clearIOSView();
17746         
17747         if(this.allowBlank) {
17748             
17749             var default_text = '-- SELECT --';
17750             
17751             if(this.placeholder.length){
17752                 default_text = this.placeholder;
17753             }
17754             
17755             if(this.emptyTitle.length){
17756                 default_text += ' - ' + this.emptyTitle + ' -';
17757             }
17758             
17759             var opt = this.inputEl().createChild({
17760                 tag: 'option',
17761                 value : 0,
17762                 html : default_text
17763             });
17764             
17765             var o = {};
17766             o[this.valueField] = 0;
17767             o[this.displayField] = default_text;
17768             
17769             this.ios_options.push({
17770                 data : o,
17771                 el : opt
17772             });
17773             
17774         }
17775         
17776         this.store.data.each(function(d, rowIndex){
17777             
17778             var html = '';
17779             
17780             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17781                 html = d.data[this.displayField];
17782             }
17783             
17784             var value = '';
17785             
17786             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17787                 value = d.data[this.valueField];
17788             }
17789             
17790             var option = {
17791                 tag: 'option',
17792                 value : value,
17793                 html : html
17794             };
17795             
17796             if(this.value == d.data[this.valueField]){
17797                 option['selected'] = true;
17798             }
17799             
17800             var opt = this.inputEl().createChild(option);
17801             
17802             this.ios_options.push({
17803                 data : d.data,
17804                 el : opt
17805             });
17806             
17807         }, this);
17808         
17809         this.inputEl().on('change', function(){
17810            this.fireEvent('select', this);
17811         }, this);
17812         
17813     },
17814     
17815     clearIOSView: function()
17816     {
17817         this.inputEl().dom.innerHTML = '';
17818         
17819         this.ios_options = [];
17820     },
17821     
17822     setIOSValue: function(v)
17823     {
17824         this.value = v;
17825         
17826         if(!this.ios_options){
17827             return;
17828         }
17829         
17830         Roo.each(this.ios_options, function(opts){
17831            
17832            opts.el.dom.removeAttribute('selected');
17833            
17834            if(opts.data[this.valueField] != v){
17835                return;
17836            }
17837            
17838            opts.el.dom.setAttribute('selected', true);
17839            
17840         }, this);
17841     }
17842
17843     /** 
17844     * @cfg {Boolean} grow 
17845     * @hide 
17846     */
17847     /** 
17848     * @cfg {Number} growMin 
17849     * @hide 
17850     */
17851     /** 
17852     * @cfg {Number} growMax 
17853     * @hide 
17854     */
17855     /**
17856      * @hide
17857      * @method autoSize
17858      */
17859 });
17860
17861 Roo.apply(Roo.bootstrap.ComboBox,  {
17862     
17863     header : {
17864         tag: 'div',
17865         cls: 'modal-header',
17866         cn: [
17867             {
17868                 tag: 'h4',
17869                 cls: 'modal-title'
17870             }
17871         ]
17872     },
17873     
17874     body : {
17875         tag: 'div',
17876         cls: 'modal-body',
17877         cn: [
17878             {
17879                 tag: 'ul',
17880                 cls: 'list-group'
17881             }
17882         ]
17883     },
17884     
17885     listItemRadio : {
17886         tag: 'li',
17887         cls: 'list-group-item',
17888         cn: [
17889             {
17890                 tag: 'span',
17891                 cls: 'roo-combobox-list-group-item-value'
17892             },
17893             {
17894                 tag: 'div',
17895                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17896                 cn: [
17897                     {
17898                         tag: 'input',
17899                         type: 'radio'
17900                     },
17901                     {
17902                         tag: 'label'
17903                     }
17904                 ]
17905             }
17906         ]
17907     },
17908     
17909     listItemCheckbox : {
17910         tag: 'li',
17911         cls: 'list-group-item',
17912         cn: [
17913             {
17914                 tag: 'span',
17915                 cls: 'roo-combobox-list-group-item-value'
17916             },
17917             {
17918                 tag: 'div',
17919                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17920                 cn: [
17921                     {
17922                         tag: 'input',
17923                         type: 'checkbox'
17924                     },
17925                     {
17926                         tag: 'label'
17927                     }
17928                 ]
17929             }
17930         ]
17931     },
17932     
17933     emptyResult : {
17934         tag: 'div',
17935         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17936     },
17937     
17938     footer : {
17939         tag: 'div',
17940         cls: 'modal-footer',
17941         cn: [
17942             {
17943                 tag: 'div',
17944                 cls: 'row',
17945                 cn: [
17946                     {
17947                         tag: 'div',
17948                         cls: 'col-xs-6 text-left',
17949                         cn: {
17950                             tag: 'button',
17951                             cls: 'btn btn-danger roo-touch-view-cancel',
17952                             html: 'Cancel'
17953                         }
17954                     },
17955                     {
17956                         tag: 'div',
17957                         cls: 'col-xs-6 text-right',
17958                         cn: {
17959                             tag: 'button',
17960                             cls: 'btn btn-success roo-touch-view-ok',
17961                             html: 'OK'
17962                         }
17963                     }
17964                 ]
17965             }
17966         ]
17967         
17968     }
17969 });
17970
17971 Roo.apply(Roo.bootstrap.ComboBox,  {
17972     
17973     touchViewTemplate : {
17974         tag: 'div',
17975         cls: 'modal fade roo-combobox-touch-view',
17976         cn: [
17977             {
17978                 tag: 'div',
17979                 cls: 'modal-dialog',
17980                 style : 'position:fixed', // we have to fix position....
17981                 cn: [
17982                     {
17983                         tag: 'div',
17984                         cls: 'modal-content',
17985                         cn: [
17986                             Roo.bootstrap.ComboBox.header,
17987                             Roo.bootstrap.ComboBox.body,
17988                             Roo.bootstrap.ComboBox.footer
17989                         ]
17990                     }
17991                 ]
17992             }
17993         ]
17994     }
17995 });/*
17996  * Based on:
17997  * Ext JS Library 1.1.1
17998  * Copyright(c) 2006-2007, Ext JS, LLC.
17999  *
18000  * Originally Released Under LGPL - original licence link has changed is not relivant.
18001  *
18002  * Fork - LGPL
18003  * <script type="text/javascript">
18004  */
18005
18006 /**
18007  * @class Roo.View
18008  * @extends Roo.util.Observable
18009  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18010  * This class also supports single and multi selection modes. <br>
18011  * Create a data model bound view:
18012  <pre><code>
18013  var store = new Roo.data.Store(...);
18014
18015  var view = new Roo.View({
18016     el : "my-element",
18017     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18018  
18019     singleSelect: true,
18020     selectedClass: "ydataview-selected",
18021     store: store
18022  });
18023
18024  // listen for node click?
18025  view.on("click", function(vw, index, node, e){
18026  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18027  });
18028
18029  // load XML data
18030  dataModel.load("foobar.xml");
18031  </code></pre>
18032  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18033  * <br><br>
18034  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18035  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18036  * 
18037  * Note: old style constructor is still suported (container, template, config)
18038  * 
18039  * @constructor
18040  * Create a new View
18041  * @param {Object} config The config object
18042  * 
18043  */
18044 Roo.View = function(config, depreciated_tpl, depreciated_config){
18045     
18046     this.parent = false;
18047     
18048     if (typeof(depreciated_tpl) == 'undefined') {
18049         // new way.. - universal constructor.
18050         Roo.apply(this, config);
18051         this.el  = Roo.get(this.el);
18052     } else {
18053         // old format..
18054         this.el  = Roo.get(config);
18055         this.tpl = depreciated_tpl;
18056         Roo.apply(this, depreciated_config);
18057     }
18058     this.wrapEl  = this.el.wrap().wrap();
18059     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18060     
18061     
18062     if(typeof(this.tpl) == "string"){
18063         this.tpl = new Roo.Template(this.tpl);
18064     } else {
18065         // support xtype ctors..
18066         this.tpl = new Roo.factory(this.tpl, Roo);
18067     }
18068     
18069     
18070     this.tpl.compile();
18071     
18072     /** @private */
18073     this.addEvents({
18074         /**
18075          * @event beforeclick
18076          * Fires before a click is processed. Returns false to cancel the default action.
18077          * @param {Roo.View} this
18078          * @param {Number} index The index of the target node
18079          * @param {HTMLElement} node The target node
18080          * @param {Roo.EventObject} e The raw event object
18081          */
18082             "beforeclick" : true,
18083         /**
18084          * @event click
18085          * Fires when a template node is clicked.
18086          * @param {Roo.View} this
18087          * @param {Number} index The index of the target node
18088          * @param {HTMLElement} node The target node
18089          * @param {Roo.EventObject} e The raw event object
18090          */
18091             "click" : true,
18092         /**
18093          * @event dblclick
18094          * Fires when a template node is double clicked.
18095          * @param {Roo.View} this
18096          * @param {Number} index The index of the target node
18097          * @param {HTMLElement} node The target node
18098          * @param {Roo.EventObject} e The raw event object
18099          */
18100             "dblclick" : true,
18101         /**
18102          * @event contextmenu
18103          * Fires when a template node is right clicked.
18104          * @param {Roo.View} this
18105          * @param {Number} index The index of the target node
18106          * @param {HTMLElement} node The target node
18107          * @param {Roo.EventObject} e The raw event object
18108          */
18109             "contextmenu" : true,
18110         /**
18111          * @event selectionchange
18112          * Fires when the selected nodes change.
18113          * @param {Roo.View} this
18114          * @param {Array} selections Array of the selected nodes
18115          */
18116             "selectionchange" : true,
18117     
18118         /**
18119          * @event beforeselect
18120          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18121          * @param {Roo.View} this
18122          * @param {HTMLElement} node The node to be selected
18123          * @param {Array} selections Array of currently selected nodes
18124          */
18125             "beforeselect" : true,
18126         /**
18127          * @event preparedata
18128          * Fires on every row to render, to allow you to change the data.
18129          * @param {Roo.View} this
18130          * @param {Object} data to be rendered (change this)
18131          */
18132           "preparedata" : true
18133           
18134           
18135         });
18136
18137
18138
18139     this.el.on({
18140         "click": this.onClick,
18141         "dblclick": this.onDblClick,
18142         "contextmenu": this.onContextMenu,
18143         scope:this
18144     });
18145
18146     this.selections = [];
18147     this.nodes = [];
18148     this.cmp = new Roo.CompositeElementLite([]);
18149     if(this.store){
18150         this.store = Roo.factory(this.store, Roo.data);
18151         this.setStore(this.store, true);
18152     }
18153     
18154     if ( this.footer && this.footer.xtype) {
18155            
18156          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18157         
18158         this.footer.dataSource = this.store;
18159         this.footer.container = fctr;
18160         this.footer = Roo.factory(this.footer, Roo);
18161         fctr.insertFirst(this.el);
18162         
18163         // this is a bit insane - as the paging toolbar seems to detach the el..
18164 //        dom.parentNode.parentNode.parentNode
18165          // they get detached?
18166     }
18167     
18168     
18169     Roo.View.superclass.constructor.call(this);
18170     
18171     
18172 };
18173
18174 Roo.extend(Roo.View, Roo.util.Observable, {
18175     
18176      /**
18177      * @cfg {Roo.data.Store} store Data store to load data from.
18178      */
18179     store : false,
18180     
18181     /**
18182      * @cfg {String|Roo.Element} el The container element.
18183      */
18184     el : '',
18185     
18186     /**
18187      * @cfg {String|Roo.Template} tpl The template used by this View 
18188      */
18189     tpl : false,
18190     /**
18191      * @cfg {String} dataName the named area of the template to use as the data area
18192      *                          Works with domtemplates roo-name="name"
18193      */
18194     dataName: false,
18195     /**
18196      * @cfg {String} selectedClass The css class to add to selected nodes
18197      */
18198     selectedClass : "x-view-selected",
18199      /**
18200      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18201      */
18202     emptyText : "",
18203     
18204     /**
18205      * @cfg {String} text to display on mask (default Loading)
18206      */
18207     mask : false,
18208     /**
18209      * @cfg {Boolean} multiSelect Allow multiple selection
18210      */
18211     multiSelect : false,
18212     /**
18213      * @cfg {Boolean} singleSelect Allow single selection
18214      */
18215     singleSelect:  false,
18216     
18217     /**
18218      * @cfg {Boolean} toggleSelect - selecting 
18219      */
18220     toggleSelect : false,
18221     
18222     /**
18223      * @cfg {Boolean} tickable - selecting 
18224      */
18225     tickable : false,
18226     
18227     /**
18228      * Returns the element this view is bound to.
18229      * @return {Roo.Element}
18230      */
18231     getEl : function(){
18232         return this.wrapEl;
18233     },
18234     
18235     
18236
18237     /**
18238      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18239      */
18240     refresh : function(){
18241         //Roo.log('refresh');
18242         var t = this.tpl;
18243         
18244         // if we are using something like 'domtemplate', then
18245         // the what gets used is:
18246         // t.applySubtemplate(NAME, data, wrapping data..)
18247         // the outer template then get' applied with
18248         //     the store 'extra data'
18249         // and the body get's added to the
18250         //      roo-name="data" node?
18251         //      <span class='roo-tpl-{name}'></span> ?????
18252         
18253         
18254         
18255         this.clearSelections();
18256         this.el.update("");
18257         var html = [];
18258         var records = this.store.getRange();
18259         if(records.length < 1) {
18260             
18261             // is this valid??  = should it render a template??
18262             
18263             this.el.update(this.emptyText);
18264             return;
18265         }
18266         var el = this.el;
18267         if (this.dataName) {
18268             this.el.update(t.apply(this.store.meta)); //????
18269             el = this.el.child('.roo-tpl-' + this.dataName);
18270         }
18271         
18272         for(var i = 0, len = records.length; i < len; i++){
18273             var data = this.prepareData(records[i].data, i, records[i]);
18274             this.fireEvent("preparedata", this, data, i, records[i]);
18275             
18276             var d = Roo.apply({}, data);
18277             
18278             if(this.tickable){
18279                 Roo.apply(d, {'roo-id' : Roo.id()});
18280                 
18281                 var _this = this;
18282             
18283                 Roo.each(this.parent.item, function(item){
18284                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18285                         return;
18286                     }
18287                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18288                 });
18289             }
18290             
18291             html[html.length] = Roo.util.Format.trim(
18292                 this.dataName ?
18293                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18294                     t.apply(d)
18295             );
18296         }
18297         
18298         
18299         
18300         el.update(html.join(""));
18301         this.nodes = el.dom.childNodes;
18302         this.updateIndexes(0);
18303     },
18304     
18305
18306     /**
18307      * Function to override to reformat the data that is sent to
18308      * the template for each node.
18309      * DEPRICATED - use the preparedata event handler.
18310      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18311      * a JSON object for an UpdateManager bound view).
18312      */
18313     prepareData : function(data, index, record)
18314     {
18315         this.fireEvent("preparedata", this, data, index, record);
18316         return data;
18317     },
18318
18319     onUpdate : function(ds, record){
18320         // Roo.log('on update');   
18321         this.clearSelections();
18322         var index = this.store.indexOf(record);
18323         var n = this.nodes[index];
18324         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18325         n.parentNode.removeChild(n);
18326         this.updateIndexes(index, index);
18327     },
18328
18329     
18330     
18331 // --------- FIXME     
18332     onAdd : function(ds, records, index)
18333     {
18334         //Roo.log(['on Add', ds, records, index] );        
18335         this.clearSelections();
18336         if(this.nodes.length == 0){
18337             this.refresh();
18338             return;
18339         }
18340         var n = this.nodes[index];
18341         for(var i = 0, len = records.length; i < len; i++){
18342             var d = this.prepareData(records[i].data, i, records[i]);
18343             if(n){
18344                 this.tpl.insertBefore(n, d);
18345             }else{
18346                 
18347                 this.tpl.append(this.el, d);
18348             }
18349         }
18350         this.updateIndexes(index);
18351     },
18352
18353     onRemove : function(ds, record, index){
18354        // Roo.log('onRemove');
18355         this.clearSelections();
18356         var el = this.dataName  ?
18357             this.el.child('.roo-tpl-' + this.dataName) :
18358             this.el; 
18359         
18360         el.dom.removeChild(this.nodes[index]);
18361         this.updateIndexes(index);
18362     },
18363
18364     /**
18365      * Refresh an individual node.
18366      * @param {Number} index
18367      */
18368     refreshNode : function(index){
18369         this.onUpdate(this.store, this.store.getAt(index));
18370     },
18371
18372     updateIndexes : function(startIndex, endIndex){
18373         var ns = this.nodes;
18374         startIndex = startIndex || 0;
18375         endIndex = endIndex || ns.length - 1;
18376         for(var i = startIndex; i <= endIndex; i++){
18377             ns[i].nodeIndex = i;
18378         }
18379     },
18380
18381     /**
18382      * Changes the data store this view uses and refresh the view.
18383      * @param {Store} store
18384      */
18385     setStore : function(store, initial){
18386         if(!initial && this.store){
18387             this.store.un("datachanged", this.refresh);
18388             this.store.un("add", this.onAdd);
18389             this.store.un("remove", this.onRemove);
18390             this.store.un("update", this.onUpdate);
18391             this.store.un("clear", this.refresh);
18392             this.store.un("beforeload", this.onBeforeLoad);
18393             this.store.un("load", this.onLoad);
18394             this.store.un("loadexception", this.onLoad);
18395         }
18396         if(store){
18397           
18398             store.on("datachanged", this.refresh, this);
18399             store.on("add", this.onAdd, this);
18400             store.on("remove", this.onRemove, this);
18401             store.on("update", this.onUpdate, this);
18402             store.on("clear", this.refresh, this);
18403             store.on("beforeload", this.onBeforeLoad, this);
18404             store.on("load", this.onLoad, this);
18405             store.on("loadexception", this.onLoad, this);
18406         }
18407         
18408         if(store){
18409             this.refresh();
18410         }
18411     },
18412     /**
18413      * onbeforeLoad - masks the loading area.
18414      *
18415      */
18416     onBeforeLoad : function(store,opts)
18417     {
18418          //Roo.log('onBeforeLoad');   
18419         if (!opts.add) {
18420             this.el.update("");
18421         }
18422         this.el.mask(this.mask ? this.mask : "Loading" ); 
18423     },
18424     onLoad : function ()
18425     {
18426         this.el.unmask();
18427     },
18428     
18429
18430     /**
18431      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18432      * @param {HTMLElement} node
18433      * @return {HTMLElement} The template node
18434      */
18435     findItemFromChild : function(node){
18436         var el = this.dataName  ?
18437             this.el.child('.roo-tpl-' + this.dataName,true) :
18438             this.el.dom; 
18439         
18440         if(!node || node.parentNode == el){
18441                     return node;
18442             }
18443             var p = node.parentNode;
18444             while(p && p != el){
18445             if(p.parentNode == el){
18446                 return p;
18447             }
18448             p = p.parentNode;
18449         }
18450             return null;
18451     },
18452
18453     /** @ignore */
18454     onClick : function(e){
18455         var item = this.findItemFromChild(e.getTarget());
18456         if(item){
18457             var index = this.indexOf(item);
18458             if(this.onItemClick(item, index, e) !== false){
18459                 this.fireEvent("click", this, index, item, e);
18460             }
18461         }else{
18462             this.clearSelections();
18463         }
18464     },
18465
18466     /** @ignore */
18467     onContextMenu : function(e){
18468         var item = this.findItemFromChild(e.getTarget());
18469         if(item){
18470             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18471         }
18472     },
18473
18474     /** @ignore */
18475     onDblClick : function(e){
18476         var item = this.findItemFromChild(e.getTarget());
18477         if(item){
18478             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18479         }
18480     },
18481
18482     onItemClick : function(item, index, e)
18483     {
18484         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18485             return false;
18486         }
18487         if (this.toggleSelect) {
18488             var m = this.isSelected(item) ? 'unselect' : 'select';
18489             //Roo.log(m);
18490             var _t = this;
18491             _t[m](item, true, false);
18492             return true;
18493         }
18494         if(this.multiSelect || this.singleSelect){
18495             if(this.multiSelect && e.shiftKey && this.lastSelection){
18496                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18497             }else{
18498                 this.select(item, this.multiSelect && e.ctrlKey);
18499                 this.lastSelection = item;
18500             }
18501             
18502             if(!this.tickable){
18503                 e.preventDefault();
18504             }
18505             
18506         }
18507         return true;
18508     },
18509
18510     /**
18511      * Get the number of selected nodes.
18512      * @return {Number}
18513      */
18514     getSelectionCount : function(){
18515         return this.selections.length;
18516     },
18517
18518     /**
18519      * Get the currently selected nodes.
18520      * @return {Array} An array of HTMLElements
18521      */
18522     getSelectedNodes : function(){
18523         return this.selections;
18524     },
18525
18526     /**
18527      * Get the indexes of the selected nodes.
18528      * @return {Array}
18529      */
18530     getSelectedIndexes : function(){
18531         var indexes = [], s = this.selections;
18532         for(var i = 0, len = s.length; i < len; i++){
18533             indexes.push(s[i].nodeIndex);
18534         }
18535         return indexes;
18536     },
18537
18538     /**
18539      * Clear all selections
18540      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18541      */
18542     clearSelections : function(suppressEvent){
18543         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18544             this.cmp.elements = this.selections;
18545             this.cmp.removeClass(this.selectedClass);
18546             this.selections = [];
18547             if(!suppressEvent){
18548                 this.fireEvent("selectionchange", this, this.selections);
18549             }
18550         }
18551     },
18552
18553     /**
18554      * Returns true if the passed node is selected
18555      * @param {HTMLElement/Number} node The node or node index
18556      * @return {Boolean}
18557      */
18558     isSelected : function(node){
18559         var s = this.selections;
18560         if(s.length < 1){
18561             return false;
18562         }
18563         node = this.getNode(node);
18564         return s.indexOf(node) !== -1;
18565     },
18566
18567     /**
18568      * Selects nodes.
18569      * @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
18570      * @param {Boolean} keepExisting (optional) true to keep existing selections
18571      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18572      */
18573     select : function(nodeInfo, keepExisting, suppressEvent){
18574         if(nodeInfo instanceof Array){
18575             if(!keepExisting){
18576                 this.clearSelections(true);
18577             }
18578             for(var i = 0, len = nodeInfo.length; i < len; i++){
18579                 this.select(nodeInfo[i], true, true);
18580             }
18581             return;
18582         } 
18583         var node = this.getNode(nodeInfo);
18584         if(!node || this.isSelected(node)){
18585             return; // already selected.
18586         }
18587         if(!keepExisting){
18588             this.clearSelections(true);
18589         }
18590         
18591         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18592             Roo.fly(node).addClass(this.selectedClass);
18593             this.selections.push(node);
18594             if(!suppressEvent){
18595                 this.fireEvent("selectionchange", this, this.selections);
18596             }
18597         }
18598         
18599         
18600     },
18601       /**
18602      * Unselects nodes.
18603      * @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
18604      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18605      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18606      */
18607     unselect : function(nodeInfo, keepExisting, suppressEvent)
18608     {
18609         if(nodeInfo instanceof Array){
18610             Roo.each(this.selections, function(s) {
18611                 this.unselect(s, nodeInfo);
18612             }, this);
18613             return;
18614         }
18615         var node = this.getNode(nodeInfo);
18616         if(!node || !this.isSelected(node)){
18617             //Roo.log("not selected");
18618             return; // not selected.
18619         }
18620         // fireevent???
18621         var ns = [];
18622         Roo.each(this.selections, function(s) {
18623             if (s == node ) {
18624                 Roo.fly(node).removeClass(this.selectedClass);
18625
18626                 return;
18627             }
18628             ns.push(s);
18629         },this);
18630         
18631         this.selections= ns;
18632         this.fireEvent("selectionchange", this, this.selections);
18633     },
18634
18635     /**
18636      * Gets a template node.
18637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18638      * @return {HTMLElement} The node or null if it wasn't found
18639      */
18640     getNode : function(nodeInfo){
18641         if(typeof nodeInfo == "string"){
18642             return document.getElementById(nodeInfo);
18643         }else if(typeof nodeInfo == "number"){
18644             return this.nodes[nodeInfo];
18645         }
18646         return nodeInfo;
18647     },
18648
18649     /**
18650      * Gets a range template nodes.
18651      * @param {Number} startIndex
18652      * @param {Number} endIndex
18653      * @return {Array} An array of nodes
18654      */
18655     getNodes : function(start, end){
18656         var ns = this.nodes;
18657         start = start || 0;
18658         end = typeof end == "undefined" ? ns.length - 1 : end;
18659         var nodes = [];
18660         if(start <= end){
18661             for(var i = start; i <= end; i++){
18662                 nodes.push(ns[i]);
18663             }
18664         } else{
18665             for(var i = start; i >= end; i--){
18666                 nodes.push(ns[i]);
18667             }
18668         }
18669         return nodes;
18670     },
18671
18672     /**
18673      * Finds the index of the passed node
18674      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18675      * @return {Number} The index of the node or -1
18676      */
18677     indexOf : function(node){
18678         node = this.getNode(node);
18679         if(typeof node.nodeIndex == "number"){
18680             return node.nodeIndex;
18681         }
18682         var ns = this.nodes;
18683         for(var i = 0, len = ns.length; i < len; i++){
18684             if(ns[i] == node){
18685                 return i;
18686             }
18687         }
18688         return -1;
18689     }
18690 });
18691 /*
18692  * - LGPL
18693  *
18694  * based on jquery fullcalendar
18695  * 
18696  */
18697
18698 Roo.bootstrap = Roo.bootstrap || {};
18699 /**
18700  * @class Roo.bootstrap.Calendar
18701  * @extends Roo.bootstrap.Component
18702  * Bootstrap Calendar class
18703  * @cfg {Boolean} loadMask (true|false) default false
18704  * @cfg {Object} header generate the user specific header of the calendar, default false
18705
18706  * @constructor
18707  * Create a new Container
18708  * @param {Object} config The config object
18709  */
18710
18711
18712
18713 Roo.bootstrap.Calendar = function(config){
18714     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18715      this.addEvents({
18716         /**
18717              * @event select
18718              * Fires when a date is selected
18719              * @param {DatePicker} this
18720              * @param {Date} date The selected date
18721              */
18722         'select': true,
18723         /**
18724              * @event monthchange
18725              * Fires when the displayed month changes 
18726              * @param {DatePicker} this
18727              * @param {Date} date The selected month
18728              */
18729         'monthchange': true,
18730         /**
18731              * @event evententer
18732              * Fires when mouse over an event
18733              * @param {Calendar} this
18734              * @param {event} Event
18735              */
18736         'evententer': true,
18737         /**
18738              * @event eventleave
18739              * Fires when the mouse leaves an
18740              * @param {Calendar} this
18741              * @param {event}
18742              */
18743         'eventleave': true,
18744         /**
18745              * @event eventclick
18746              * Fires when the mouse click an
18747              * @param {Calendar} this
18748              * @param {event}
18749              */
18750         'eventclick': true
18751         
18752     });
18753
18754 };
18755
18756 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18757     
18758      /**
18759      * @cfg {Number} startDay
18760      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18761      */
18762     startDay : 0,
18763     
18764     loadMask : false,
18765     
18766     header : false,
18767       
18768     getAutoCreate : function(){
18769         
18770         
18771         var fc_button = function(name, corner, style, content ) {
18772             return Roo.apply({},{
18773                 tag : 'span',
18774                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18775                          (corner.length ?
18776                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18777                             ''
18778                         ),
18779                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18780                 unselectable: 'on'
18781             });
18782         };
18783         
18784         var header = {};
18785         
18786         if(!this.header){
18787             header = {
18788                 tag : 'table',
18789                 cls : 'fc-header',
18790                 style : 'width:100%',
18791                 cn : [
18792                     {
18793                         tag: 'tr',
18794                         cn : [
18795                             {
18796                                 tag : 'td',
18797                                 cls : 'fc-header-left',
18798                                 cn : [
18799                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18800                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18801                                     { tag: 'span', cls: 'fc-header-space' },
18802                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18803
18804
18805                                 ]
18806                             },
18807
18808                             {
18809                                 tag : 'td',
18810                                 cls : 'fc-header-center',
18811                                 cn : [
18812                                     {
18813                                         tag: 'span',
18814                                         cls: 'fc-header-title',
18815                                         cn : {
18816                                             tag: 'H2',
18817                                             html : 'month / year'
18818                                         }
18819                                     }
18820
18821                                 ]
18822                             },
18823                             {
18824                                 tag : 'td',
18825                                 cls : 'fc-header-right',
18826                                 cn : [
18827                               /*      fc_button('month', 'left', '', 'month' ),
18828                                     fc_button('week', '', '', 'week' ),
18829                                     fc_button('day', 'right', '', 'day' )
18830                                 */    
18831
18832                                 ]
18833                             }
18834
18835                         ]
18836                     }
18837                 ]
18838             };
18839         }
18840         
18841         header = this.header;
18842         
18843        
18844         var cal_heads = function() {
18845             var ret = [];
18846             // fixme - handle this.
18847             
18848             for (var i =0; i < Date.dayNames.length; i++) {
18849                 var d = Date.dayNames[i];
18850                 ret.push({
18851                     tag: 'th',
18852                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18853                     html : d.substring(0,3)
18854                 });
18855                 
18856             }
18857             ret[0].cls += ' fc-first';
18858             ret[6].cls += ' fc-last';
18859             return ret;
18860         };
18861         var cal_cell = function(n) {
18862             return  {
18863                 tag: 'td',
18864                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18865                 cn : [
18866                     {
18867                         cn : [
18868                             {
18869                                 cls: 'fc-day-number',
18870                                 html: 'D'
18871                             },
18872                             {
18873                                 cls: 'fc-day-content',
18874                              
18875                                 cn : [
18876                                      {
18877                                         style: 'position: relative;' // height: 17px;
18878                                     }
18879                                 ]
18880                             }
18881                             
18882                             
18883                         ]
18884                     }
18885                 ]
18886                 
18887             }
18888         };
18889         var cal_rows = function() {
18890             
18891             var ret = [];
18892             for (var r = 0; r < 6; r++) {
18893                 var row= {
18894                     tag : 'tr',
18895                     cls : 'fc-week',
18896                     cn : []
18897                 };
18898                 
18899                 for (var i =0; i < Date.dayNames.length; i++) {
18900                     var d = Date.dayNames[i];
18901                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18902
18903                 }
18904                 row.cn[0].cls+=' fc-first';
18905                 row.cn[0].cn[0].style = 'min-height:90px';
18906                 row.cn[6].cls+=' fc-last';
18907                 ret.push(row);
18908                 
18909             }
18910             ret[0].cls += ' fc-first';
18911             ret[4].cls += ' fc-prev-last';
18912             ret[5].cls += ' fc-last';
18913             return ret;
18914             
18915         };
18916         
18917         var cal_table = {
18918             tag: 'table',
18919             cls: 'fc-border-separate',
18920             style : 'width:100%',
18921             cellspacing  : 0,
18922             cn : [
18923                 { 
18924                     tag: 'thead',
18925                     cn : [
18926                         { 
18927                             tag: 'tr',
18928                             cls : 'fc-first fc-last',
18929                             cn : cal_heads()
18930                         }
18931                     ]
18932                 },
18933                 { 
18934                     tag: 'tbody',
18935                     cn : cal_rows()
18936                 }
18937                   
18938             ]
18939         };
18940          
18941          var cfg = {
18942             cls : 'fc fc-ltr',
18943             cn : [
18944                 header,
18945                 {
18946                     cls : 'fc-content',
18947                     style : "position: relative;",
18948                     cn : [
18949                         {
18950                             cls : 'fc-view fc-view-month fc-grid',
18951                             style : 'position: relative',
18952                             unselectable : 'on',
18953                             cn : [
18954                                 {
18955                                     cls : 'fc-event-container',
18956                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18957                                 },
18958                                 cal_table
18959                             ]
18960                         }
18961                     ]
18962     
18963                 }
18964            ] 
18965             
18966         };
18967         
18968          
18969         
18970         return cfg;
18971     },
18972     
18973     
18974     initEvents : function()
18975     {
18976         if(!this.store){
18977             throw "can not find store for calendar";
18978         }
18979         
18980         var mark = {
18981             tag: "div",
18982             cls:"x-dlg-mask",
18983             style: "text-align:center",
18984             cn: [
18985                 {
18986                     tag: "div",
18987                     style: "background-color:white;width:50%;margin:250 auto",
18988                     cn: [
18989                         {
18990                             tag: "img",
18991                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18992                         },
18993                         {
18994                             tag: "span",
18995                             html: "Loading"
18996                         }
18997                         
18998                     ]
18999                 }
19000             ]
19001         };
19002         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19003         
19004         var size = this.el.select('.fc-content', true).first().getSize();
19005         this.maskEl.setSize(size.width, size.height);
19006         this.maskEl.enableDisplayMode("block");
19007         if(!this.loadMask){
19008             this.maskEl.hide();
19009         }
19010         
19011         this.store = Roo.factory(this.store, Roo.data);
19012         this.store.on('load', this.onLoad, this);
19013         this.store.on('beforeload', this.onBeforeLoad, this);
19014         
19015         this.resize();
19016         
19017         this.cells = this.el.select('.fc-day',true);
19018         //Roo.log(this.cells);
19019         this.textNodes = this.el.query('.fc-day-number');
19020         this.cells.addClassOnOver('fc-state-hover');
19021         
19022         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19023         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19024         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19025         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19026         
19027         this.on('monthchange', this.onMonthChange, this);
19028         
19029         this.update(new Date().clearTime());
19030     },
19031     
19032     resize : function() {
19033         var sz  = this.el.getSize();
19034         
19035         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19036         this.el.select('.fc-day-content div',true).setHeight(34);
19037     },
19038     
19039     
19040     // private
19041     showPrevMonth : function(e){
19042         this.update(this.activeDate.add("mo", -1));
19043     },
19044     showToday : function(e){
19045         this.update(new Date().clearTime());
19046     },
19047     // private
19048     showNextMonth : function(e){
19049         this.update(this.activeDate.add("mo", 1));
19050     },
19051
19052     // private
19053     showPrevYear : function(){
19054         this.update(this.activeDate.add("y", -1));
19055     },
19056
19057     // private
19058     showNextYear : function(){
19059         this.update(this.activeDate.add("y", 1));
19060     },
19061
19062     
19063    // private
19064     update : function(date)
19065     {
19066         var vd = this.activeDate;
19067         this.activeDate = date;
19068 //        if(vd && this.el){
19069 //            var t = date.getTime();
19070 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19071 //                Roo.log('using add remove');
19072 //                
19073 //                this.fireEvent('monthchange', this, date);
19074 //                
19075 //                this.cells.removeClass("fc-state-highlight");
19076 //                this.cells.each(function(c){
19077 //                   if(c.dateValue == t){
19078 //                       c.addClass("fc-state-highlight");
19079 //                       setTimeout(function(){
19080 //                            try{c.dom.firstChild.focus();}catch(e){}
19081 //                       }, 50);
19082 //                       return false;
19083 //                   }
19084 //                   return true;
19085 //                });
19086 //                return;
19087 //            }
19088 //        }
19089         
19090         var days = date.getDaysInMonth();
19091         
19092         var firstOfMonth = date.getFirstDateOfMonth();
19093         var startingPos = firstOfMonth.getDay()-this.startDay;
19094         
19095         if(startingPos < this.startDay){
19096             startingPos += 7;
19097         }
19098         
19099         var pm = date.add(Date.MONTH, -1);
19100         var prevStart = pm.getDaysInMonth()-startingPos;
19101 //        
19102         this.cells = this.el.select('.fc-day',true);
19103         this.textNodes = this.el.query('.fc-day-number');
19104         this.cells.addClassOnOver('fc-state-hover');
19105         
19106         var cells = this.cells.elements;
19107         var textEls = this.textNodes;
19108         
19109         Roo.each(cells, function(cell){
19110             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19111         });
19112         
19113         days += startingPos;
19114
19115         // convert everything to numbers so it's fast
19116         var day = 86400000;
19117         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19118         //Roo.log(d);
19119         //Roo.log(pm);
19120         //Roo.log(prevStart);
19121         
19122         var today = new Date().clearTime().getTime();
19123         var sel = date.clearTime().getTime();
19124         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19125         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19126         var ddMatch = this.disabledDatesRE;
19127         var ddText = this.disabledDatesText;
19128         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19129         var ddaysText = this.disabledDaysText;
19130         var format = this.format;
19131         
19132         var setCellClass = function(cal, cell){
19133             cell.row = 0;
19134             cell.events = [];
19135             cell.more = [];
19136             //Roo.log('set Cell Class');
19137             cell.title = "";
19138             var t = d.getTime();
19139             
19140             //Roo.log(d);
19141             
19142             cell.dateValue = t;
19143             if(t == today){
19144                 cell.className += " fc-today";
19145                 cell.className += " fc-state-highlight";
19146                 cell.title = cal.todayText;
19147             }
19148             if(t == sel){
19149                 // disable highlight in other month..
19150                 //cell.className += " fc-state-highlight";
19151                 
19152             }
19153             // disabling
19154             if(t < min) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.minText;
19157                 return;
19158             }
19159             if(t > max) {
19160                 cell.className = " fc-state-disabled";
19161                 cell.title = cal.maxText;
19162                 return;
19163             }
19164             if(ddays){
19165                 if(ddays.indexOf(d.getDay()) != -1){
19166                     cell.title = ddaysText;
19167                     cell.className = " fc-state-disabled";
19168                 }
19169             }
19170             if(ddMatch && format){
19171                 var fvalue = d.dateFormat(format);
19172                 if(ddMatch.test(fvalue)){
19173                     cell.title = ddText.replace("%0", fvalue);
19174                     cell.className = " fc-state-disabled";
19175                 }
19176             }
19177             
19178             if (!cell.initialClassName) {
19179                 cell.initialClassName = cell.dom.className;
19180             }
19181             
19182             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19183         };
19184
19185         var i = 0;
19186         
19187         for(; i < startingPos; i++) {
19188             textEls[i].innerHTML = (++prevStart);
19189             d.setDate(d.getDate()+1);
19190             
19191             cells[i].className = "fc-past fc-other-month";
19192             setCellClass(this, cells[i]);
19193         }
19194         
19195         var intDay = 0;
19196         
19197         for(; i < days; i++){
19198             intDay = i - startingPos + 1;
19199             textEls[i].innerHTML = (intDay);
19200             d.setDate(d.getDate()+1);
19201             
19202             cells[i].className = ''; // "x-date-active";
19203             setCellClass(this, cells[i]);
19204         }
19205         var extraDays = 0;
19206         
19207         for(; i < 42; i++) {
19208             textEls[i].innerHTML = (++extraDays);
19209             d.setDate(d.getDate()+1);
19210             
19211             cells[i].className = "fc-future fc-other-month";
19212             setCellClass(this, cells[i]);
19213         }
19214         
19215         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19216         
19217         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19218         
19219         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19220         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19221         
19222         if(totalRows != 6){
19223             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19224             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19225         }
19226         
19227         this.fireEvent('monthchange', this, date);
19228         
19229         
19230         /*
19231         if(!this.internalRender){
19232             var main = this.el.dom.firstChild;
19233             var w = main.offsetWidth;
19234             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19235             Roo.fly(main).setWidth(w);
19236             this.internalRender = true;
19237             // opera does not respect the auto grow header center column
19238             // then, after it gets a width opera refuses to recalculate
19239             // without a second pass
19240             if(Roo.isOpera && !this.secondPass){
19241                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19242                 this.secondPass = true;
19243                 this.update.defer(10, this, [date]);
19244             }
19245         }
19246         */
19247         
19248     },
19249     
19250     findCell : function(dt) {
19251         dt = dt.clearTime().getTime();
19252         var ret = false;
19253         this.cells.each(function(c){
19254             //Roo.log("check " +c.dateValue + '?=' + dt);
19255             if(c.dateValue == dt){
19256                 ret = c;
19257                 return false;
19258             }
19259             return true;
19260         });
19261         
19262         return ret;
19263     },
19264     
19265     findCells : function(ev) {
19266         var s = ev.start.clone().clearTime().getTime();
19267        // Roo.log(s);
19268         var e= ev.end.clone().clearTime().getTime();
19269        // Roo.log(e);
19270         var ret = [];
19271         this.cells.each(function(c){
19272              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19273             
19274             if(c.dateValue > e){
19275                 return ;
19276             }
19277             if(c.dateValue < s){
19278                 return ;
19279             }
19280             ret.push(c);
19281         });
19282         
19283         return ret;    
19284     },
19285     
19286 //    findBestRow: function(cells)
19287 //    {
19288 //        var ret = 0;
19289 //        
19290 //        for (var i =0 ; i < cells.length;i++) {
19291 //            ret  = Math.max(cells[i].rows || 0,ret);
19292 //        }
19293 //        return ret;
19294 //        
19295 //    },
19296     
19297     
19298     addItem : function(ev)
19299     {
19300         // look for vertical location slot in
19301         var cells = this.findCells(ev);
19302         
19303 //        ev.row = this.findBestRow(cells);
19304         
19305         // work out the location.
19306         
19307         var crow = false;
19308         var rows = [];
19309         for(var i =0; i < cells.length; i++) {
19310             
19311             cells[i].row = cells[0].row;
19312             
19313             if(i == 0){
19314                 cells[i].row = cells[i].row + 1;
19315             }
19316             
19317             if (!crow) {
19318                 crow = {
19319                     start : cells[i],
19320                     end :  cells[i]
19321                 };
19322                 continue;
19323             }
19324             if (crow.start.getY() == cells[i].getY()) {
19325                 // on same row.
19326                 crow.end = cells[i];
19327                 continue;
19328             }
19329             // different row.
19330             rows.push(crow);
19331             crow = {
19332                 start: cells[i],
19333                 end : cells[i]
19334             };
19335             
19336         }
19337         
19338         rows.push(crow);
19339         ev.els = [];
19340         ev.rows = rows;
19341         ev.cells = cells;
19342         
19343         cells[0].events.push(ev);
19344         
19345         this.calevents.push(ev);
19346     },
19347     
19348     clearEvents: function() {
19349         
19350         if(!this.calevents){
19351             return;
19352         }
19353         
19354         Roo.each(this.cells.elements, function(c){
19355             c.row = 0;
19356             c.events = [];
19357             c.more = [];
19358         });
19359         
19360         Roo.each(this.calevents, function(e) {
19361             Roo.each(e.els, function(el) {
19362                 el.un('mouseenter' ,this.onEventEnter, this);
19363                 el.un('mouseleave' ,this.onEventLeave, this);
19364                 el.remove();
19365             },this);
19366         },this);
19367         
19368         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19369             e.remove();
19370         });
19371         
19372     },
19373     
19374     renderEvents: function()
19375     {   
19376         var _this = this;
19377         
19378         this.cells.each(function(c) {
19379             
19380             if(c.row < 5){
19381                 return;
19382             }
19383             
19384             var ev = c.events;
19385             
19386             var r = 4;
19387             if(c.row != c.events.length){
19388                 r = 4 - (4 - (c.row - c.events.length));
19389             }
19390             
19391             c.events = ev.slice(0, r);
19392             c.more = ev.slice(r);
19393             
19394             if(c.more.length && c.more.length == 1){
19395                 c.events.push(c.more.pop());
19396             }
19397             
19398             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19399             
19400         });
19401             
19402         this.cells.each(function(c) {
19403             
19404             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19405             
19406             
19407             for (var e = 0; e < c.events.length; e++){
19408                 var ev = c.events[e];
19409                 var rows = ev.rows;
19410                 
19411                 for(var i = 0; i < rows.length; i++) {
19412                 
19413                     // how many rows should it span..
19414
19415                     var  cfg = {
19416                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19417                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19418
19419                         unselectable : "on",
19420                         cn : [
19421                             {
19422                                 cls: 'fc-event-inner',
19423                                 cn : [
19424     //                                {
19425     //                                  tag:'span',
19426     //                                  cls: 'fc-event-time',
19427     //                                  html : cells.length > 1 ? '' : ev.time
19428     //                                },
19429                                     {
19430                                       tag:'span',
19431                                       cls: 'fc-event-title',
19432                                       html : String.format('{0}', ev.title)
19433                                     }
19434
19435
19436                                 ]
19437                             },
19438                             {
19439                                 cls: 'ui-resizable-handle ui-resizable-e',
19440                                 html : '&nbsp;&nbsp;&nbsp'
19441                             }
19442
19443                         ]
19444                     };
19445
19446                     if (i == 0) {
19447                         cfg.cls += ' fc-event-start';
19448                     }
19449                     if ((i+1) == rows.length) {
19450                         cfg.cls += ' fc-event-end';
19451                     }
19452
19453                     var ctr = _this.el.select('.fc-event-container',true).first();
19454                     var cg = ctr.createChild(cfg);
19455
19456                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19457                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19458
19459                     var r = (c.more.length) ? 1 : 0;
19460                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19461                     cg.setWidth(ebox.right - sbox.x -2);
19462
19463                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19464                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19465                     cg.on('click', _this.onEventClick, _this, ev);
19466
19467                     ev.els.push(cg);
19468                     
19469                 }
19470                 
19471             }
19472             
19473             
19474             if(c.more.length){
19475                 var  cfg = {
19476                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19477                     style : 'position: absolute',
19478                     unselectable : "on",
19479                     cn : [
19480                         {
19481                             cls: 'fc-event-inner',
19482                             cn : [
19483                                 {
19484                                   tag:'span',
19485                                   cls: 'fc-event-title',
19486                                   html : 'More'
19487                                 }
19488
19489
19490                             ]
19491                         },
19492                         {
19493                             cls: 'ui-resizable-handle ui-resizable-e',
19494                             html : '&nbsp;&nbsp;&nbsp'
19495                         }
19496
19497                     ]
19498                 };
19499
19500                 var ctr = _this.el.select('.fc-event-container',true).first();
19501                 var cg = ctr.createChild(cfg);
19502
19503                 var sbox = c.select('.fc-day-content',true).first().getBox();
19504                 var ebox = c.select('.fc-day-content',true).first().getBox();
19505                 //Roo.log(cg);
19506                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19507                 cg.setWidth(ebox.right - sbox.x -2);
19508
19509                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19510                 
19511             }
19512             
19513         });
19514         
19515         
19516         
19517     },
19518     
19519     onEventEnter: function (e, el,event,d) {
19520         this.fireEvent('evententer', this, el, event);
19521     },
19522     
19523     onEventLeave: function (e, el,event,d) {
19524         this.fireEvent('eventleave', this, el, event);
19525     },
19526     
19527     onEventClick: function (e, el,event,d) {
19528         this.fireEvent('eventclick', this, el, event);
19529     },
19530     
19531     onMonthChange: function () {
19532         this.store.load();
19533     },
19534     
19535     onMoreEventClick: function(e, el, more)
19536     {
19537         var _this = this;
19538         
19539         this.calpopover.placement = 'right';
19540         this.calpopover.setTitle('More');
19541         
19542         this.calpopover.setContent('');
19543         
19544         var ctr = this.calpopover.el.select('.popover-content', true).first();
19545         
19546         Roo.each(more, function(m){
19547             var cfg = {
19548                 cls : 'fc-event-hori fc-event-draggable',
19549                 html : m.title
19550             };
19551             var cg = ctr.createChild(cfg);
19552             
19553             cg.on('click', _this.onEventClick, _this, m);
19554         });
19555         
19556         this.calpopover.show(el);
19557         
19558         
19559     },
19560     
19561     onLoad: function () 
19562     {   
19563         this.calevents = [];
19564         var cal = this;
19565         
19566         if(this.store.getCount() > 0){
19567             this.store.data.each(function(d){
19568                cal.addItem({
19569                     id : d.data.id,
19570                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19571                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19572                     time : d.data.start_time,
19573                     title : d.data.title,
19574                     description : d.data.description,
19575                     venue : d.data.venue
19576                 });
19577             });
19578         }
19579         
19580         this.renderEvents();
19581         
19582         if(this.calevents.length && this.loadMask){
19583             this.maskEl.hide();
19584         }
19585     },
19586     
19587     onBeforeLoad: function()
19588     {
19589         this.clearEvents();
19590         if(this.loadMask){
19591             this.maskEl.show();
19592         }
19593     }
19594 });
19595
19596  
19597  /*
19598  * - LGPL
19599  *
19600  * element
19601  * 
19602  */
19603
19604 /**
19605  * @class Roo.bootstrap.Popover
19606  * @extends Roo.bootstrap.Component
19607  * Bootstrap Popover class
19608  * @cfg {String} html contents of the popover   (or false to use children..)
19609  * @cfg {String} title of popover (or false to hide)
19610  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19611  * @cfg {String} trigger click || hover (or false to trigger manually)
19612  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19613  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19614  *      - if false and it has a 'parent' then it will be automatically added to that element
19615  *      - if string - Roo.get  will be called 
19616  * @cfg {Number} delay - delay before showing
19617  
19618  * @constructor
19619  * Create a new Popover
19620  * @param {Object} config The config object
19621  */
19622
19623 Roo.bootstrap.Popover = function(config){
19624     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19625     
19626     this.addEvents({
19627         // raw events
19628          /**
19629          * @event show
19630          * After the popover show
19631          * 
19632          * @param {Roo.bootstrap.Popover} this
19633          */
19634         "show" : true,
19635         /**
19636          * @event hide
19637          * After the popover hide
19638          * 
19639          * @param {Roo.bootstrap.Popover} this
19640          */
19641         "hide" : true
19642     });
19643 };
19644
19645 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19646     
19647     title: false,
19648     html: false,
19649     
19650     placement : 'right',
19651     trigger : 'hover', // hover
19652     modal : false,
19653     delay : 0,
19654     
19655     over: false,
19656     
19657     can_build_overlaid : false,
19658     
19659     maskEl : false, // the mask element
19660     headerEl : false,
19661     contentEl : false,
19662     alignEl : false, // when show is called with an element - this get's stored.
19663     
19664     getChildContainer : function()
19665     {
19666         return this.contentEl;
19667         
19668     },
19669     getPopoverHeader : function()
19670     {
19671         this.title = true; // flag not to hide it..
19672         this.headerEl.addClass('p-0');
19673         return this.headerEl
19674     },
19675     
19676     
19677     getAutoCreate : function(){
19678          
19679         var cfg = {
19680            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19681            style: 'display:block',
19682            cn : [
19683                 {
19684                     cls : 'arrow'
19685                 },
19686                 {
19687                     cls : 'popover-inner ',
19688                     cn : [
19689                         {
19690                             tag: 'h3',
19691                             cls: 'popover-title popover-header',
19692                             html : this.title === false ? '' : this.title
19693                         },
19694                         {
19695                             cls : 'popover-content popover-body '  + (this.cls || ''),
19696                             html : this.html || ''
19697                         }
19698                     ]
19699                     
19700                 }
19701            ]
19702         };
19703         
19704         return cfg;
19705     },
19706     /**
19707      * @param {string} the title
19708      */
19709     setTitle: function(str)
19710     {
19711         this.title = str;
19712         if (this.el) {
19713             this.headerEl.dom.innerHTML = str;
19714         }
19715         
19716     },
19717     /**
19718      * @param {string} the body content
19719      */
19720     setContent: function(str)
19721     {
19722         this.html = str;
19723         if (this.contentEl) {
19724             this.contentEl.dom.innerHTML = str;
19725         }
19726         
19727     },
19728     // as it get's added to the bottom of the page.
19729     onRender : function(ct, position)
19730     {
19731         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19732         
19733         
19734         
19735         if(!this.el){
19736             var cfg = Roo.apply({},  this.getAutoCreate());
19737             cfg.id = Roo.id();
19738             
19739             if (this.cls) {
19740                 cfg.cls += ' ' + this.cls;
19741             }
19742             if (this.style) {
19743                 cfg.style = this.style;
19744             }
19745             //Roo.log("adding to ");
19746             this.el = Roo.get(document.body).createChild(cfg, position);
19747 //            Roo.log(this.el);
19748         }
19749         
19750         this.contentEl = this.el.select('.popover-content',true).first();
19751         this.headerEl =  this.el.select('.popover-title',true).first();
19752         
19753         var nitems = [];
19754         if(typeof(this.items) != 'undefined'){
19755             var items = this.items;
19756             delete this.items;
19757
19758             for(var i =0;i < items.length;i++) {
19759                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19760             }
19761         }
19762
19763         this.items = nitems;
19764         
19765         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19766         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19767         
19768         
19769         
19770         this.initEvents();
19771     },
19772     
19773     resizeMask : function()
19774     {
19775         this.maskEl.setSize(
19776             Roo.lib.Dom.getViewWidth(true),
19777             Roo.lib.Dom.getViewHeight(true)
19778         );
19779     },
19780     
19781     initEvents : function()
19782     {
19783         
19784         if (!this.modal) { 
19785             Roo.bootstrap.Popover.register(this);
19786         }
19787          
19788         this.arrowEl = this.el.select('.arrow',true).first();
19789         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19790         this.el.enableDisplayMode('block');
19791         this.el.hide();
19792  
19793         
19794         if (this.over === false && !this.parent()) {
19795             return; 
19796         }
19797         if (this.triggers === false) {
19798             return;
19799         }
19800          
19801         // support parent
19802         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19803         var triggers = this.trigger ? this.trigger.split(' ') : [];
19804         Roo.each(triggers, function(trigger) {
19805         
19806             if (trigger == 'click') {
19807                 on_el.on('click', this.toggle, this);
19808             } else if (trigger != 'manual') {
19809                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19810                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19811       
19812                 on_el.on(eventIn  ,this.enter, this);
19813                 on_el.on(eventOut, this.leave, this);
19814             }
19815         }, this);
19816     },
19817     
19818     
19819     // private
19820     timeout : null,
19821     hoverState : null,
19822     
19823     toggle : function () {
19824         this.hoverState == 'in' ? this.leave() : this.enter();
19825     },
19826     
19827     enter : function () {
19828         
19829         clearTimeout(this.timeout);
19830     
19831         this.hoverState = 'in';
19832     
19833         if (!this.delay || !this.delay.show) {
19834             this.show();
19835             return;
19836         }
19837         var _t = this;
19838         this.timeout = setTimeout(function () {
19839             if (_t.hoverState == 'in') {
19840                 _t.show();
19841             }
19842         }, this.delay.show)
19843     },
19844     
19845     leave : function() {
19846         clearTimeout(this.timeout);
19847     
19848         this.hoverState = 'out';
19849     
19850         if (!this.delay || !this.delay.hide) {
19851             this.hide();
19852             return;
19853         }
19854         var _t = this;
19855         this.timeout = setTimeout(function () {
19856             if (_t.hoverState == 'out') {
19857                 _t.hide();
19858             }
19859         }, this.delay.hide)
19860     },
19861     /**
19862      * Show the popover
19863      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19864      * @param {string} (left|right|top|bottom) position
19865      */
19866     show : function (on_el, placement)
19867     {
19868         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19869         on_el = on_el || false; // default to false
19870          
19871         if (!on_el) {
19872             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19873                 on_el = this.parent().el;
19874             } else if (this.over) {
19875                 Roo.get(this.over);
19876             }
19877             
19878         }
19879         
19880         if (!this.el) {
19881             this.render(document.body);
19882         }
19883         
19884         
19885         this.el.removeClass([
19886             'fade','top','bottom', 'left', 'right','in',
19887             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19888         ]);
19889         
19890         if (this.title === false) {
19891             this.headerEl.hide();
19892         }
19893         
19894        
19895         this.el.show();
19896         this.el.dom.style.display = 'block';
19897          
19898         
19899         this.el.addClass(placement + ' roo-popover-' + placement);
19900
19901         if (on_el) {
19902             this.updatePosition();
19903              
19904         } else {
19905             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19906             var es = this.el.getSize();
19907             var x = Roo.lib.Dom.getViewWidth()/2;
19908             var y = Roo.lib.Dom.getViewHeight()/2;
19909             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19910             
19911         }
19912
19913         
19914         //var arrow = this.el.select('.arrow',true).first();
19915         //arrow.set(align[2], 
19916         
19917         this.el.addClass('in');
19918         
19919          
19920         
19921         this.hoverState = 'in';
19922         
19923         if (this.modal) {
19924             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19925             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19926             this.maskEl.dom.style.display = 'block';
19927             this.maskEl.addClass('show');
19928         }
19929         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19930  
19931         this.fireEvent('show', this);
19932         
19933     },
19934     /**
19935      * fire this manually after loading a grid in the table for example
19936      * @param {string} (left|right|top|bottom) where to try and put it
19937      * @param {Boolean} try and move it if we cant get right position.
19938      */
19939     updatePosition : function(placement, try_move)
19940     {
19941         this.el.addClass(placement + ' roo-popover-' + placement);
19942         
19943         if (!this.alignEl ) {
19944             return false;
19945         }
19946         
19947         switch (placement) {
19948             case 'right':
19949                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19950                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19951                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19952                     //normal display... or moved up/down.
19953                     this.setXY(offset);
19954                     var xy = this.alignEl.getAnchorXY('tr', false);
19955                     xy[0]+=2;xy[1]+=5;
19956                     this.arrowEl.setXY(xy);
19957                     return true;
19958                 }
19959                 // continue through...
19960                 try_move = false;
19961                 
19962             
19963             case 'left':
19964                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19965                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[-10,0]);
19966                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19967                     //normal display... or moved up/down.
19968                     this.setXY(offset);
19969                     var xy = this.alignEl.getAnchorXY('tl', false);
19970                     xy[0]+=2;xy[1]+=5; // << fix me
19971                     this.arrowEl.setXY(xy);
19972                     return true;
19973                 }
19974                 // call self...
19975                 return this.updatePosition('right', false);
19976             
19977             case 'top':
19978                 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,-10]);
19979                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,-10]);
19980                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
19981                     //normal display... or moved up/down.
19982                     this.setXY(offset);
19983                     var xy = this.alignEl.getAnchorXY('tl', false);
19984                     xy[0]+=2;xy[1]+=5; // << fix me
19985                     this.arrowEl.setXY(xy);
19986                     return true;
19987                 }
19988                 return this.updatePosition('right', false);
19989                 
19990             
19991         }
19992         
19993         
19994         
19995         // work out the pointy position.
19996         var p1 = this.alignment[0].split('-').pop().replace('?','');
19997         var xy = this.alignEl.getAnchorXY(p1, false);
19998         xy[0]+=2;xy[1]+=5;
19999         this.arrowEl.setXY(xy);
20000         return true;
20001     },
20002     
20003     hide : function()
20004     {
20005         this.el.setXY([0,0]);
20006         this.el.removeClass('in');
20007         this.el.hide();
20008         this.hoverState = null;
20009         this.maskEl.hide(); // always..
20010         this.fireEvent('hide', this);
20011     }
20012     
20013 });
20014
20015
20016 Roo.apply(Roo.bootstrap.Popover, {
20017
20018     alignment : {
20019         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20020         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20021         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20022         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20023     },
20024     
20025     zIndex : 20001,
20026
20027     clickHander : false,
20028     
20029
20030     onMouseDown : function(e)
20031     {
20032         if (!e.getTarget(".roo-popover")) {
20033             this.hideAll();
20034         }
20035          
20036     },
20037     
20038     popups : [],
20039     
20040     register : function(popup)
20041     {
20042         if (!Roo.bootstrap.Popover.clickHandler) {
20043             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20044         }
20045         // hide other popups.
20046         this.hideAll();
20047         this.popups.push(popup);
20048     },
20049     hideAll : function()
20050     {
20051         this.popups.forEach(function(p) {
20052             p.hide();
20053         });
20054     }
20055
20056 });/*
20057  * - LGPL
20058  *
20059  * Card header - holder for the card header elements.
20060  * 
20061  */
20062
20063 /**
20064  * @class Roo.bootstrap.PopoverNav
20065  * @extends Roo.bootstrap.NavGroup
20066  * Bootstrap Popover header navigation class
20067  * @constructor
20068  * Create a new Popover Header Navigation 
20069  * @param {Object} config The config object
20070  */
20071
20072 Roo.bootstrap.PopoverNav = function(config){
20073     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20074 };
20075
20076 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20077     
20078     
20079     container_method : 'getPopoverHeader' 
20080     
20081      
20082     
20083     
20084    
20085 });
20086
20087  
20088
20089  /*
20090  * - LGPL
20091  *
20092  * Progress
20093  * 
20094  */
20095
20096 /**
20097  * @class Roo.bootstrap.Progress
20098  * @extends Roo.bootstrap.Component
20099  * Bootstrap Progress class
20100  * @cfg {Boolean} striped striped of the progress bar
20101  * @cfg {Boolean} active animated of the progress bar
20102  * 
20103  * 
20104  * @constructor
20105  * Create a new Progress
20106  * @param {Object} config The config object
20107  */
20108
20109 Roo.bootstrap.Progress = function(config){
20110     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20111 };
20112
20113 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20114     
20115     striped : false,
20116     active: false,
20117     
20118     getAutoCreate : function(){
20119         var cfg = {
20120             tag: 'div',
20121             cls: 'progress'
20122         };
20123         
20124         
20125         if(this.striped){
20126             cfg.cls += ' progress-striped';
20127         }
20128       
20129         if(this.active){
20130             cfg.cls += ' active';
20131         }
20132         
20133         
20134         return cfg;
20135     }
20136    
20137 });
20138
20139  
20140
20141  /*
20142  * - LGPL
20143  *
20144  * ProgressBar
20145  * 
20146  */
20147
20148 /**
20149  * @class Roo.bootstrap.ProgressBar
20150  * @extends Roo.bootstrap.Component
20151  * Bootstrap ProgressBar class
20152  * @cfg {Number} aria_valuenow aria-value now
20153  * @cfg {Number} aria_valuemin aria-value min
20154  * @cfg {Number} aria_valuemax aria-value max
20155  * @cfg {String} label label for the progress bar
20156  * @cfg {String} panel (success | info | warning | danger )
20157  * @cfg {String} role role of the progress bar
20158  * @cfg {String} sr_only text
20159  * 
20160  * 
20161  * @constructor
20162  * Create a new ProgressBar
20163  * @param {Object} config The config object
20164  */
20165
20166 Roo.bootstrap.ProgressBar = function(config){
20167     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20168 };
20169
20170 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20171     
20172     aria_valuenow : 0,
20173     aria_valuemin : 0,
20174     aria_valuemax : 100,
20175     label : false,
20176     panel : false,
20177     role : false,
20178     sr_only: false,
20179     
20180     getAutoCreate : function()
20181     {
20182         
20183         var cfg = {
20184             tag: 'div',
20185             cls: 'progress-bar',
20186             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20187         };
20188         
20189         if(this.sr_only){
20190             cfg.cn = {
20191                 tag: 'span',
20192                 cls: 'sr-only',
20193                 html: this.sr_only
20194             }
20195         }
20196         
20197         if(this.role){
20198             cfg.role = this.role;
20199         }
20200         
20201         if(this.aria_valuenow){
20202             cfg['aria-valuenow'] = this.aria_valuenow;
20203         }
20204         
20205         if(this.aria_valuemin){
20206             cfg['aria-valuemin'] = this.aria_valuemin;
20207         }
20208         
20209         if(this.aria_valuemax){
20210             cfg['aria-valuemax'] = this.aria_valuemax;
20211         }
20212         
20213         if(this.label && !this.sr_only){
20214             cfg.html = this.label;
20215         }
20216         
20217         if(this.panel){
20218             cfg.cls += ' progress-bar-' + this.panel;
20219         }
20220         
20221         return cfg;
20222     },
20223     
20224     update : function(aria_valuenow)
20225     {
20226         this.aria_valuenow = aria_valuenow;
20227         
20228         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20229     }
20230    
20231 });
20232
20233  
20234
20235  /*
20236  * - LGPL
20237  *
20238  * column
20239  * 
20240  */
20241
20242 /**
20243  * @class Roo.bootstrap.TabGroup
20244  * @extends Roo.bootstrap.Column
20245  * Bootstrap Column class
20246  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20247  * @cfg {Boolean} carousel true to make the group behave like a carousel
20248  * @cfg {Boolean} bullets show bullets for the panels
20249  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20250  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20251  * @cfg {Boolean} showarrow (true|false) show arrow default true
20252  * 
20253  * @constructor
20254  * Create a new TabGroup
20255  * @param {Object} config The config object
20256  */
20257
20258 Roo.bootstrap.TabGroup = function(config){
20259     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20260     if (!this.navId) {
20261         this.navId = Roo.id();
20262     }
20263     this.tabs = [];
20264     Roo.bootstrap.TabGroup.register(this);
20265     
20266 };
20267
20268 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20269     
20270     carousel : false,
20271     transition : false,
20272     bullets : 0,
20273     timer : 0,
20274     autoslide : false,
20275     slideFn : false,
20276     slideOnTouch : false,
20277     showarrow : true,
20278     
20279     getAutoCreate : function()
20280     {
20281         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20282         
20283         cfg.cls += ' tab-content';
20284         
20285         if (this.carousel) {
20286             cfg.cls += ' carousel slide';
20287             
20288             cfg.cn = [{
20289                cls : 'carousel-inner',
20290                cn : []
20291             }];
20292         
20293             if(this.bullets  && !Roo.isTouch){
20294                 
20295                 var bullets = {
20296                     cls : 'carousel-bullets',
20297                     cn : []
20298                 };
20299                
20300                 if(this.bullets_cls){
20301                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20302                 }
20303                 
20304                 bullets.cn.push({
20305                     cls : 'clear'
20306                 });
20307                 
20308                 cfg.cn[0].cn.push(bullets);
20309             }
20310             
20311             if(this.showarrow){
20312                 cfg.cn[0].cn.push({
20313                     tag : 'div',
20314                     class : 'carousel-arrow',
20315                     cn : [
20316                         {
20317                             tag : 'div',
20318                             class : 'carousel-prev',
20319                             cn : [
20320                                 {
20321                                     tag : 'i',
20322                                     class : 'fa fa-chevron-left'
20323                                 }
20324                             ]
20325                         },
20326                         {
20327                             tag : 'div',
20328                             class : 'carousel-next',
20329                             cn : [
20330                                 {
20331                                     tag : 'i',
20332                                     class : 'fa fa-chevron-right'
20333                                 }
20334                             ]
20335                         }
20336                     ]
20337                 });
20338             }
20339             
20340         }
20341         
20342         return cfg;
20343     },
20344     
20345     initEvents:  function()
20346     {
20347 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20348 //            this.el.on("touchstart", this.onTouchStart, this);
20349 //        }
20350         
20351         if(this.autoslide){
20352             var _this = this;
20353             
20354             this.slideFn = window.setInterval(function() {
20355                 _this.showPanelNext();
20356             }, this.timer);
20357         }
20358         
20359         if(this.showarrow){
20360             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20361             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20362         }
20363         
20364         
20365     },
20366     
20367 //    onTouchStart : function(e, el, o)
20368 //    {
20369 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20370 //            return;
20371 //        }
20372 //        
20373 //        this.showPanelNext();
20374 //    },
20375     
20376     
20377     getChildContainer : function()
20378     {
20379         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20380     },
20381     
20382     /**
20383     * register a Navigation item
20384     * @param {Roo.bootstrap.NavItem} the navitem to add
20385     */
20386     register : function(item)
20387     {
20388         this.tabs.push( item);
20389         item.navId = this.navId; // not really needed..
20390         this.addBullet();
20391     
20392     },
20393     
20394     getActivePanel : function()
20395     {
20396         var r = false;
20397         Roo.each(this.tabs, function(t) {
20398             if (t.active) {
20399                 r = t;
20400                 return false;
20401             }
20402             return null;
20403         });
20404         return r;
20405         
20406     },
20407     getPanelByName : function(n)
20408     {
20409         var r = false;
20410         Roo.each(this.tabs, function(t) {
20411             if (t.tabId == n) {
20412                 r = t;
20413                 return false;
20414             }
20415             return null;
20416         });
20417         return r;
20418     },
20419     indexOfPanel : function(p)
20420     {
20421         var r = false;
20422         Roo.each(this.tabs, function(t,i) {
20423             if (t.tabId == p.tabId) {
20424                 r = i;
20425                 return false;
20426             }
20427             return null;
20428         });
20429         return r;
20430     },
20431     /**
20432      * show a specific panel
20433      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20434      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20435      */
20436     showPanel : function (pan)
20437     {
20438         if(this.transition || typeof(pan) == 'undefined'){
20439             Roo.log("waiting for the transitionend");
20440             return false;
20441         }
20442         
20443         if (typeof(pan) == 'number') {
20444             pan = this.tabs[pan];
20445         }
20446         
20447         if (typeof(pan) == 'string') {
20448             pan = this.getPanelByName(pan);
20449         }
20450         
20451         var cur = this.getActivePanel();
20452         
20453         if(!pan || !cur){
20454             Roo.log('pan or acitve pan is undefined');
20455             return false;
20456         }
20457         
20458         if (pan.tabId == this.getActivePanel().tabId) {
20459             return true;
20460         }
20461         
20462         if (false === cur.fireEvent('beforedeactivate')) {
20463             return false;
20464         }
20465         
20466         if(this.bullets > 0 && !Roo.isTouch){
20467             this.setActiveBullet(this.indexOfPanel(pan));
20468         }
20469         
20470         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20471             
20472             //class="carousel-item carousel-item-next carousel-item-left"
20473             
20474             this.transition = true;
20475             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20476             var lr = dir == 'next' ? 'left' : 'right';
20477             pan.el.addClass(dir); // or prev
20478             pan.el.addClass('carousel-item-' + dir); // or prev
20479             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20480             cur.el.addClass(lr); // or right
20481             pan.el.addClass(lr);
20482             cur.el.addClass('carousel-item-' +lr); // or right
20483             pan.el.addClass('carousel-item-' +lr);
20484             
20485             
20486             var _this = this;
20487             cur.el.on('transitionend', function() {
20488                 Roo.log("trans end?");
20489                 
20490                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20491                 pan.setActive(true);
20492                 
20493                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20494                 cur.setActive(false);
20495                 
20496                 _this.transition = false;
20497                 
20498             }, this, { single:  true } );
20499             
20500             return true;
20501         }
20502         
20503         cur.setActive(false);
20504         pan.setActive(true);
20505         
20506         return true;
20507         
20508     },
20509     showPanelNext : function()
20510     {
20511         var i = this.indexOfPanel(this.getActivePanel());
20512         
20513         if (i >= this.tabs.length - 1 && !this.autoslide) {
20514             return;
20515         }
20516         
20517         if (i >= this.tabs.length - 1 && this.autoslide) {
20518             i = -1;
20519         }
20520         
20521         this.showPanel(this.tabs[i+1]);
20522     },
20523     
20524     showPanelPrev : function()
20525     {
20526         var i = this.indexOfPanel(this.getActivePanel());
20527         
20528         if (i  < 1 && !this.autoslide) {
20529             return;
20530         }
20531         
20532         if (i < 1 && this.autoslide) {
20533             i = this.tabs.length;
20534         }
20535         
20536         this.showPanel(this.tabs[i-1]);
20537     },
20538     
20539     
20540     addBullet: function()
20541     {
20542         if(!this.bullets || Roo.isTouch){
20543             return;
20544         }
20545         var ctr = this.el.select('.carousel-bullets',true).first();
20546         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20547         var bullet = ctr.createChild({
20548             cls : 'bullet bullet-' + i
20549         },ctr.dom.lastChild);
20550         
20551         
20552         var _this = this;
20553         
20554         bullet.on('click', (function(e, el, o, ii, t){
20555
20556             e.preventDefault();
20557
20558             this.showPanel(ii);
20559
20560             if(this.autoslide && this.slideFn){
20561                 clearInterval(this.slideFn);
20562                 this.slideFn = window.setInterval(function() {
20563                     _this.showPanelNext();
20564                 }, this.timer);
20565             }
20566
20567         }).createDelegate(this, [i, bullet], true));
20568                 
20569         
20570     },
20571      
20572     setActiveBullet : function(i)
20573     {
20574         if(Roo.isTouch){
20575             return;
20576         }
20577         
20578         Roo.each(this.el.select('.bullet', true).elements, function(el){
20579             el.removeClass('selected');
20580         });
20581
20582         var bullet = this.el.select('.bullet-' + i, true).first();
20583         
20584         if(!bullet){
20585             return;
20586         }
20587         
20588         bullet.addClass('selected');
20589     }
20590     
20591     
20592   
20593 });
20594
20595  
20596
20597  
20598  
20599 Roo.apply(Roo.bootstrap.TabGroup, {
20600     
20601     groups: {},
20602      /**
20603     * register a Navigation Group
20604     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20605     */
20606     register : function(navgrp)
20607     {
20608         this.groups[navgrp.navId] = navgrp;
20609         
20610     },
20611     /**
20612     * fetch a Navigation Group based on the navigation ID
20613     * if one does not exist , it will get created.
20614     * @param {string} the navgroup to add
20615     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20616     */
20617     get: function(navId) {
20618         if (typeof(this.groups[navId]) == 'undefined') {
20619             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20620         }
20621         return this.groups[navId] ;
20622     }
20623     
20624     
20625     
20626 });
20627
20628  /*
20629  * - LGPL
20630  *
20631  * TabPanel
20632  * 
20633  */
20634
20635 /**
20636  * @class Roo.bootstrap.TabPanel
20637  * @extends Roo.bootstrap.Component
20638  * Bootstrap TabPanel class
20639  * @cfg {Boolean} active panel active
20640  * @cfg {String} html panel content
20641  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20642  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20643  * @cfg {String} href click to link..
20644  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20645  * 
20646  * 
20647  * @constructor
20648  * Create a new TabPanel
20649  * @param {Object} config The config object
20650  */
20651
20652 Roo.bootstrap.TabPanel = function(config){
20653     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20654     this.addEvents({
20655         /**
20656              * @event changed
20657              * Fires when the active status changes
20658              * @param {Roo.bootstrap.TabPanel} this
20659              * @param {Boolean} state the new state
20660             
20661          */
20662         'changed': true,
20663         /**
20664              * @event beforedeactivate
20665              * Fires before a tab is de-activated - can be used to do validation on a form.
20666              * @param {Roo.bootstrap.TabPanel} this
20667              * @return {Boolean} false if there is an error
20668             
20669          */
20670         'beforedeactivate': true
20671      });
20672     
20673     this.tabId = this.tabId || Roo.id();
20674   
20675 };
20676
20677 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20678     
20679     active: false,
20680     html: false,
20681     tabId: false,
20682     navId : false,
20683     href : '',
20684     touchSlide : false,
20685     getAutoCreate : function(){
20686         
20687         
20688         var cfg = {
20689             tag: 'div',
20690             // item is needed for carousel - not sure if it has any effect otherwise
20691             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20692             html: this.html || ''
20693         };
20694         
20695         if(this.active){
20696             cfg.cls += ' active';
20697         }
20698         
20699         if(this.tabId){
20700             cfg.tabId = this.tabId;
20701         }
20702         
20703         
20704         
20705         return cfg;
20706     },
20707     
20708     initEvents:  function()
20709     {
20710         var p = this.parent();
20711         
20712         this.navId = this.navId || p.navId;
20713         
20714         if (typeof(this.navId) != 'undefined') {
20715             // not really needed.. but just in case.. parent should be a NavGroup.
20716             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20717             
20718             tg.register(this);
20719             
20720             var i = tg.tabs.length - 1;
20721             
20722             if(this.active && tg.bullets > 0 && i < tg.bullets){
20723                 tg.setActiveBullet(i);
20724             }
20725         }
20726         
20727         this.el.on('click', this.onClick, this);
20728         
20729         if(Roo.isTouch && this.touchSlide){
20730             this.el.on("touchstart", this.onTouchStart, this);
20731             this.el.on("touchmove", this.onTouchMove, this);
20732             this.el.on("touchend", this.onTouchEnd, this);
20733         }
20734         
20735     },
20736     
20737     onRender : function(ct, position)
20738     {
20739         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20740     },
20741     
20742     setActive : function(state)
20743     {
20744         Roo.log("panel - set active " + this.tabId + "=" + state);
20745         
20746         this.active = state;
20747         if (!state) {
20748             this.el.removeClass('active');
20749             
20750         } else  if (!this.el.hasClass('active')) {
20751             this.el.addClass('active');
20752         }
20753         
20754         this.fireEvent('changed', this, state);
20755     },
20756     
20757     onClick : function(e)
20758     {
20759         e.preventDefault();
20760         
20761         if(!this.href.length){
20762             return;
20763         }
20764         
20765         window.location.href = this.href;
20766     },
20767     
20768     startX : 0,
20769     startY : 0,
20770     endX : 0,
20771     endY : 0,
20772     swiping : false,
20773     
20774     onTouchStart : function(e)
20775     {
20776         this.swiping = false;
20777         
20778         this.startX = e.browserEvent.touches[0].clientX;
20779         this.startY = e.browserEvent.touches[0].clientY;
20780     },
20781     
20782     onTouchMove : function(e)
20783     {
20784         this.swiping = true;
20785         
20786         this.endX = e.browserEvent.touches[0].clientX;
20787         this.endY = e.browserEvent.touches[0].clientY;
20788     },
20789     
20790     onTouchEnd : function(e)
20791     {
20792         if(!this.swiping){
20793             this.onClick(e);
20794             return;
20795         }
20796         
20797         var tabGroup = this.parent();
20798         
20799         if(this.endX > this.startX){ // swiping right
20800             tabGroup.showPanelPrev();
20801             return;
20802         }
20803         
20804         if(this.startX > this.endX){ // swiping left
20805             tabGroup.showPanelNext();
20806             return;
20807         }
20808     }
20809     
20810     
20811 });
20812  
20813
20814  
20815
20816  /*
20817  * - LGPL
20818  *
20819  * DateField
20820  * 
20821  */
20822
20823 /**
20824  * @class Roo.bootstrap.DateField
20825  * @extends Roo.bootstrap.Input
20826  * Bootstrap DateField class
20827  * @cfg {Number} weekStart default 0
20828  * @cfg {String} viewMode default empty, (months|years)
20829  * @cfg {String} minViewMode default empty, (months|years)
20830  * @cfg {Number} startDate default -Infinity
20831  * @cfg {Number} endDate default Infinity
20832  * @cfg {Boolean} todayHighlight default false
20833  * @cfg {Boolean} todayBtn default false
20834  * @cfg {Boolean} calendarWeeks default false
20835  * @cfg {Object} daysOfWeekDisabled default empty
20836  * @cfg {Boolean} singleMode default false (true | false)
20837  * 
20838  * @cfg {Boolean} keyboardNavigation default true
20839  * @cfg {String} language default en
20840  * 
20841  * @constructor
20842  * Create a new DateField
20843  * @param {Object} config The config object
20844  */
20845
20846 Roo.bootstrap.DateField = function(config){
20847     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20848      this.addEvents({
20849             /**
20850              * @event show
20851              * Fires when this field show.
20852              * @param {Roo.bootstrap.DateField} this
20853              * @param {Mixed} date The date value
20854              */
20855             show : true,
20856             /**
20857              * @event show
20858              * Fires when this field hide.
20859              * @param {Roo.bootstrap.DateField} this
20860              * @param {Mixed} date The date value
20861              */
20862             hide : true,
20863             /**
20864              * @event select
20865              * Fires when select a date.
20866              * @param {Roo.bootstrap.DateField} this
20867              * @param {Mixed} date The date value
20868              */
20869             select : true,
20870             /**
20871              * @event beforeselect
20872              * Fires when before select a date.
20873              * @param {Roo.bootstrap.DateField} this
20874              * @param {Mixed} date The date value
20875              */
20876             beforeselect : true
20877         });
20878 };
20879
20880 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20881     
20882     /**
20883      * @cfg {String} format
20884      * The default date format string which can be overriden for localization support.  The format must be
20885      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20886      */
20887     format : "m/d/y",
20888     /**
20889      * @cfg {String} altFormats
20890      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20891      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20892      */
20893     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20894     
20895     weekStart : 0,
20896     
20897     viewMode : '',
20898     
20899     minViewMode : '',
20900     
20901     todayHighlight : false,
20902     
20903     todayBtn: false,
20904     
20905     language: 'en',
20906     
20907     keyboardNavigation: true,
20908     
20909     calendarWeeks: false,
20910     
20911     startDate: -Infinity,
20912     
20913     endDate: Infinity,
20914     
20915     daysOfWeekDisabled: [],
20916     
20917     _events: [],
20918     
20919     singleMode : false,
20920     
20921     UTCDate: function()
20922     {
20923         return new Date(Date.UTC.apply(Date, arguments));
20924     },
20925     
20926     UTCToday: function()
20927     {
20928         var today = new Date();
20929         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20930     },
20931     
20932     getDate: function() {
20933             var d = this.getUTCDate();
20934             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20935     },
20936     
20937     getUTCDate: function() {
20938             return this.date;
20939     },
20940     
20941     setDate: function(d) {
20942             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20943     },
20944     
20945     setUTCDate: function(d) {
20946             this.date = d;
20947             this.setValue(this.formatDate(this.date));
20948     },
20949         
20950     onRender: function(ct, position)
20951     {
20952         
20953         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20954         
20955         this.language = this.language || 'en';
20956         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20957         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20958         
20959         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20960         this.format = this.format || 'm/d/y';
20961         this.isInline = false;
20962         this.isInput = true;
20963         this.component = this.el.select('.add-on', true).first() || false;
20964         this.component = (this.component && this.component.length === 0) ? false : this.component;
20965         this.hasInput = this.component && this.inputEl().length;
20966         
20967         if (typeof(this.minViewMode === 'string')) {
20968             switch (this.minViewMode) {
20969                 case 'months':
20970                     this.minViewMode = 1;
20971                     break;
20972                 case 'years':
20973                     this.minViewMode = 2;
20974                     break;
20975                 default:
20976                     this.minViewMode = 0;
20977                     break;
20978             }
20979         }
20980         
20981         if (typeof(this.viewMode === 'string')) {
20982             switch (this.viewMode) {
20983                 case 'months':
20984                     this.viewMode = 1;
20985                     break;
20986                 case 'years':
20987                     this.viewMode = 2;
20988                     break;
20989                 default:
20990                     this.viewMode = 0;
20991                     break;
20992             }
20993         }
20994                 
20995         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20996         
20997 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20998         
20999         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21000         
21001         this.picker().on('mousedown', this.onMousedown, this);
21002         this.picker().on('click', this.onClick, this);
21003         
21004         this.picker().addClass('datepicker-dropdown');
21005         
21006         this.startViewMode = this.viewMode;
21007         
21008         if(this.singleMode){
21009             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21010                 v.setVisibilityMode(Roo.Element.DISPLAY);
21011                 v.hide();
21012             });
21013             
21014             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21015                 v.setStyle('width', '189px');
21016             });
21017         }
21018         
21019         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21020             if(!this.calendarWeeks){
21021                 v.remove();
21022                 return;
21023             }
21024             
21025             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21026             v.attr('colspan', function(i, val){
21027                 return parseInt(val) + 1;
21028             });
21029         });
21030                         
21031         
21032         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21033         
21034         this.setStartDate(this.startDate);
21035         this.setEndDate(this.endDate);
21036         
21037         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21038         
21039         this.fillDow();
21040         this.fillMonths();
21041         this.update();
21042         this.showMode();
21043         
21044         if(this.isInline) {
21045             this.showPopup();
21046         }
21047     },
21048     
21049     picker : function()
21050     {
21051         return this.pickerEl;
21052 //        return this.el.select('.datepicker', true).first();
21053     },
21054     
21055     fillDow: function()
21056     {
21057         var dowCnt = this.weekStart;
21058         
21059         var dow = {
21060             tag: 'tr',
21061             cn: [
21062                 
21063             ]
21064         };
21065         
21066         if(this.calendarWeeks){
21067             dow.cn.push({
21068                 tag: 'th',
21069                 cls: 'cw',
21070                 html: '&nbsp;'
21071             })
21072         }
21073         
21074         while (dowCnt < this.weekStart + 7) {
21075             dow.cn.push({
21076                 tag: 'th',
21077                 cls: 'dow',
21078                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21079             });
21080         }
21081         
21082         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21083     },
21084     
21085     fillMonths: function()
21086     {    
21087         var i = 0;
21088         var months = this.picker().select('>.datepicker-months td', true).first();
21089         
21090         months.dom.innerHTML = '';
21091         
21092         while (i < 12) {
21093             var month = {
21094                 tag: 'span',
21095                 cls: 'month',
21096                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21097             };
21098             
21099             months.createChild(month);
21100         }
21101         
21102     },
21103     
21104     update: function()
21105     {
21106         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;
21107         
21108         if (this.date < this.startDate) {
21109             this.viewDate = new Date(this.startDate);
21110         } else if (this.date > this.endDate) {
21111             this.viewDate = new Date(this.endDate);
21112         } else {
21113             this.viewDate = new Date(this.date);
21114         }
21115         
21116         this.fill();
21117     },
21118     
21119     fill: function() 
21120     {
21121         var d = new Date(this.viewDate),
21122                 year = d.getUTCFullYear(),
21123                 month = d.getUTCMonth(),
21124                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21125                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21126                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21127                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21128                 currentDate = this.date && this.date.valueOf(),
21129                 today = this.UTCToday();
21130         
21131         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21132         
21133 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21134         
21135 //        this.picker.select('>tfoot th.today').
21136 //                                              .text(dates[this.language].today)
21137 //                                              .toggle(this.todayBtn !== false);
21138     
21139         this.updateNavArrows();
21140         this.fillMonths();
21141                                                 
21142         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21143         
21144         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21145          
21146         prevMonth.setUTCDate(day);
21147         
21148         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21149         
21150         var nextMonth = new Date(prevMonth);
21151         
21152         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21153         
21154         nextMonth = nextMonth.valueOf();
21155         
21156         var fillMonths = false;
21157         
21158         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21159         
21160         while(prevMonth.valueOf() <= nextMonth) {
21161             var clsName = '';
21162             
21163             if (prevMonth.getUTCDay() === this.weekStart) {
21164                 if(fillMonths){
21165                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21166                 }
21167                     
21168                 fillMonths = {
21169                     tag: 'tr',
21170                     cn: []
21171                 };
21172                 
21173                 if(this.calendarWeeks){
21174                     // ISO 8601: First week contains first thursday.
21175                     // ISO also states week starts on Monday, but we can be more abstract here.
21176                     var
21177                     // Start of current week: based on weekstart/current date
21178                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21179                     // Thursday of this week
21180                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21181                     // First Thursday of year, year from thursday
21182                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21183                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21184                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21185                     
21186                     fillMonths.cn.push({
21187                         tag: 'td',
21188                         cls: 'cw',
21189                         html: calWeek
21190                     });
21191                 }
21192             }
21193             
21194             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21195                 clsName += ' old';
21196             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21197                 clsName += ' new';
21198             }
21199             if (this.todayHighlight &&
21200                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21201                 prevMonth.getUTCMonth() == today.getMonth() &&
21202                 prevMonth.getUTCDate() == today.getDate()) {
21203                 clsName += ' today';
21204             }
21205             
21206             if (currentDate && prevMonth.valueOf() === currentDate) {
21207                 clsName += ' active';
21208             }
21209             
21210             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21211                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21212                     clsName += ' disabled';
21213             }
21214             
21215             fillMonths.cn.push({
21216                 tag: 'td',
21217                 cls: 'day ' + clsName,
21218                 html: prevMonth.getDate()
21219             });
21220             
21221             prevMonth.setDate(prevMonth.getDate()+1);
21222         }
21223           
21224         var currentYear = this.date && this.date.getUTCFullYear();
21225         var currentMonth = this.date && this.date.getUTCMonth();
21226         
21227         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21228         
21229         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21230             v.removeClass('active');
21231             
21232             if(currentYear === year && k === currentMonth){
21233                 v.addClass('active');
21234             }
21235             
21236             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21237                 v.addClass('disabled');
21238             }
21239             
21240         });
21241         
21242         
21243         year = parseInt(year/10, 10) * 10;
21244         
21245         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21246         
21247         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21248         
21249         year -= 1;
21250         for (var i = -1; i < 11; i++) {
21251             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21252                 tag: 'span',
21253                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21254                 html: year
21255             });
21256             
21257             year += 1;
21258         }
21259     },
21260     
21261     showMode: function(dir) 
21262     {
21263         if (dir) {
21264             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21265         }
21266         
21267         Roo.each(this.picker().select('>div',true).elements, function(v){
21268             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21269             v.hide();
21270         });
21271         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21272     },
21273     
21274     place: function()
21275     {
21276         if(this.isInline) {
21277             return;
21278         }
21279         
21280         this.picker().removeClass(['bottom', 'top']);
21281         
21282         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21283             /*
21284              * place to the top of element!
21285              *
21286              */
21287             
21288             this.picker().addClass('top');
21289             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21290             
21291             return;
21292         }
21293         
21294         this.picker().addClass('bottom');
21295         
21296         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21297     },
21298     
21299     parseDate : function(value)
21300     {
21301         if(!value || value instanceof Date){
21302             return value;
21303         }
21304         var v = Date.parseDate(value, this.format);
21305         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21306             v = Date.parseDate(value, 'Y-m-d');
21307         }
21308         if(!v && this.altFormats){
21309             if(!this.altFormatsArray){
21310                 this.altFormatsArray = this.altFormats.split("|");
21311             }
21312             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21313                 v = Date.parseDate(value, this.altFormatsArray[i]);
21314             }
21315         }
21316         return v;
21317     },
21318     
21319     formatDate : function(date, fmt)
21320     {   
21321         return (!date || !(date instanceof Date)) ?
21322         date : date.dateFormat(fmt || this.format);
21323     },
21324     
21325     onFocus : function()
21326     {
21327         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21328         this.showPopup();
21329     },
21330     
21331     onBlur : function()
21332     {
21333         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21334         
21335         var d = this.inputEl().getValue();
21336         
21337         this.setValue(d);
21338                 
21339         this.hidePopup();
21340     },
21341     
21342     showPopup : function()
21343     {
21344         this.picker().show();
21345         this.update();
21346         this.place();
21347         
21348         this.fireEvent('showpopup', this, this.date);
21349     },
21350     
21351     hidePopup : function()
21352     {
21353         if(this.isInline) {
21354             return;
21355         }
21356         this.picker().hide();
21357         this.viewMode = this.startViewMode;
21358         this.showMode();
21359         
21360         this.fireEvent('hidepopup', this, this.date);
21361         
21362     },
21363     
21364     onMousedown: function(e)
21365     {
21366         e.stopPropagation();
21367         e.preventDefault();
21368     },
21369     
21370     keyup: function(e)
21371     {
21372         Roo.bootstrap.DateField.superclass.keyup.call(this);
21373         this.update();
21374     },
21375
21376     setValue: function(v)
21377     {
21378         if(this.fireEvent('beforeselect', this, v) !== false){
21379             var d = new Date(this.parseDate(v) ).clearTime();
21380         
21381             if(isNaN(d.getTime())){
21382                 this.date = this.viewDate = '';
21383                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21384                 return;
21385             }
21386
21387             v = this.formatDate(d);
21388
21389             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21390
21391             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21392
21393             this.update();
21394
21395             this.fireEvent('select', this, this.date);
21396         }
21397     },
21398     
21399     getValue: function()
21400     {
21401         return this.formatDate(this.date);
21402     },
21403     
21404     fireKey: function(e)
21405     {
21406         if (!this.picker().isVisible()){
21407             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21408                 this.showPopup();
21409             }
21410             return;
21411         }
21412         
21413         var dateChanged = false,
21414         dir, day, month,
21415         newDate, newViewDate;
21416         
21417         switch(e.keyCode){
21418             case 27: // escape
21419                 this.hidePopup();
21420                 e.preventDefault();
21421                 break;
21422             case 37: // left
21423             case 39: // right
21424                 if (!this.keyboardNavigation) {
21425                     break;
21426                 }
21427                 dir = e.keyCode == 37 ? -1 : 1;
21428                 
21429                 if (e.ctrlKey){
21430                     newDate = this.moveYear(this.date, dir);
21431                     newViewDate = this.moveYear(this.viewDate, dir);
21432                 } else if (e.shiftKey){
21433                     newDate = this.moveMonth(this.date, dir);
21434                     newViewDate = this.moveMonth(this.viewDate, dir);
21435                 } else {
21436                     newDate = new Date(this.date);
21437                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21438                     newViewDate = new Date(this.viewDate);
21439                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21440                 }
21441                 if (this.dateWithinRange(newDate)){
21442                     this.date = newDate;
21443                     this.viewDate = newViewDate;
21444                     this.setValue(this.formatDate(this.date));
21445 //                    this.update();
21446                     e.preventDefault();
21447                     dateChanged = true;
21448                 }
21449                 break;
21450             case 38: // up
21451             case 40: // down
21452                 if (!this.keyboardNavigation) {
21453                     break;
21454                 }
21455                 dir = e.keyCode == 38 ? -1 : 1;
21456                 if (e.ctrlKey){
21457                     newDate = this.moveYear(this.date, dir);
21458                     newViewDate = this.moveYear(this.viewDate, dir);
21459                 } else if (e.shiftKey){
21460                     newDate = this.moveMonth(this.date, dir);
21461                     newViewDate = this.moveMonth(this.viewDate, dir);
21462                 } else {
21463                     newDate = new Date(this.date);
21464                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21465                     newViewDate = new Date(this.viewDate);
21466                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21467                 }
21468                 if (this.dateWithinRange(newDate)){
21469                     this.date = newDate;
21470                     this.viewDate = newViewDate;
21471                     this.setValue(this.formatDate(this.date));
21472 //                    this.update();
21473                     e.preventDefault();
21474                     dateChanged = true;
21475                 }
21476                 break;
21477             case 13: // enter
21478                 this.setValue(this.formatDate(this.date));
21479                 this.hidePopup();
21480                 e.preventDefault();
21481                 break;
21482             case 9: // tab
21483                 this.setValue(this.formatDate(this.date));
21484                 this.hidePopup();
21485                 break;
21486             case 16: // shift
21487             case 17: // ctrl
21488             case 18: // alt
21489                 break;
21490             default :
21491                 this.hidePopup();
21492                 
21493         }
21494     },
21495     
21496     
21497     onClick: function(e) 
21498     {
21499         e.stopPropagation();
21500         e.preventDefault();
21501         
21502         var target = e.getTarget();
21503         
21504         if(target.nodeName.toLowerCase() === 'i'){
21505             target = Roo.get(target).dom.parentNode;
21506         }
21507         
21508         var nodeName = target.nodeName;
21509         var className = target.className;
21510         var html = target.innerHTML;
21511         //Roo.log(nodeName);
21512         
21513         switch(nodeName.toLowerCase()) {
21514             case 'th':
21515                 switch(className) {
21516                     case 'switch':
21517                         this.showMode(1);
21518                         break;
21519                     case 'prev':
21520                     case 'next':
21521                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21522                         switch(this.viewMode){
21523                                 case 0:
21524                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21525                                         break;
21526                                 case 1:
21527                                 case 2:
21528                                         this.viewDate = this.moveYear(this.viewDate, dir);
21529                                         break;
21530                         }
21531                         this.fill();
21532                         break;
21533                     case 'today':
21534                         var date = new Date();
21535                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21536 //                        this.fill()
21537                         this.setValue(this.formatDate(this.date));
21538                         
21539                         this.hidePopup();
21540                         break;
21541                 }
21542                 break;
21543             case 'span':
21544                 if (className.indexOf('disabled') < 0) {
21545                     this.viewDate.setUTCDate(1);
21546                     if (className.indexOf('month') > -1) {
21547                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21548                     } else {
21549                         var year = parseInt(html, 10) || 0;
21550                         this.viewDate.setUTCFullYear(year);
21551                         
21552                     }
21553                     
21554                     if(this.singleMode){
21555                         this.setValue(this.formatDate(this.viewDate));
21556                         this.hidePopup();
21557                         return;
21558                     }
21559                     
21560                     this.showMode(-1);
21561                     this.fill();
21562                 }
21563                 break;
21564                 
21565             case 'td':
21566                 //Roo.log(className);
21567                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21568                     var day = parseInt(html, 10) || 1;
21569                     var year = this.viewDate.getUTCFullYear(),
21570                         month = this.viewDate.getUTCMonth();
21571
21572                     if (className.indexOf('old') > -1) {
21573                         if(month === 0 ){
21574                             month = 11;
21575                             year -= 1;
21576                         }else{
21577                             month -= 1;
21578                         }
21579                     } else if (className.indexOf('new') > -1) {
21580                         if (month == 11) {
21581                             month = 0;
21582                             year += 1;
21583                         } else {
21584                             month += 1;
21585                         }
21586                     }
21587                     //Roo.log([year,month,day]);
21588                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21589                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21590 //                    this.fill();
21591                     //Roo.log(this.formatDate(this.date));
21592                     this.setValue(this.formatDate(this.date));
21593                     this.hidePopup();
21594                 }
21595                 break;
21596         }
21597     },
21598     
21599     setStartDate: function(startDate)
21600     {
21601         this.startDate = startDate || -Infinity;
21602         if (this.startDate !== -Infinity) {
21603             this.startDate = this.parseDate(this.startDate);
21604         }
21605         this.update();
21606         this.updateNavArrows();
21607     },
21608
21609     setEndDate: function(endDate)
21610     {
21611         this.endDate = endDate || Infinity;
21612         if (this.endDate !== Infinity) {
21613             this.endDate = this.parseDate(this.endDate);
21614         }
21615         this.update();
21616         this.updateNavArrows();
21617     },
21618     
21619     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21620     {
21621         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21622         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21623             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21624         }
21625         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21626             return parseInt(d, 10);
21627         });
21628         this.update();
21629         this.updateNavArrows();
21630     },
21631     
21632     updateNavArrows: function() 
21633     {
21634         if(this.singleMode){
21635             return;
21636         }
21637         
21638         var d = new Date(this.viewDate),
21639         year = d.getUTCFullYear(),
21640         month = d.getUTCMonth();
21641         
21642         Roo.each(this.picker().select('.prev', true).elements, function(v){
21643             v.show();
21644             switch (this.viewMode) {
21645                 case 0:
21646
21647                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21648                         v.hide();
21649                     }
21650                     break;
21651                 case 1:
21652                 case 2:
21653                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21654                         v.hide();
21655                     }
21656                     break;
21657             }
21658         });
21659         
21660         Roo.each(this.picker().select('.next', true).elements, function(v){
21661             v.show();
21662             switch (this.viewMode) {
21663                 case 0:
21664
21665                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21666                         v.hide();
21667                     }
21668                     break;
21669                 case 1:
21670                 case 2:
21671                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21672                         v.hide();
21673                     }
21674                     break;
21675             }
21676         })
21677     },
21678     
21679     moveMonth: function(date, dir)
21680     {
21681         if (!dir) {
21682             return date;
21683         }
21684         var new_date = new Date(date.valueOf()),
21685         day = new_date.getUTCDate(),
21686         month = new_date.getUTCMonth(),
21687         mag = Math.abs(dir),
21688         new_month, test;
21689         dir = dir > 0 ? 1 : -1;
21690         if (mag == 1){
21691             test = dir == -1
21692             // If going back one month, make sure month is not current month
21693             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21694             ? function(){
21695                 return new_date.getUTCMonth() == month;
21696             }
21697             // If going forward one month, make sure month is as expected
21698             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21699             : function(){
21700                 return new_date.getUTCMonth() != new_month;
21701             };
21702             new_month = month + dir;
21703             new_date.setUTCMonth(new_month);
21704             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21705             if (new_month < 0 || new_month > 11) {
21706                 new_month = (new_month + 12) % 12;
21707             }
21708         } else {
21709             // For magnitudes >1, move one month at a time...
21710             for (var i=0; i<mag; i++) {
21711                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21712                 new_date = this.moveMonth(new_date, dir);
21713             }
21714             // ...then reset the day, keeping it in the new month
21715             new_month = new_date.getUTCMonth();
21716             new_date.setUTCDate(day);
21717             test = function(){
21718                 return new_month != new_date.getUTCMonth();
21719             };
21720         }
21721         // Common date-resetting loop -- if date is beyond end of month, make it
21722         // end of month
21723         while (test()){
21724             new_date.setUTCDate(--day);
21725             new_date.setUTCMonth(new_month);
21726         }
21727         return new_date;
21728     },
21729
21730     moveYear: function(date, dir)
21731     {
21732         return this.moveMonth(date, dir*12);
21733     },
21734
21735     dateWithinRange: function(date)
21736     {
21737         return date >= this.startDate && date <= this.endDate;
21738     },
21739
21740     
21741     remove: function() 
21742     {
21743         this.picker().remove();
21744     },
21745     
21746     validateValue : function(value)
21747     {
21748         if(this.getVisibilityEl().hasClass('hidden')){
21749             return true;
21750         }
21751         
21752         if(value.length < 1)  {
21753             if(this.allowBlank){
21754                 return true;
21755             }
21756             return false;
21757         }
21758         
21759         if(value.length < this.minLength){
21760             return false;
21761         }
21762         if(value.length > this.maxLength){
21763             return false;
21764         }
21765         if(this.vtype){
21766             var vt = Roo.form.VTypes;
21767             if(!vt[this.vtype](value, this)){
21768                 return false;
21769             }
21770         }
21771         if(typeof this.validator == "function"){
21772             var msg = this.validator(value);
21773             if(msg !== true){
21774                 return false;
21775             }
21776         }
21777         
21778         if(this.regex && !this.regex.test(value)){
21779             return false;
21780         }
21781         
21782         if(typeof(this.parseDate(value)) == 'undefined'){
21783             return false;
21784         }
21785         
21786         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21787             return false;
21788         }      
21789         
21790         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21791             return false;
21792         } 
21793         
21794         
21795         return true;
21796     },
21797     
21798     reset : function()
21799     {
21800         this.date = this.viewDate = '';
21801         
21802         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21803     }
21804    
21805 });
21806
21807 Roo.apply(Roo.bootstrap.DateField,  {
21808     
21809     head : {
21810         tag: 'thead',
21811         cn: [
21812         {
21813             tag: 'tr',
21814             cn: [
21815             {
21816                 tag: 'th',
21817                 cls: 'prev',
21818                 html: '<i class="fa fa-arrow-left"/>'
21819             },
21820             {
21821                 tag: 'th',
21822                 cls: 'switch',
21823                 colspan: '5'
21824             },
21825             {
21826                 tag: 'th',
21827                 cls: 'next',
21828                 html: '<i class="fa fa-arrow-right"/>'
21829             }
21830
21831             ]
21832         }
21833         ]
21834     },
21835     
21836     content : {
21837         tag: 'tbody',
21838         cn: [
21839         {
21840             tag: 'tr',
21841             cn: [
21842             {
21843                 tag: 'td',
21844                 colspan: '7'
21845             }
21846             ]
21847         }
21848         ]
21849     },
21850     
21851     footer : {
21852         tag: 'tfoot',
21853         cn: [
21854         {
21855             tag: 'tr',
21856             cn: [
21857             {
21858                 tag: 'th',
21859                 colspan: '7',
21860                 cls: 'today'
21861             }
21862                     
21863             ]
21864         }
21865         ]
21866     },
21867     
21868     dates:{
21869         en: {
21870             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21871             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21872             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21873             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21874             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21875             today: "Today"
21876         }
21877     },
21878     
21879     modes: [
21880     {
21881         clsName: 'days',
21882         navFnc: 'Month',
21883         navStep: 1
21884     },
21885     {
21886         clsName: 'months',
21887         navFnc: 'FullYear',
21888         navStep: 1
21889     },
21890     {
21891         clsName: 'years',
21892         navFnc: 'FullYear',
21893         navStep: 10
21894     }]
21895 });
21896
21897 Roo.apply(Roo.bootstrap.DateField,  {
21898   
21899     template : {
21900         tag: 'div',
21901         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21902         cn: [
21903         {
21904             tag: 'div',
21905             cls: 'datepicker-days',
21906             cn: [
21907             {
21908                 tag: 'table',
21909                 cls: 'table-condensed',
21910                 cn:[
21911                 Roo.bootstrap.DateField.head,
21912                 {
21913                     tag: 'tbody'
21914                 },
21915                 Roo.bootstrap.DateField.footer
21916                 ]
21917             }
21918             ]
21919         },
21920         {
21921             tag: 'div',
21922             cls: 'datepicker-months',
21923             cn: [
21924             {
21925                 tag: 'table',
21926                 cls: 'table-condensed',
21927                 cn:[
21928                 Roo.bootstrap.DateField.head,
21929                 Roo.bootstrap.DateField.content,
21930                 Roo.bootstrap.DateField.footer
21931                 ]
21932             }
21933             ]
21934         },
21935         {
21936             tag: 'div',
21937             cls: 'datepicker-years',
21938             cn: [
21939             {
21940                 tag: 'table',
21941                 cls: 'table-condensed',
21942                 cn:[
21943                 Roo.bootstrap.DateField.head,
21944                 Roo.bootstrap.DateField.content,
21945                 Roo.bootstrap.DateField.footer
21946                 ]
21947             }
21948             ]
21949         }
21950         ]
21951     }
21952 });
21953
21954  
21955
21956  /*
21957  * - LGPL
21958  *
21959  * TimeField
21960  * 
21961  */
21962
21963 /**
21964  * @class Roo.bootstrap.TimeField
21965  * @extends Roo.bootstrap.Input
21966  * Bootstrap DateField class
21967  * 
21968  * 
21969  * @constructor
21970  * Create a new TimeField
21971  * @param {Object} config The config object
21972  */
21973
21974 Roo.bootstrap.TimeField = function(config){
21975     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21976     this.addEvents({
21977             /**
21978              * @event show
21979              * Fires when this field show.
21980              * @param {Roo.bootstrap.DateField} thisthis
21981              * @param {Mixed} date The date value
21982              */
21983             show : true,
21984             /**
21985              * @event show
21986              * Fires when this field hide.
21987              * @param {Roo.bootstrap.DateField} this
21988              * @param {Mixed} date The date value
21989              */
21990             hide : true,
21991             /**
21992              * @event select
21993              * Fires when select a date.
21994              * @param {Roo.bootstrap.DateField} this
21995              * @param {Mixed} date The date value
21996              */
21997             select : true
21998         });
21999 };
22000
22001 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22002     
22003     /**
22004      * @cfg {String} format
22005      * The default time format string which can be overriden for localization support.  The format must be
22006      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22007      */
22008     format : "H:i",
22009
22010     getAutoCreate : function()
22011     {
22012         this.after = '<i class="fa far fa-clock"></i>';
22013         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22014         
22015          
22016     },
22017     onRender: function(ct, position)
22018     {
22019         
22020         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22021                 
22022         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22023         
22024         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22025         
22026         this.pop = this.picker().select('>.datepicker-time',true).first();
22027         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22028         
22029         this.picker().on('mousedown', this.onMousedown, this);
22030         this.picker().on('click', this.onClick, this);
22031         
22032         this.picker().addClass('datepicker-dropdown');
22033     
22034         this.fillTime();
22035         this.update();
22036             
22037         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22038         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22039         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22040         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22041         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22042         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22043
22044     },
22045     
22046     fireKey: function(e){
22047         if (!this.picker().isVisible()){
22048             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22049                 this.show();
22050             }
22051             return;
22052         }
22053
22054         e.preventDefault();
22055         
22056         switch(e.keyCode){
22057             case 27: // escape
22058                 this.hide();
22059                 break;
22060             case 37: // left
22061             case 39: // right
22062                 this.onTogglePeriod();
22063                 break;
22064             case 38: // up
22065                 this.onIncrementMinutes();
22066                 break;
22067             case 40: // down
22068                 this.onDecrementMinutes();
22069                 break;
22070             case 13: // enter
22071             case 9: // tab
22072                 this.setTime();
22073                 break;
22074         }
22075     },
22076     
22077     onClick: function(e) {
22078         e.stopPropagation();
22079         e.preventDefault();
22080     },
22081     
22082     picker : function()
22083     {
22084         return this.pickerEl;
22085     },
22086     
22087     fillTime: function()
22088     {    
22089         var time = this.pop.select('tbody', true).first();
22090         
22091         time.dom.innerHTML = '';
22092         
22093         time.createChild({
22094             tag: 'tr',
22095             cn: [
22096                 {
22097                     tag: 'td',
22098                     cn: [
22099                         {
22100                             tag: 'a',
22101                             href: '#',
22102                             cls: 'btn',
22103                             cn: [
22104                                 {
22105                                     tag: 'i',
22106                                     cls: 'hours-up fa fas fa-chevron-up'
22107                                 }
22108                             ]
22109                         } 
22110                     ]
22111                 },
22112                 {
22113                     tag: 'td',
22114                     cls: 'separator'
22115                 },
22116                 {
22117                     tag: 'td',
22118                     cn: [
22119                         {
22120                             tag: 'a',
22121                             href: '#',
22122                             cls: 'btn',
22123                             cn: [
22124                                 {
22125                                     tag: 'i',
22126                                     cls: 'minutes-up fa fas fa-chevron-up'
22127                                 }
22128                             ]
22129                         }
22130                     ]
22131                 },
22132                 {
22133                     tag: 'td',
22134                     cls: 'separator'
22135                 }
22136             ]
22137         });
22138         
22139         time.createChild({
22140             tag: 'tr',
22141             cn: [
22142                 {
22143                     tag: 'td',
22144                     cn: [
22145                         {
22146                             tag: 'span',
22147                             cls: 'timepicker-hour',
22148                             html: '00'
22149                         }  
22150                     ]
22151                 },
22152                 {
22153                     tag: 'td',
22154                     cls: 'separator',
22155                     html: ':'
22156                 },
22157                 {
22158                     tag: 'td',
22159                     cn: [
22160                         {
22161                             tag: 'span',
22162                             cls: 'timepicker-minute',
22163                             html: '00'
22164                         }  
22165                     ]
22166                 },
22167                 {
22168                     tag: 'td',
22169                     cls: 'separator'
22170                 },
22171                 {
22172                     tag: 'td',
22173                     cn: [
22174                         {
22175                             tag: 'button',
22176                             type: 'button',
22177                             cls: 'btn btn-primary period',
22178                             html: 'AM'
22179                             
22180                         }
22181                     ]
22182                 }
22183             ]
22184         });
22185         
22186         time.createChild({
22187             tag: 'tr',
22188             cn: [
22189                 {
22190                     tag: 'td',
22191                     cn: [
22192                         {
22193                             tag: 'a',
22194                             href: '#',
22195                             cls: 'btn',
22196                             cn: [
22197                                 {
22198                                     tag: 'span',
22199                                     cls: 'hours-down fa fas fa-chevron-down'
22200                                 }
22201                             ]
22202                         }
22203                     ]
22204                 },
22205                 {
22206                     tag: 'td',
22207                     cls: 'separator'
22208                 },
22209                 {
22210                     tag: 'td',
22211                     cn: [
22212                         {
22213                             tag: 'a',
22214                             href: '#',
22215                             cls: 'btn',
22216                             cn: [
22217                                 {
22218                                     tag: 'span',
22219                                     cls: 'minutes-down fa fas fa-chevron-down'
22220                                 }
22221                             ]
22222                         }
22223                     ]
22224                 },
22225                 {
22226                     tag: 'td',
22227                     cls: 'separator'
22228                 }
22229             ]
22230         });
22231         
22232     },
22233     
22234     update: function()
22235     {
22236         
22237         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22238         
22239         this.fill();
22240     },
22241     
22242     fill: function() 
22243     {
22244         var hours = this.time.getHours();
22245         var minutes = this.time.getMinutes();
22246         var period = 'AM';
22247         
22248         if(hours > 11){
22249             period = 'PM';
22250         }
22251         
22252         if(hours == 0){
22253             hours = 12;
22254         }
22255         
22256         
22257         if(hours > 12){
22258             hours = hours - 12;
22259         }
22260         
22261         if(hours < 10){
22262             hours = '0' + hours;
22263         }
22264         
22265         if(minutes < 10){
22266             minutes = '0' + minutes;
22267         }
22268         
22269         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22270         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22271         this.pop.select('button', true).first().dom.innerHTML = period;
22272         
22273     },
22274     
22275     place: function()
22276     {   
22277         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22278         
22279         var cls = ['bottom'];
22280         
22281         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22282             cls.pop();
22283             cls.push('top');
22284         }
22285         
22286         cls.push('right');
22287         
22288         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22289             cls.pop();
22290             cls.push('left');
22291         }
22292         //this.picker().setXY(20000,20000);
22293         this.picker().addClass(cls.join('-'));
22294         
22295         var _this = this;
22296         
22297         Roo.each(cls, function(c){
22298             if(c == 'bottom'){
22299                 (function() {
22300                  //  
22301                 }).defer(200);
22302                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22303                 //_this.picker().setTop(_this.inputEl().getHeight());
22304                 return;
22305             }
22306             if(c == 'top'){
22307                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22308                 
22309                 //_this.picker().setTop(0 - _this.picker().getHeight());
22310                 return;
22311             }
22312             /*
22313             if(c == 'left'){
22314                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22315                 return;
22316             }
22317             if(c == 'right'){
22318                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22319                 return;
22320             }
22321             */
22322         });
22323         
22324     },
22325   
22326     onFocus : function()
22327     {
22328         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22329         this.show();
22330     },
22331     
22332     onBlur : function()
22333     {
22334         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22335         this.hide();
22336     },
22337     
22338     show : function()
22339     {
22340         this.picker().show();
22341         this.pop.show();
22342         this.update();
22343         this.place();
22344         
22345         this.fireEvent('show', this, this.date);
22346     },
22347     
22348     hide : function()
22349     {
22350         this.picker().hide();
22351         this.pop.hide();
22352         
22353         this.fireEvent('hide', this, this.date);
22354     },
22355     
22356     setTime : function()
22357     {
22358         this.hide();
22359         this.setValue(this.time.format(this.format));
22360         
22361         this.fireEvent('select', this, this.date);
22362         
22363         
22364     },
22365     
22366     onMousedown: function(e){
22367         e.stopPropagation();
22368         e.preventDefault();
22369     },
22370     
22371     onIncrementHours: function()
22372     {
22373         Roo.log('onIncrementHours');
22374         this.time = this.time.add(Date.HOUR, 1);
22375         this.update();
22376         
22377     },
22378     
22379     onDecrementHours: function()
22380     {
22381         Roo.log('onDecrementHours');
22382         this.time = this.time.add(Date.HOUR, -1);
22383         this.update();
22384     },
22385     
22386     onIncrementMinutes: function()
22387     {
22388         Roo.log('onIncrementMinutes');
22389         this.time = this.time.add(Date.MINUTE, 1);
22390         this.update();
22391     },
22392     
22393     onDecrementMinutes: function()
22394     {
22395         Roo.log('onDecrementMinutes');
22396         this.time = this.time.add(Date.MINUTE, -1);
22397         this.update();
22398     },
22399     
22400     onTogglePeriod: function()
22401     {
22402         Roo.log('onTogglePeriod');
22403         this.time = this.time.add(Date.HOUR, 12);
22404         this.update();
22405     }
22406     
22407    
22408 });
22409  
22410
22411 Roo.apply(Roo.bootstrap.TimeField,  {
22412   
22413     template : {
22414         tag: 'div',
22415         cls: 'datepicker dropdown-menu',
22416         cn: [
22417             {
22418                 tag: 'div',
22419                 cls: 'datepicker-time',
22420                 cn: [
22421                 {
22422                     tag: 'table',
22423                     cls: 'table-condensed',
22424                     cn:[
22425                         {
22426                             tag: 'tbody',
22427                             cn: [
22428                                 {
22429                                     tag: 'tr',
22430                                     cn: [
22431                                     {
22432                                         tag: 'td',
22433                                         colspan: '7'
22434                                     }
22435                                     ]
22436                                 }
22437                             ]
22438                         },
22439                         {
22440                             tag: 'tfoot',
22441                             cn: [
22442                                 {
22443                                     tag: 'tr',
22444                                     cn: [
22445                                     {
22446                                         tag: 'th',
22447                                         colspan: '7',
22448                                         cls: '',
22449                                         cn: [
22450                                             {
22451                                                 tag: 'button',
22452                                                 cls: 'btn btn-info ok',
22453                                                 html: 'OK'
22454                                             }
22455                                         ]
22456                                     }
22457                     
22458                                     ]
22459                                 }
22460                             ]
22461                         }
22462                     ]
22463                 }
22464                 ]
22465             }
22466         ]
22467     }
22468 });
22469
22470  
22471
22472  /*
22473  * - LGPL
22474  *
22475  * MonthField
22476  * 
22477  */
22478
22479 /**
22480  * @class Roo.bootstrap.MonthField
22481  * @extends Roo.bootstrap.Input
22482  * Bootstrap MonthField class
22483  * 
22484  * @cfg {String} language default en
22485  * 
22486  * @constructor
22487  * Create a new MonthField
22488  * @param {Object} config The config object
22489  */
22490
22491 Roo.bootstrap.MonthField = function(config){
22492     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22493     
22494     this.addEvents({
22495         /**
22496          * @event show
22497          * Fires when this field show.
22498          * @param {Roo.bootstrap.MonthField} this
22499          * @param {Mixed} date The date value
22500          */
22501         show : true,
22502         /**
22503          * @event show
22504          * Fires when this field hide.
22505          * @param {Roo.bootstrap.MonthField} this
22506          * @param {Mixed} date The date value
22507          */
22508         hide : true,
22509         /**
22510          * @event select
22511          * Fires when select a date.
22512          * @param {Roo.bootstrap.MonthField} this
22513          * @param {String} oldvalue The old value
22514          * @param {String} newvalue The new value
22515          */
22516         select : true
22517     });
22518 };
22519
22520 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22521     
22522     onRender: function(ct, position)
22523     {
22524         
22525         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22526         
22527         this.language = this.language || 'en';
22528         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22529         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22530         
22531         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22532         this.isInline = false;
22533         this.isInput = true;
22534         this.component = this.el.select('.add-on', true).first() || false;
22535         this.component = (this.component && this.component.length === 0) ? false : this.component;
22536         this.hasInput = this.component && this.inputEL().length;
22537         
22538         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22539         
22540         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22541         
22542         this.picker().on('mousedown', this.onMousedown, this);
22543         this.picker().on('click', this.onClick, this);
22544         
22545         this.picker().addClass('datepicker-dropdown');
22546         
22547         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22548             v.setStyle('width', '189px');
22549         });
22550         
22551         this.fillMonths();
22552         
22553         this.update();
22554         
22555         if(this.isInline) {
22556             this.show();
22557         }
22558         
22559     },
22560     
22561     setValue: function(v, suppressEvent)
22562     {   
22563         var o = this.getValue();
22564         
22565         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22566         
22567         this.update();
22568
22569         if(suppressEvent !== true){
22570             this.fireEvent('select', this, o, v);
22571         }
22572         
22573     },
22574     
22575     getValue: function()
22576     {
22577         return this.value;
22578     },
22579     
22580     onClick: function(e) 
22581     {
22582         e.stopPropagation();
22583         e.preventDefault();
22584         
22585         var target = e.getTarget();
22586         
22587         if(target.nodeName.toLowerCase() === 'i'){
22588             target = Roo.get(target).dom.parentNode;
22589         }
22590         
22591         var nodeName = target.nodeName;
22592         var className = target.className;
22593         var html = target.innerHTML;
22594         
22595         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22596             return;
22597         }
22598         
22599         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22600         
22601         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22602         
22603         this.hide();
22604                         
22605     },
22606     
22607     picker : function()
22608     {
22609         return this.pickerEl;
22610     },
22611     
22612     fillMonths: function()
22613     {    
22614         var i = 0;
22615         var months = this.picker().select('>.datepicker-months td', true).first();
22616         
22617         months.dom.innerHTML = '';
22618         
22619         while (i < 12) {
22620             var month = {
22621                 tag: 'span',
22622                 cls: 'month',
22623                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22624             };
22625             
22626             months.createChild(month);
22627         }
22628         
22629     },
22630     
22631     update: function()
22632     {
22633         var _this = this;
22634         
22635         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22636             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22637         }
22638         
22639         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22640             e.removeClass('active');
22641             
22642             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22643                 e.addClass('active');
22644             }
22645         })
22646     },
22647     
22648     place: function()
22649     {
22650         if(this.isInline) {
22651             return;
22652         }
22653         
22654         this.picker().removeClass(['bottom', 'top']);
22655         
22656         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22657             /*
22658              * place to the top of element!
22659              *
22660              */
22661             
22662             this.picker().addClass('top');
22663             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22664             
22665             return;
22666         }
22667         
22668         this.picker().addClass('bottom');
22669         
22670         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22671     },
22672     
22673     onFocus : function()
22674     {
22675         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22676         this.show();
22677     },
22678     
22679     onBlur : function()
22680     {
22681         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22682         
22683         var d = this.inputEl().getValue();
22684         
22685         this.setValue(d);
22686                 
22687         this.hide();
22688     },
22689     
22690     show : function()
22691     {
22692         this.picker().show();
22693         this.picker().select('>.datepicker-months', true).first().show();
22694         this.update();
22695         this.place();
22696         
22697         this.fireEvent('show', this, this.date);
22698     },
22699     
22700     hide : function()
22701     {
22702         if(this.isInline) {
22703             return;
22704         }
22705         this.picker().hide();
22706         this.fireEvent('hide', this, this.date);
22707         
22708     },
22709     
22710     onMousedown: function(e)
22711     {
22712         e.stopPropagation();
22713         e.preventDefault();
22714     },
22715     
22716     keyup: function(e)
22717     {
22718         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22719         this.update();
22720     },
22721
22722     fireKey: function(e)
22723     {
22724         if (!this.picker().isVisible()){
22725             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22726                 this.show();
22727             }
22728             return;
22729         }
22730         
22731         var dir;
22732         
22733         switch(e.keyCode){
22734             case 27: // escape
22735                 this.hide();
22736                 e.preventDefault();
22737                 break;
22738             case 37: // left
22739             case 39: // right
22740                 dir = e.keyCode == 37 ? -1 : 1;
22741                 
22742                 this.vIndex = this.vIndex + dir;
22743                 
22744                 if(this.vIndex < 0){
22745                     this.vIndex = 0;
22746                 }
22747                 
22748                 if(this.vIndex > 11){
22749                     this.vIndex = 11;
22750                 }
22751                 
22752                 if(isNaN(this.vIndex)){
22753                     this.vIndex = 0;
22754                 }
22755                 
22756                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22757                 
22758                 break;
22759             case 38: // up
22760             case 40: // down
22761                 
22762                 dir = e.keyCode == 38 ? -1 : 1;
22763                 
22764                 this.vIndex = this.vIndex + dir * 4;
22765                 
22766                 if(this.vIndex < 0){
22767                     this.vIndex = 0;
22768                 }
22769                 
22770                 if(this.vIndex > 11){
22771                     this.vIndex = 11;
22772                 }
22773                 
22774                 if(isNaN(this.vIndex)){
22775                     this.vIndex = 0;
22776                 }
22777                 
22778                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22779                 break;
22780                 
22781             case 13: // enter
22782                 
22783                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22784                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22785                 }
22786                 
22787                 this.hide();
22788                 e.preventDefault();
22789                 break;
22790             case 9: // tab
22791                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22792                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22793                 }
22794                 this.hide();
22795                 break;
22796             case 16: // shift
22797             case 17: // ctrl
22798             case 18: // alt
22799                 break;
22800             default :
22801                 this.hide();
22802                 
22803         }
22804     },
22805     
22806     remove: function() 
22807     {
22808         this.picker().remove();
22809     }
22810    
22811 });
22812
22813 Roo.apply(Roo.bootstrap.MonthField,  {
22814     
22815     content : {
22816         tag: 'tbody',
22817         cn: [
22818         {
22819             tag: 'tr',
22820             cn: [
22821             {
22822                 tag: 'td',
22823                 colspan: '7'
22824             }
22825             ]
22826         }
22827         ]
22828     },
22829     
22830     dates:{
22831         en: {
22832             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22833             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22834         }
22835     }
22836 });
22837
22838 Roo.apply(Roo.bootstrap.MonthField,  {
22839   
22840     template : {
22841         tag: 'div',
22842         cls: 'datepicker dropdown-menu roo-dynamic',
22843         cn: [
22844             {
22845                 tag: 'div',
22846                 cls: 'datepicker-months',
22847                 cn: [
22848                 {
22849                     tag: 'table',
22850                     cls: 'table-condensed',
22851                     cn:[
22852                         Roo.bootstrap.DateField.content
22853                     ]
22854                 }
22855                 ]
22856             }
22857         ]
22858     }
22859 });
22860
22861  
22862
22863  
22864  /*
22865  * - LGPL
22866  *
22867  * CheckBox
22868  * 
22869  */
22870
22871 /**
22872  * @class Roo.bootstrap.CheckBox
22873  * @extends Roo.bootstrap.Input
22874  * Bootstrap CheckBox class
22875  * 
22876  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22877  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22878  * @cfg {String} boxLabel The text that appears beside the checkbox
22879  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22880  * @cfg {Boolean} checked initnal the element
22881  * @cfg {Boolean} inline inline the element (default false)
22882  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22883  * @cfg {String} tooltip label tooltip
22884  * 
22885  * @constructor
22886  * Create a new CheckBox
22887  * @param {Object} config The config object
22888  */
22889
22890 Roo.bootstrap.CheckBox = function(config){
22891     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22892    
22893     this.addEvents({
22894         /**
22895         * @event check
22896         * Fires when the element is checked or unchecked.
22897         * @param {Roo.bootstrap.CheckBox} this This input
22898         * @param {Boolean} checked The new checked value
22899         */
22900        check : true,
22901        /**
22902         * @event click
22903         * Fires when the element is click.
22904         * @param {Roo.bootstrap.CheckBox} this This input
22905         */
22906        click : true
22907     });
22908     
22909 };
22910
22911 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22912   
22913     inputType: 'checkbox',
22914     inputValue: 1,
22915     valueOff: 0,
22916     boxLabel: false,
22917     checked: false,
22918     weight : false,
22919     inline: false,
22920     tooltip : '',
22921     
22922     // checkbox success does not make any sense really.. 
22923     invalidClass : "",
22924     validClass : "",
22925     
22926     
22927     getAutoCreate : function()
22928     {
22929         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22930         
22931         var id = Roo.id();
22932         
22933         var cfg = {};
22934         
22935         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22936         
22937         if(this.inline){
22938             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22939         }
22940         
22941         var input =  {
22942             tag: 'input',
22943             id : id,
22944             type : this.inputType,
22945             value : this.inputValue,
22946             cls : 'roo-' + this.inputType, //'form-box',
22947             placeholder : this.placeholder || ''
22948             
22949         };
22950         
22951         if(this.inputType != 'radio'){
22952             var hidden =  {
22953                 tag: 'input',
22954                 type : 'hidden',
22955                 cls : 'roo-hidden-value',
22956                 value : this.checked ? this.inputValue : this.valueOff
22957             };
22958         }
22959         
22960             
22961         if (this.weight) { // Validity check?
22962             cfg.cls += " " + this.inputType + "-" + this.weight;
22963         }
22964         
22965         if (this.disabled) {
22966             input.disabled=true;
22967         }
22968         
22969         if(this.checked){
22970             input.checked = this.checked;
22971         }
22972         
22973         if (this.name) {
22974             
22975             input.name = this.name;
22976             
22977             if(this.inputType != 'radio'){
22978                 hidden.name = this.name;
22979                 input.name = '_hidden_' + this.name;
22980             }
22981         }
22982         
22983         if (this.size) {
22984             input.cls += ' input-' + this.size;
22985         }
22986         
22987         var settings=this;
22988         
22989         ['xs','sm','md','lg'].map(function(size){
22990             if (settings[size]) {
22991                 cfg.cls += ' col-' + size + '-' + settings[size];
22992             }
22993         });
22994         
22995         var inputblock = input;
22996          
22997         if (this.before || this.after) {
22998             
22999             inputblock = {
23000                 cls : 'input-group',
23001                 cn :  [] 
23002             };
23003             
23004             if (this.before) {
23005                 inputblock.cn.push({
23006                     tag :'span',
23007                     cls : 'input-group-addon',
23008                     html : this.before
23009                 });
23010             }
23011             
23012             inputblock.cn.push(input);
23013             
23014             if(this.inputType != 'radio'){
23015                 inputblock.cn.push(hidden);
23016             }
23017             
23018             if (this.after) {
23019                 inputblock.cn.push({
23020                     tag :'span',
23021                     cls : 'input-group-addon',
23022                     html : this.after
23023                 });
23024             }
23025             
23026         }
23027         var boxLabelCfg = false;
23028         
23029         if(this.boxLabel){
23030            
23031             boxLabelCfg = {
23032                 tag: 'label',
23033                 //'for': id, // box label is handled by onclick - so no for...
23034                 cls: 'box-label',
23035                 html: this.boxLabel
23036             };
23037             if(this.tooltip){
23038                 boxLabelCfg.tooltip = this.tooltip;
23039             }
23040              
23041         }
23042         
23043         
23044         if (align ==='left' && this.fieldLabel.length) {
23045 //                Roo.log("left and has label");
23046             cfg.cn = [
23047                 {
23048                     tag: 'label',
23049                     'for' :  id,
23050                     cls : 'control-label',
23051                     html : this.fieldLabel
23052                 },
23053                 {
23054                     cls : "", 
23055                     cn: [
23056                         inputblock
23057                     ]
23058                 }
23059             ];
23060             
23061             if (boxLabelCfg) {
23062                 cfg.cn[1].cn.push(boxLabelCfg);
23063             }
23064             
23065             if(this.labelWidth > 12){
23066                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23067             }
23068             
23069             if(this.labelWidth < 13 && this.labelmd == 0){
23070                 this.labelmd = this.labelWidth;
23071             }
23072             
23073             if(this.labellg > 0){
23074                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23075                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23076             }
23077             
23078             if(this.labelmd > 0){
23079                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23080                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23081             }
23082             
23083             if(this.labelsm > 0){
23084                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23085                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23086             }
23087             
23088             if(this.labelxs > 0){
23089                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23090                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23091             }
23092             
23093         } else if ( this.fieldLabel.length) {
23094 //                Roo.log(" label");
23095                 cfg.cn = [
23096                    
23097                     {
23098                         tag: this.boxLabel ? 'span' : 'label',
23099                         'for': id,
23100                         cls: 'control-label box-input-label',
23101                         //cls : 'input-group-addon',
23102                         html : this.fieldLabel
23103                     },
23104                     
23105                     inputblock
23106                     
23107                 ];
23108                 if (boxLabelCfg) {
23109                     cfg.cn.push(boxLabelCfg);
23110                 }
23111
23112         } else {
23113             
23114 //                Roo.log(" no label && no align");
23115                 cfg.cn = [  inputblock ] ;
23116                 if (boxLabelCfg) {
23117                     cfg.cn.push(boxLabelCfg);
23118                 }
23119
23120                 
23121         }
23122         
23123        
23124         
23125         if(this.inputType != 'radio'){
23126             cfg.cn.push(hidden);
23127         }
23128         
23129         return cfg;
23130         
23131     },
23132     
23133     /**
23134      * return the real input element.
23135      */
23136     inputEl: function ()
23137     {
23138         return this.el.select('input.roo-' + this.inputType,true).first();
23139     },
23140     hiddenEl: function ()
23141     {
23142         return this.el.select('input.roo-hidden-value',true).first();
23143     },
23144     
23145     labelEl: function()
23146     {
23147         return this.el.select('label.control-label',true).first();
23148     },
23149     /* depricated... */
23150     
23151     label: function()
23152     {
23153         return this.labelEl();
23154     },
23155     
23156     boxLabelEl: function()
23157     {
23158         return this.el.select('label.box-label',true).first();
23159     },
23160     
23161     initEvents : function()
23162     {
23163 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23164         
23165         this.inputEl().on('click', this.onClick,  this);
23166         
23167         if (this.boxLabel) { 
23168             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23169         }
23170         
23171         this.startValue = this.getValue();
23172         
23173         if(this.groupId){
23174             Roo.bootstrap.CheckBox.register(this);
23175         }
23176     },
23177     
23178     onClick : function(e)
23179     {   
23180         if(this.fireEvent('click', this, e) !== false){
23181             this.setChecked(!this.checked);
23182         }
23183         
23184     },
23185     
23186     setChecked : function(state,suppressEvent)
23187     {
23188         this.startValue = this.getValue();
23189
23190         if(this.inputType == 'radio'){
23191             
23192             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23193                 e.dom.checked = false;
23194             });
23195             
23196             this.inputEl().dom.checked = true;
23197             
23198             this.inputEl().dom.value = this.inputValue;
23199             
23200             if(suppressEvent !== true){
23201                 this.fireEvent('check', this, true);
23202             }
23203             
23204             this.validate();
23205             
23206             return;
23207         }
23208         
23209         this.checked = state;
23210         
23211         this.inputEl().dom.checked = state;
23212         
23213         
23214         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23215         
23216         if(suppressEvent !== true){
23217             this.fireEvent('check', this, state);
23218         }
23219         
23220         this.validate();
23221     },
23222     
23223     getValue : function()
23224     {
23225         if(this.inputType == 'radio'){
23226             return this.getGroupValue();
23227         }
23228         
23229         return this.hiddenEl().dom.value;
23230         
23231     },
23232     
23233     getGroupValue : function()
23234     {
23235         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23236             return '';
23237         }
23238         
23239         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23240     },
23241     
23242     setValue : function(v,suppressEvent)
23243     {
23244         if(this.inputType == 'radio'){
23245             this.setGroupValue(v, suppressEvent);
23246             return;
23247         }
23248         
23249         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23250         
23251         this.validate();
23252     },
23253     
23254     setGroupValue : function(v, suppressEvent)
23255     {
23256         this.startValue = this.getValue();
23257         
23258         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23259             e.dom.checked = false;
23260             
23261             if(e.dom.value == v){
23262                 e.dom.checked = true;
23263             }
23264         });
23265         
23266         if(suppressEvent !== true){
23267             this.fireEvent('check', this, true);
23268         }
23269
23270         this.validate();
23271         
23272         return;
23273     },
23274     
23275     validate : function()
23276     {
23277         if(this.getVisibilityEl().hasClass('hidden')){
23278             return true;
23279         }
23280         
23281         if(
23282                 this.disabled || 
23283                 (this.inputType == 'radio' && this.validateRadio()) ||
23284                 (this.inputType == 'checkbox' && this.validateCheckbox())
23285         ){
23286             this.markValid();
23287             return true;
23288         }
23289         
23290         this.markInvalid();
23291         return false;
23292     },
23293     
23294     validateRadio : function()
23295     {
23296         if(this.getVisibilityEl().hasClass('hidden')){
23297             return true;
23298         }
23299         
23300         if(this.allowBlank){
23301             return true;
23302         }
23303         
23304         var valid = false;
23305         
23306         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23307             if(!e.dom.checked){
23308                 return;
23309             }
23310             
23311             valid = true;
23312             
23313             return false;
23314         });
23315         
23316         return valid;
23317     },
23318     
23319     validateCheckbox : function()
23320     {
23321         if(!this.groupId){
23322             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23323             //return (this.getValue() == this.inputValue) ? true : false;
23324         }
23325         
23326         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23327         
23328         if(!group){
23329             return false;
23330         }
23331         
23332         var r = false;
23333         
23334         for(var i in group){
23335             if(group[i].el.isVisible(true)){
23336                 r = false;
23337                 break;
23338             }
23339             
23340             r = true;
23341         }
23342         
23343         for(var i in group){
23344             if(r){
23345                 break;
23346             }
23347             
23348             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23349         }
23350         
23351         return r;
23352     },
23353     
23354     /**
23355      * Mark this field as valid
23356      */
23357     markValid : function()
23358     {
23359         var _this = this;
23360         
23361         this.fireEvent('valid', this);
23362         
23363         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23364         
23365         if(this.groupId){
23366             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23367         }
23368         
23369         if(label){
23370             label.markValid();
23371         }
23372
23373         if(this.inputType == 'radio'){
23374             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23375                 var fg = e.findParent('.form-group', false, true);
23376                 if (Roo.bootstrap.version == 3) {
23377                     fg.removeClass([_this.invalidClass, _this.validClass]);
23378                     fg.addClass(_this.validClass);
23379                 } else {
23380                     fg.removeClass(['is-valid', 'is-invalid']);
23381                     fg.addClass('is-valid');
23382                 }
23383             });
23384             
23385             return;
23386         }
23387
23388         if(!this.groupId){
23389             var fg = this.el.findParent('.form-group', false, true);
23390             if (Roo.bootstrap.version == 3) {
23391                 fg.removeClass([this.invalidClass, this.validClass]);
23392                 fg.addClass(this.validClass);
23393             } else {
23394                 fg.removeClass(['is-valid', 'is-invalid']);
23395                 fg.addClass('is-valid');
23396             }
23397             return;
23398         }
23399         
23400         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23401         
23402         if(!group){
23403             return;
23404         }
23405         
23406         for(var i in group){
23407             var fg = group[i].el.findParent('.form-group', false, true);
23408             if (Roo.bootstrap.version == 3) {
23409                 fg.removeClass([this.invalidClass, this.validClass]);
23410                 fg.addClass(this.validClass);
23411             } else {
23412                 fg.removeClass(['is-valid', 'is-invalid']);
23413                 fg.addClass('is-valid');
23414             }
23415         }
23416     },
23417     
23418      /**
23419      * Mark this field as invalid
23420      * @param {String} msg The validation message
23421      */
23422     markInvalid : function(msg)
23423     {
23424         if(this.allowBlank){
23425             return;
23426         }
23427         
23428         var _this = this;
23429         
23430         this.fireEvent('invalid', this, msg);
23431         
23432         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23433         
23434         if(this.groupId){
23435             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23436         }
23437         
23438         if(label){
23439             label.markInvalid();
23440         }
23441             
23442         if(this.inputType == 'radio'){
23443             
23444             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23445                 var fg = e.findParent('.form-group', false, true);
23446                 if (Roo.bootstrap.version == 3) {
23447                     fg.removeClass([_this.invalidClass, _this.validClass]);
23448                     fg.addClass(_this.invalidClass);
23449                 } else {
23450                     fg.removeClass(['is-invalid', 'is-valid']);
23451                     fg.addClass('is-invalid');
23452                 }
23453             });
23454             
23455             return;
23456         }
23457         
23458         if(!this.groupId){
23459             var fg = this.el.findParent('.form-group', false, true);
23460             if (Roo.bootstrap.version == 3) {
23461                 fg.removeClass([_this.invalidClass, _this.validClass]);
23462                 fg.addClass(_this.invalidClass);
23463             } else {
23464                 fg.removeClass(['is-invalid', 'is-valid']);
23465                 fg.addClass('is-invalid');
23466             }
23467             return;
23468         }
23469         
23470         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23471         
23472         if(!group){
23473             return;
23474         }
23475         
23476         for(var i in group){
23477             var fg = group[i].el.findParent('.form-group', false, true);
23478             if (Roo.bootstrap.version == 3) {
23479                 fg.removeClass([_this.invalidClass, _this.validClass]);
23480                 fg.addClass(_this.invalidClass);
23481             } else {
23482                 fg.removeClass(['is-invalid', 'is-valid']);
23483                 fg.addClass('is-invalid');
23484             }
23485         }
23486         
23487     },
23488     
23489     clearInvalid : function()
23490     {
23491         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23492         
23493         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23494         
23495         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23496         
23497         if (label && label.iconEl) {
23498             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23499             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23500         }
23501     },
23502     
23503     disable : function()
23504     {
23505         if(this.inputType != 'radio'){
23506             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23507             return;
23508         }
23509         
23510         var _this = this;
23511         
23512         if(this.rendered){
23513             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23514                 _this.getActionEl().addClass(this.disabledClass);
23515                 e.dom.disabled = true;
23516             });
23517         }
23518         
23519         this.disabled = true;
23520         this.fireEvent("disable", this);
23521         return this;
23522     },
23523
23524     enable : function()
23525     {
23526         if(this.inputType != 'radio'){
23527             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23528             return;
23529         }
23530         
23531         var _this = this;
23532         
23533         if(this.rendered){
23534             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23535                 _this.getActionEl().removeClass(this.disabledClass);
23536                 e.dom.disabled = false;
23537             });
23538         }
23539         
23540         this.disabled = false;
23541         this.fireEvent("enable", this);
23542         return this;
23543     },
23544     
23545     setBoxLabel : function(v)
23546     {
23547         this.boxLabel = v;
23548         
23549         if(this.rendered){
23550             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23551         }
23552     }
23553
23554 });
23555
23556 Roo.apply(Roo.bootstrap.CheckBox, {
23557     
23558     groups: {},
23559     
23560      /**
23561     * register a CheckBox Group
23562     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23563     */
23564     register : function(checkbox)
23565     {
23566         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23567             this.groups[checkbox.groupId] = {};
23568         }
23569         
23570         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23571             return;
23572         }
23573         
23574         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23575         
23576     },
23577     /**
23578     * fetch a CheckBox Group based on the group ID
23579     * @param {string} the group ID
23580     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23581     */
23582     get: function(groupId) {
23583         if (typeof(this.groups[groupId]) == 'undefined') {
23584             return false;
23585         }
23586         
23587         return this.groups[groupId] ;
23588     }
23589     
23590     
23591 });
23592 /*
23593  * - LGPL
23594  *
23595  * RadioItem
23596  * 
23597  */
23598
23599 /**
23600  * @class Roo.bootstrap.Radio
23601  * @extends Roo.bootstrap.Component
23602  * Bootstrap Radio class
23603  * @cfg {String} boxLabel - the label associated
23604  * @cfg {String} value - the value of radio
23605  * 
23606  * @constructor
23607  * Create a new Radio
23608  * @param {Object} config The config object
23609  */
23610 Roo.bootstrap.Radio = function(config){
23611     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23612     
23613 };
23614
23615 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23616     
23617     boxLabel : '',
23618     
23619     value : '',
23620     
23621     getAutoCreate : function()
23622     {
23623         var cfg = {
23624             tag : 'div',
23625             cls : 'form-group radio',
23626             cn : [
23627                 {
23628                     tag : 'label',
23629                     cls : 'box-label',
23630                     html : this.boxLabel
23631                 }
23632             ]
23633         };
23634         
23635         return cfg;
23636     },
23637     
23638     initEvents : function() 
23639     {
23640         this.parent().register(this);
23641         
23642         this.el.on('click', this.onClick, this);
23643         
23644     },
23645     
23646     onClick : function(e)
23647     {
23648         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23649             this.setChecked(true);
23650         }
23651     },
23652     
23653     setChecked : function(state, suppressEvent)
23654     {
23655         this.parent().setValue(this.value, suppressEvent);
23656         
23657     },
23658     
23659     setBoxLabel : function(v)
23660     {
23661         this.boxLabel = v;
23662         
23663         if(this.rendered){
23664             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23665         }
23666     }
23667     
23668 });
23669  
23670
23671  /*
23672  * - LGPL
23673  *
23674  * Input
23675  * 
23676  */
23677
23678 /**
23679  * @class Roo.bootstrap.SecurePass
23680  * @extends Roo.bootstrap.Input
23681  * Bootstrap SecurePass class
23682  *
23683  * 
23684  * @constructor
23685  * Create a new SecurePass
23686  * @param {Object} config The config object
23687  */
23688  
23689 Roo.bootstrap.SecurePass = function (config) {
23690     // these go here, so the translation tool can replace them..
23691     this.errors = {
23692         PwdEmpty: "Please type a password, and then retype it to confirm.",
23693         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23694         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23695         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23696         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23697         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23698         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23699         TooWeak: "Your password is Too Weak."
23700     },
23701     this.meterLabel = "Password strength:";
23702     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23703     this.meterClass = [
23704         "roo-password-meter-tooweak", 
23705         "roo-password-meter-weak", 
23706         "roo-password-meter-medium", 
23707         "roo-password-meter-strong", 
23708         "roo-password-meter-grey"
23709     ];
23710     
23711     this.errors = {};
23712     
23713     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23714 }
23715
23716 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23717     /**
23718      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23719      * {
23720      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23721      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23722      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23723      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23724      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23725      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23726      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23727      * })
23728      */
23729     // private
23730     
23731     meterWidth: 300,
23732     errorMsg :'',    
23733     errors: false,
23734     imageRoot: '/',
23735     /**
23736      * @cfg {String/Object} Label for the strength meter (defaults to
23737      * 'Password strength:')
23738      */
23739     // private
23740     meterLabel: '',
23741     /**
23742      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23743      * ['Weak', 'Medium', 'Strong'])
23744      */
23745     // private    
23746     pwdStrengths: false,    
23747     // private
23748     strength: 0,
23749     // private
23750     _lastPwd: null,
23751     // private
23752     kCapitalLetter: 0,
23753     kSmallLetter: 1,
23754     kDigit: 2,
23755     kPunctuation: 3,
23756     
23757     insecure: false,
23758     // private
23759     initEvents: function ()
23760     {
23761         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23762
23763         if (this.el.is('input[type=password]') && Roo.isSafari) {
23764             this.el.on('keydown', this.SafariOnKeyDown, this);
23765         }
23766
23767         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23768     },
23769     // private
23770     onRender: function (ct, position)
23771     {
23772         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23773         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23774         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23775
23776         this.trigger.createChild({
23777                    cn: [
23778                     {
23779                     //id: 'PwdMeter',
23780                     tag: 'div',
23781                     cls: 'roo-password-meter-grey col-xs-12',
23782                     style: {
23783                         //width: 0,
23784                         //width: this.meterWidth + 'px'                                                
23785                         }
23786                     },
23787                     {                            
23788                          cls: 'roo-password-meter-text'                          
23789                     }
23790                 ]            
23791         });
23792
23793          
23794         if (this.hideTrigger) {
23795             this.trigger.setDisplayed(false);
23796         }
23797         this.setSize(this.width || '', this.height || '');
23798     },
23799     // private
23800     onDestroy: function ()
23801     {
23802         if (this.trigger) {
23803             this.trigger.removeAllListeners();
23804             this.trigger.remove();
23805         }
23806         if (this.wrap) {
23807             this.wrap.remove();
23808         }
23809         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23810     },
23811     // private
23812     checkStrength: function ()
23813     {
23814         var pwd = this.inputEl().getValue();
23815         if (pwd == this._lastPwd) {
23816             return;
23817         }
23818
23819         var strength;
23820         if (this.ClientSideStrongPassword(pwd)) {
23821             strength = 3;
23822         } else if (this.ClientSideMediumPassword(pwd)) {
23823             strength = 2;
23824         } else if (this.ClientSideWeakPassword(pwd)) {
23825             strength = 1;
23826         } else {
23827             strength = 0;
23828         }
23829         
23830         Roo.log('strength1: ' + strength);
23831         
23832         //var pm = this.trigger.child('div/div/div').dom;
23833         var pm = this.trigger.child('div/div');
23834         pm.removeClass(this.meterClass);
23835         pm.addClass(this.meterClass[strength]);
23836                 
23837         
23838         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23839                 
23840         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23841         
23842         this._lastPwd = pwd;
23843     },
23844     reset: function ()
23845     {
23846         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23847         
23848         this._lastPwd = '';
23849         
23850         var pm = this.trigger.child('div/div');
23851         pm.removeClass(this.meterClass);
23852         pm.addClass('roo-password-meter-grey');        
23853         
23854         
23855         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23856         
23857         pt.innerHTML = '';
23858         this.inputEl().dom.type='password';
23859     },
23860     // private
23861     validateValue: function (value)
23862     {
23863         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23864             return false;
23865         }
23866         if (value.length == 0) {
23867             if (this.allowBlank) {
23868                 this.clearInvalid();
23869                 return true;
23870             }
23871
23872             this.markInvalid(this.errors.PwdEmpty);
23873             this.errorMsg = this.errors.PwdEmpty;
23874             return false;
23875         }
23876         
23877         if(this.insecure){
23878             return true;
23879         }
23880         
23881         if (!value.match(/[\x21-\x7e]+/)) {
23882             this.markInvalid(this.errors.PwdBadChar);
23883             this.errorMsg = this.errors.PwdBadChar;
23884             return false;
23885         }
23886         if (value.length < 6) {
23887             this.markInvalid(this.errors.PwdShort);
23888             this.errorMsg = this.errors.PwdShort;
23889             return false;
23890         }
23891         if (value.length > 16) {
23892             this.markInvalid(this.errors.PwdLong);
23893             this.errorMsg = this.errors.PwdLong;
23894             return false;
23895         }
23896         var strength;
23897         if (this.ClientSideStrongPassword(value)) {
23898             strength = 3;
23899         } else if (this.ClientSideMediumPassword(value)) {
23900             strength = 2;
23901         } else if (this.ClientSideWeakPassword(value)) {
23902             strength = 1;
23903         } else {
23904             strength = 0;
23905         }
23906
23907         
23908         if (strength < 2) {
23909             //this.markInvalid(this.errors.TooWeak);
23910             this.errorMsg = this.errors.TooWeak;
23911             //return false;
23912         }
23913         
23914         
23915         console.log('strength2: ' + strength);
23916         
23917         //var pm = this.trigger.child('div/div/div').dom;
23918         
23919         var pm = this.trigger.child('div/div');
23920         pm.removeClass(this.meterClass);
23921         pm.addClass(this.meterClass[strength]);
23922                 
23923         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23924                 
23925         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23926         
23927         this.errorMsg = ''; 
23928         return true;
23929     },
23930     // private
23931     CharacterSetChecks: function (type)
23932     {
23933         this.type = type;
23934         this.fResult = false;
23935     },
23936     // private
23937     isctype: function (character, type)
23938     {
23939         switch (type) {  
23940             case this.kCapitalLetter:
23941                 if (character >= 'A' && character <= 'Z') {
23942                     return true;
23943                 }
23944                 break;
23945             
23946             case this.kSmallLetter:
23947                 if (character >= 'a' && character <= 'z') {
23948                     return true;
23949                 }
23950                 break;
23951             
23952             case this.kDigit:
23953                 if (character >= '0' && character <= '9') {
23954                     return true;
23955                 }
23956                 break;
23957             
23958             case this.kPunctuation:
23959                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23960                     return true;
23961                 }
23962                 break;
23963             
23964             default:
23965                 return false;
23966         }
23967
23968     },
23969     // private
23970     IsLongEnough: function (pwd, size)
23971     {
23972         return !(pwd == null || isNaN(size) || pwd.length < size);
23973     },
23974     // private
23975     SpansEnoughCharacterSets: function (word, nb)
23976     {
23977         if (!this.IsLongEnough(word, nb))
23978         {
23979             return false;
23980         }
23981
23982         var characterSetChecks = new Array(
23983             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23984             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23985         );
23986         
23987         for (var index = 0; index < word.length; ++index) {
23988             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23989                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23990                     characterSetChecks[nCharSet].fResult = true;
23991                     break;
23992                 }
23993             }
23994         }
23995
23996         var nCharSets = 0;
23997         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23998             if (characterSetChecks[nCharSet].fResult) {
23999                 ++nCharSets;
24000             }
24001         }
24002
24003         if (nCharSets < nb) {
24004             return false;
24005         }
24006         return true;
24007     },
24008     // private
24009     ClientSideStrongPassword: function (pwd)
24010     {
24011         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24012     },
24013     // private
24014     ClientSideMediumPassword: function (pwd)
24015     {
24016         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24017     },
24018     // private
24019     ClientSideWeakPassword: function (pwd)
24020     {
24021         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24022     }
24023           
24024 })//<script type="text/javascript">
24025
24026 /*
24027  * Based  Ext JS Library 1.1.1
24028  * Copyright(c) 2006-2007, Ext JS, LLC.
24029  * LGPL
24030  *
24031  */
24032  
24033 /**
24034  * @class Roo.HtmlEditorCore
24035  * @extends Roo.Component
24036  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24037  *
24038  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24039  */
24040
24041 Roo.HtmlEditorCore = function(config){
24042     
24043     
24044     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24045     
24046     
24047     this.addEvents({
24048         /**
24049          * @event initialize
24050          * Fires when the editor is fully initialized (including the iframe)
24051          * @param {Roo.HtmlEditorCore} this
24052          */
24053         initialize: true,
24054         /**
24055          * @event activate
24056          * Fires when the editor is first receives the focus. Any insertion must wait
24057          * until after this event.
24058          * @param {Roo.HtmlEditorCore} this
24059          */
24060         activate: true,
24061          /**
24062          * @event beforesync
24063          * Fires before the textarea is updated with content from the editor iframe. Return false
24064          * to cancel the sync.
24065          * @param {Roo.HtmlEditorCore} this
24066          * @param {String} html
24067          */
24068         beforesync: true,
24069          /**
24070          * @event beforepush
24071          * Fires before the iframe editor is updated with content from the textarea. Return false
24072          * to cancel the push.
24073          * @param {Roo.HtmlEditorCore} this
24074          * @param {String} html
24075          */
24076         beforepush: true,
24077          /**
24078          * @event sync
24079          * Fires when the textarea is updated with content from the editor iframe.
24080          * @param {Roo.HtmlEditorCore} this
24081          * @param {String} html
24082          */
24083         sync: true,
24084          /**
24085          * @event push
24086          * Fires when the iframe editor is updated with content from the textarea.
24087          * @param {Roo.HtmlEditorCore} this
24088          * @param {String} html
24089          */
24090         push: true,
24091         
24092         /**
24093          * @event editorevent
24094          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24095          * @param {Roo.HtmlEditorCore} this
24096          */
24097         editorevent: true
24098         
24099     });
24100     
24101     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24102     
24103     // defaults : white / black...
24104     this.applyBlacklists();
24105     
24106     
24107     
24108 };
24109
24110
24111 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24112
24113
24114      /**
24115      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24116      */
24117     
24118     owner : false,
24119     
24120      /**
24121      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24122      *                        Roo.resizable.
24123      */
24124     resizable : false,
24125      /**
24126      * @cfg {Number} height (in pixels)
24127      */   
24128     height: 300,
24129    /**
24130      * @cfg {Number} width (in pixels)
24131      */   
24132     width: 500,
24133     
24134     /**
24135      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24136      * 
24137      */
24138     stylesheets: false,
24139     
24140     // id of frame..
24141     frameId: false,
24142     
24143     // private properties
24144     validationEvent : false,
24145     deferHeight: true,
24146     initialized : false,
24147     activated : false,
24148     sourceEditMode : false,
24149     onFocus : Roo.emptyFn,
24150     iframePad:3,
24151     hideMode:'offsets',
24152     
24153     clearUp: true,
24154     
24155     // blacklist + whitelisted elements..
24156     black: false,
24157     white: false,
24158      
24159     bodyCls : '',
24160
24161     /**
24162      * Protected method that will not generally be called directly. It
24163      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24164      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24165      */
24166     getDocMarkup : function(){
24167         // body styles..
24168         var st = '';
24169         
24170         // inherit styels from page...?? 
24171         if (this.stylesheets === false) {
24172             
24173             Roo.get(document.head).select('style').each(function(node) {
24174                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24175             });
24176             
24177             Roo.get(document.head).select('link').each(function(node) { 
24178                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24179             });
24180             
24181         } else if (!this.stylesheets.length) {
24182                 // simple..
24183                 st = '<style type="text/css">' +
24184                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24185                    '</style>';
24186         } else {
24187             for (var i in this.stylesheets) { 
24188                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24189             }
24190             
24191         }
24192         
24193         st +=  '<style type="text/css">' +
24194             'IMG { cursor: pointer } ' +
24195         '</style>';
24196
24197         var cls = 'roo-htmleditor-body';
24198         
24199         if(this.bodyCls.length){
24200             cls += ' ' + this.bodyCls;
24201         }
24202         
24203         return '<html><head>' + st  +
24204             //<style type="text/css">' +
24205             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24206             //'</style>' +
24207             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24208     },
24209
24210     // private
24211     onRender : function(ct, position)
24212     {
24213         var _t = this;
24214         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24215         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24216         
24217         
24218         this.el.dom.style.border = '0 none';
24219         this.el.dom.setAttribute('tabIndex', -1);
24220         this.el.addClass('x-hidden hide');
24221         
24222         
24223         
24224         if(Roo.isIE){ // fix IE 1px bogus margin
24225             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24226         }
24227        
24228         
24229         this.frameId = Roo.id();
24230         
24231          
24232         
24233         var iframe = this.owner.wrap.createChild({
24234             tag: 'iframe',
24235             cls: 'form-control', // bootstrap..
24236             id: this.frameId,
24237             name: this.frameId,
24238             frameBorder : 'no',
24239             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24240         }, this.el
24241         );
24242         
24243         
24244         this.iframe = iframe.dom;
24245
24246          this.assignDocWin();
24247         
24248         this.doc.designMode = 'on';
24249        
24250         this.doc.open();
24251         this.doc.write(this.getDocMarkup());
24252         this.doc.close();
24253
24254         
24255         var task = { // must defer to wait for browser to be ready
24256             run : function(){
24257                 //console.log("run task?" + this.doc.readyState);
24258                 this.assignDocWin();
24259                 if(this.doc.body || this.doc.readyState == 'complete'){
24260                     try {
24261                         this.doc.designMode="on";
24262                     } catch (e) {
24263                         return;
24264                     }
24265                     Roo.TaskMgr.stop(task);
24266                     this.initEditor.defer(10, this);
24267                 }
24268             },
24269             interval : 10,
24270             duration: 10000,
24271             scope: this
24272         };
24273         Roo.TaskMgr.start(task);
24274
24275     },
24276
24277     // private
24278     onResize : function(w, h)
24279     {
24280          Roo.log('resize: ' +w + ',' + h );
24281         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24282         if(!this.iframe){
24283             return;
24284         }
24285         if(typeof w == 'number'){
24286             
24287             this.iframe.style.width = w + 'px';
24288         }
24289         if(typeof h == 'number'){
24290             
24291             this.iframe.style.height = h + 'px';
24292             if(this.doc){
24293                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24294             }
24295         }
24296         
24297     },
24298
24299     /**
24300      * Toggles the editor between standard and source edit mode.
24301      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24302      */
24303     toggleSourceEdit : function(sourceEditMode){
24304         
24305         this.sourceEditMode = sourceEditMode === true;
24306         
24307         if(this.sourceEditMode){
24308  
24309             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24310             
24311         }else{
24312             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24313             //this.iframe.className = '';
24314             this.deferFocus();
24315         }
24316         //this.setSize(this.owner.wrap.getSize());
24317         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24318     },
24319
24320     
24321   
24322
24323     /**
24324      * Protected method that will not generally be called directly. If you need/want
24325      * custom HTML cleanup, this is the method you should override.
24326      * @param {String} html The HTML to be cleaned
24327      * return {String} The cleaned HTML
24328      */
24329     cleanHtml : function(html){
24330         html = String(html);
24331         if(html.length > 5){
24332             if(Roo.isSafari){ // strip safari nonsense
24333                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24334             }
24335         }
24336         if(html == '&nbsp;'){
24337             html = '';
24338         }
24339         return html;
24340     },
24341
24342     /**
24343      * HTML Editor -> Textarea
24344      * Protected method that will not generally be called directly. Syncs the contents
24345      * of the editor iframe with the textarea.
24346      */
24347     syncValue : function(){
24348         if(this.initialized){
24349             var bd = (this.doc.body || this.doc.documentElement);
24350             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24351             var html = bd.innerHTML;
24352             if(Roo.isSafari){
24353                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24354                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24355                 if(m && m[1]){
24356                     html = '<div style="'+m[0]+'">' + html + '</div>';
24357                 }
24358             }
24359             html = this.cleanHtml(html);
24360             // fix up the special chars.. normaly like back quotes in word...
24361             // however we do not want to do this with chinese..
24362             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24363                 
24364                 var cc = match.charCodeAt();
24365
24366                 // Get the character value, handling surrogate pairs
24367                 if (match.length == 2) {
24368                     // It's a surrogate pair, calculate the Unicode code point
24369                     var high = match.charCodeAt(0) - 0xD800;
24370                     var low  = match.charCodeAt(1) - 0xDC00;
24371                     cc = (high * 0x400) + low + 0x10000;
24372                 }  else if (
24373                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24374                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24375                     (cc >= 0xf900 && cc < 0xfb00 )
24376                 ) {
24377                         return match;
24378                 }  
24379          
24380                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24381                 return "&#" + cc + ";";
24382                 
24383                 
24384             });
24385             
24386             
24387              
24388             if(this.owner.fireEvent('beforesync', this, html) !== false){
24389                 this.el.dom.value = html;
24390                 this.owner.fireEvent('sync', this, html);
24391             }
24392         }
24393     },
24394
24395     /**
24396      * Protected method that will not generally be called directly. Pushes the value of the textarea
24397      * into the iframe editor.
24398      */
24399     pushValue : function(){
24400         if(this.initialized){
24401             var v = this.el.dom.value.trim();
24402             
24403 //            if(v.length < 1){
24404 //                v = '&#160;';
24405 //            }
24406             
24407             if(this.owner.fireEvent('beforepush', this, v) !== false){
24408                 var d = (this.doc.body || this.doc.documentElement);
24409                 d.innerHTML = v;
24410                 this.cleanUpPaste();
24411                 this.el.dom.value = d.innerHTML;
24412                 this.owner.fireEvent('push', this, v);
24413             }
24414         }
24415     },
24416
24417     // private
24418     deferFocus : function(){
24419         this.focus.defer(10, this);
24420     },
24421
24422     // doc'ed in Field
24423     focus : function(){
24424         if(this.win && !this.sourceEditMode){
24425             this.win.focus();
24426         }else{
24427             this.el.focus();
24428         }
24429     },
24430     
24431     assignDocWin: function()
24432     {
24433         var iframe = this.iframe;
24434         
24435          if(Roo.isIE){
24436             this.doc = iframe.contentWindow.document;
24437             this.win = iframe.contentWindow;
24438         } else {
24439 //            if (!Roo.get(this.frameId)) {
24440 //                return;
24441 //            }
24442 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24443 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24444             
24445             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24446                 return;
24447             }
24448             
24449             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24450             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24451         }
24452     },
24453     
24454     // private
24455     initEditor : function(){
24456         //console.log("INIT EDITOR");
24457         this.assignDocWin();
24458         
24459         
24460         
24461         this.doc.designMode="on";
24462         this.doc.open();
24463         this.doc.write(this.getDocMarkup());
24464         this.doc.close();
24465         
24466         var dbody = (this.doc.body || this.doc.documentElement);
24467         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24468         // this copies styles from the containing element into thsi one..
24469         // not sure why we need all of this..
24470         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24471         
24472         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24473         //ss['background-attachment'] = 'fixed'; // w3c
24474         dbody.bgProperties = 'fixed'; // ie
24475         //Roo.DomHelper.applyStyles(dbody, ss);
24476         Roo.EventManager.on(this.doc, {
24477             //'mousedown': this.onEditorEvent,
24478             'mouseup': this.onEditorEvent,
24479             'dblclick': this.onEditorEvent,
24480             'click': this.onEditorEvent,
24481             'keyup': this.onEditorEvent,
24482             buffer:100,
24483             scope: this
24484         });
24485         if(Roo.isGecko){
24486             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24487         }
24488         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24489             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24490         }
24491         this.initialized = true;
24492
24493         this.owner.fireEvent('initialize', this);
24494         this.pushValue();
24495     },
24496
24497     // private
24498     onDestroy : function(){
24499         
24500         
24501         
24502         if(this.rendered){
24503             
24504             //for (var i =0; i < this.toolbars.length;i++) {
24505             //    // fixme - ask toolbars for heights?
24506             //    this.toolbars[i].onDestroy();
24507            // }
24508             
24509             //this.wrap.dom.innerHTML = '';
24510             //this.wrap.remove();
24511         }
24512     },
24513
24514     // private
24515     onFirstFocus : function(){
24516         
24517         this.assignDocWin();
24518         
24519         
24520         this.activated = true;
24521          
24522     
24523         if(Roo.isGecko){ // prevent silly gecko errors
24524             this.win.focus();
24525             var s = this.win.getSelection();
24526             if(!s.focusNode || s.focusNode.nodeType != 3){
24527                 var r = s.getRangeAt(0);
24528                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24529                 r.collapse(true);
24530                 this.deferFocus();
24531             }
24532             try{
24533                 this.execCmd('useCSS', true);
24534                 this.execCmd('styleWithCSS', false);
24535             }catch(e){}
24536         }
24537         this.owner.fireEvent('activate', this);
24538     },
24539
24540     // private
24541     adjustFont: function(btn){
24542         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24543         //if(Roo.isSafari){ // safari
24544         //    adjust *= 2;
24545        // }
24546         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24547         if(Roo.isSafari){ // safari
24548             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24549             v =  (v < 10) ? 10 : v;
24550             v =  (v > 48) ? 48 : v;
24551             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24552             
24553         }
24554         
24555         
24556         v = Math.max(1, v+adjust);
24557         
24558         this.execCmd('FontSize', v  );
24559     },
24560
24561     onEditorEvent : function(e)
24562     {
24563         this.owner.fireEvent('editorevent', this, e);
24564       //  this.updateToolbar();
24565         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24566     },
24567
24568     insertTag : function(tg)
24569     {
24570         // could be a bit smarter... -> wrap the current selected tRoo..
24571         if (tg.toLowerCase() == 'span' ||
24572             tg.toLowerCase() == 'code' ||
24573             tg.toLowerCase() == 'sup' ||
24574             tg.toLowerCase() == 'sub' 
24575             ) {
24576             
24577             range = this.createRange(this.getSelection());
24578             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24579             wrappingNode.appendChild(range.extractContents());
24580             range.insertNode(wrappingNode);
24581
24582             return;
24583             
24584             
24585             
24586         }
24587         this.execCmd("formatblock",   tg);
24588         
24589     },
24590     
24591     insertText : function(txt)
24592     {
24593         
24594         
24595         var range = this.createRange();
24596         range.deleteContents();
24597                //alert(Sender.getAttribute('label'));
24598                
24599         range.insertNode(this.doc.createTextNode(txt));
24600     } ,
24601     
24602      
24603
24604     /**
24605      * Executes a Midas editor command on the editor document and performs necessary focus and
24606      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24607      * @param {String} cmd The Midas command
24608      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24609      */
24610     relayCmd : function(cmd, value){
24611         this.win.focus();
24612         this.execCmd(cmd, value);
24613         this.owner.fireEvent('editorevent', this);
24614         //this.updateToolbar();
24615         this.owner.deferFocus();
24616     },
24617
24618     /**
24619      * Executes a Midas editor command directly on the editor document.
24620      * For visual commands, you should use {@link #relayCmd} instead.
24621      * <b>This should only be called after the editor is initialized.</b>
24622      * @param {String} cmd The Midas command
24623      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24624      */
24625     execCmd : function(cmd, value){
24626         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24627         this.syncValue();
24628     },
24629  
24630  
24631    
24632     /**
24633      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24634      * to insert tRoo.
24635      * @param {String} text | dom node.. 
24636      */
24637     insertAtCursor : function(text)
24638     {
24639         
24640         if(!this.activated){
24641             return;
24642         }
24643         /*
24644         if(Roo.isIE){
24645             this.win.focus();
24646             var r = this.doc.selection.createRange();
24647             if(r){
24648                 r.collapse(true);
24649                 r.pasteHTML(text);
24650                 this.syncValue();
24651                 this.deferFocus();
24652             
24653             }
24654             return;
24655         }
24656         */
24657         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24658             this.win.focus();
24659             
24660             
24661             // from jquery ui (MIT licenced)
24662             var range, node;
24663             var win = this.win;
24664             
24665             if (win.getSelection && win.getSelection().getRangeAt) {
24666                 range = win.getSelection().getRangeAt(0);
24667                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24668                 range.insertNode(node);
24669             } else if (win.document.selection && win.document.selection.createRange) {
24670                 // no firefox support
24671                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24672                 win.document.selection.createRange().pasteHTML(txt);
24673             } else {
24674                 // no firefox support
24675                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24676                 this.execCmd('InsertHTML', txt);
24677             } 
24678             
24679             this.syncValue();
24680             
24681             this.deferFocus();
24682         }
24683     },
24684  // private
24685     mozKeyPress : function(e){
24686         if(e.ctrlKey){
24687             var c = e.getCharCode(), cmd;
24688           
24689             if(c > 0){
24690                 c = String.fromCharCode(c).toLowerCase();
24691                 switch(c){
24692                     case 'b':
24693                         cmd = 'bold';
24694                         break;
24695                     case 'i':
24696                         cmd = 'italic';
24697                         break;
24698                     
24699                     case 'u':
24700                         cmd = 'underline';
24701                         break;
24702                     
24703                     case 'v':
24704                         this.cleanUpPaste.defer(100, this);
24705                         return;
24706                         
24707                 }
24708                 if(cmd){
24709                     this.win.focus();
24710                     this.execCmd(cmd);
24711                     this.deferFocus();
24712                     e.preventDefault();
24713                 }
24714                 
24715             }
24716         }
24717     },
24718
24719     // private
24720     fixKeys : function(){ // load time branching for fastest keydown performance
24721         if(Roo.isIE){
24722             return function(e){
24723                 var k = e.getKey(), r;
24724                 if(k == e.TAB){
24725                     e.stopEvent();
24726                     r = this.doc.selection.createRange();
24727                     if(r){
24728                         r.collapse(true);
24729                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24730                         this.deferFocus();
24731                     }
24732                     return;
24733                 }
24734                 
24735                 if(k == e.ENTER){
24736                     r = this.doc.selection.createRange();
24737                     if(r){
24738                         var target = r.parentElement();
24739                         if(!target || target.tagName.toLowerCase() != 'li'){
24740                             e.stopEvent();
24741                             r.pasteHTML('<br />');
24742                             r.collapse(false);
24743                             r.select();
24744                         }
24745                     }
24746                 }
24747                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24748                     this.cleanUpPaste.defer(100, this);
24749                     return;
24750                 }
24751                 
24752                 
24753             };
24754         }else if(Roo.isOpera){
24755             return function(e){
24756                 var k = e.getKey();
24757                 if(k == e.TAB){
24758                     e.stopEvent();
24759                     this.win.focus();
24760                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24761                     this.deferFocus();
24762                 }
24763                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24764                     this.cleanUpPaste.defer(100, this);
24765                     return;
24766                 }
24767                 
24768             };
24769         }else if(Roo.isSafari){
24770             return function(e){
24771                 var k = e.getKey();
24772                 
24773                 if(k == e.TAB){
24774                     e.stopEvent();
24775                     this.execCmd('InsertText','\t');
24776                     this.deferFocus();
24777                     return;
24778                 }
24779                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24780                     this.cleanUpPaste.defer(100, this);
24781                     return;
24782                 }
24783                 
24784              };
24785         }
24786     }(),
24787     
24788     getAllAncestors: function()
24789     {
24790         var p = this.getSelectedNode();
24791         var a = [];
24792         if (!p) {
24793             a.push(p); // push blank onto stack..
24794             p = this.getParentElement();
24795         }
24796         
24797         
24798         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24799             a.push(p);
24800             p = p.parentNode;
24801         }
24802         a.push(this.doc.body);
24803         return a;
24804     },
24805     lastSel : false,
24806     lastSelNode : false,
24807     
24808     
24809     getSelection : function() 
24810     {
24811         this.assignDocWin();
24812         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24813     },
24814     
24815     getSelectedNode: function() 
24816     {
24817         // this may only work on Gecko!!!
24818         
24819         // should we cache this!!!!
24820         
24821         
24822         
24823          
24824         var range = this.createRange(this.getSelection()).cloneRange();
24825         
24826         if (Roo.isIE) {
24827             var parent = range.parentElement();
24828             while (true) {
24829                 var testRange = range.duplicate();
24830                 testRange.moveToElementText(parent);
24831                 if (testRange.inRange(range)) {
24832                     break;
24833                 }
24834                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24835                     break;
24836                 }
24837                 parent = parent.parentElement;
24838             }
24839             return parent;
24840         }
24841         
24842         // is ancestor a text element.
24843         var ac =  range.commonAncestorContainer;
24844         if (ac.nodeType == 3) {
24845             ac = ac.parentNode;
24846         }
24847         
24848         var ar = ac.childNodes;
24849          
24850         var nodes = [];
24851         var other_nodes = [];
24852         var has_other_nodes = false;
24853         for (var i=0;i<ar.length;i++) {
24854             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24855                 continue;
24856             }
24857             // fullly contained node.
24858             
24859             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24860                 nodes.push(ar[i]);
24861                 continue;
24862             }
24863             
24864             // probably selected..
24865             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24866                 other_nodes.push(ar[i]);
24867                 continue;
24868             }
24869             // outer..
24870             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24871                 continue;
24872             }
24873             
24874             
24875             has_other_nodes = true;
24876         }
24877         if (!nodes.length && other_nodes.length) {
24878             nodes= other_nodes;
24879         }
24880         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24881             return false;
24882         }
24883         
24884         return nodes[0];
24885     },
24886     createRange: function(sel)
24887     {
24888         // this has strange effects when using with 
24889         // top toolbar - not sure if it's a great idea.
24890         //this.editor.contentWindow.focus();
24891         if (typeof sel != "undefined") {
24892             try {
24893                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24894             } catch(e) {
24895                 return this.doc.createRange();
24896             }
24897         } else {
24898             return this.doc.createRange();
24899         }
24900     },
24901     getParentElement: function()
24902     {
24903         
24904         this.assignDocWin();
24905         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24906         
24907         var range = this.createRange(sel);
24908          
24909         try {
24910             var p = range.commonAncestorContainer;
24911             while (p.nodeType == 3) { // text node
24912                 p = p.parentNode;
24913             }
24914             return p;
24915         } catch (e) {
24916             return null;
24917         }
24918     
24919     },
24920     /***
24921      *
24922      * Range intersection.. the hard stuff...
24923      *  '-1' = before
24924      *  '0' = hits..
24925      *  '1' = after.
24926      *         [ -- selected range --- ]
24927      *   [fail]                        [fail]
24928      *
24929      *    basically..
24930      *      if end is before start or  hits it. fail.
24931      *      if start is after end or hits it fail.
24932      *
24933      *   if either hits (but other is outside. - then it's not 
24934      *   
24935      *    
24936      **/
24937     
24938     
24939     // @see http://www.thismuchiknow.co.uk/?p=64.
24940     rangeIntersectsNode : function(range, node)
24941     {
24942         var nodeRange = node.ownerDocument.createRange();
24943         try {
24944             nodeRange.selectNode(node);
24945         } catch (e) {
24946             nodeRange.selectNodeContents(node);
24947         }
24948     
24949         var rangeStartRange = range.cloneRange();
24950         rangeStartRange.collapse(true);
24951     
24952         var rangeEndRange = range.cloneRange();
24953         rangeEndRange.collapse(false);
24954     
24955         var nodeStartRange = nodeRange.cloneRange();
24956         nodeStartRange.collapse(true);
24957     
24958         var nodeEndRange = nodeRange.cloneRange();
24959         nodeEndRange.collapse(false);
24960     
24961         return rangeStartRange.compareBoundaryPoints(
24962                  Range.START_TO_START, nodeEndRange) == -1 &&
24963                rangeEndRange.compareBoundaryPoints(
24964                  Range.START_TO_START, nodeStartRange) == 1;
24965         
24966          
24967     },
24968     rangeCompareNode : function(range, node)
24969     {
24970         var nodeRange = node.ownerDocument.createRange();
24971         try {
24972             nodeRange.selectNode(node);
24973         } catch (e) {
24974             nodeRange.selectNodeContents(node);
24975         }
24976         
24977         
24978         range.collapse(true);
24979     
24980         nodeRange.collapse(true);
24981      
24982         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24983         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24984          
24985         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24986         
24987         var nodeIsBefore   =  ss == 1;
24988         var nodeIsAfter    = ee == -1;
24989         
24990         if (nodeIsBefore && nodeIsAfter) {
24991             return 0; // outer
24992         }
24993         if (!nodeIsBefore && nodeIsAfter) {
24994             return 1; //right trailed.
24995         }
24996         
24997         if (nodeIsBefore && !nodeIsAfter) {
24998             return 2;  // left trailed.
24999         }
25000         // fully contined.
25001         return 3;
25002     },
25003
25004     // private? - in a new class?
25005     cleanUpPaste :  function()
25006     {
25007         // cleans up the whole document..
25008         Roo.log('cleanuppaste');
25009         
25010         this.cleanUpChildren(this.doc.body);
25011         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25012         if (clean != this.doc.body.innerHTML) {
25013             this.doc.body.innerHTML = clean;
25014         }
25015         
25016     },
25017     
25018     cleanWordChars : function(input) {// change the chars to hex code
25019         var he = Roo.HtmlEditorCore;
25020         
25021         var output = input;
25022         Roo.each(he.swapCodes, function(sw) { 
25023             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25024             
25025             output = output.replace(swapper, sw[1]);
25026         });
25027         
25028         return output;
25029     },
25030     
25031     
25032     cleanUpChildren : function (n)
25033     {
25034         if (!n.childNodes.length) {
25035             return;
25036         }
25037         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25038            this.cleanUpChild(n.childNodes[i]);
25039         }
25040     },
25041     
25042     
25043         
25044     
25045     cleanUpChild : function (node)
25046     {
25047         var ed = this;
25048         //console.log(node);
25049         if (node.nodeName == "#text") {
25050             // clean up silly Windows -- stuff?
25051             return; 
25052         }
25053         if (node.nodeName == "#comment") {
25054             node.parentNode.removeChild(node);
25055             // clean up silly Windows -- stuff?
25056             return; 
25057         }
25058         var lcname = node.tagName.toLowerCase();
25059         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25060         // whitelist of tags..
25061         
25062         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25063             // remove node.
25064             node.parentNode.removeChild(node);
25065             return;
25066             
25067         }
25068         
25069         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25070         
25071         // spans with no attributes - just remove them..
25072         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25073             remove_keep_children = true;
25074         }
25075         
25076         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25077         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25078         
25079         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25080         //    remove_keep_children = true;
25081         //}
25082         
25083         if (remove_keep_children) {
25084             this.cleanUpChildren(node);
25085             // inserts everything just before this node...
25086             while (node.childNodes.length) {
25087                 var cn = node.childNodes[0];
25088                 node.removeChild(cn);
25089                 node.parentNode.insertBefore(cn, node);
25090             }
25091             node.parentNode.removeChild(node);
25092             return;
25093         }
25094         
25095         if (!node.attributes || !node.attributes.length) {
25096             
25097           
25098             
25099             
25100             this.cleanUpChildren(node);
25101             return;
25102         }
25103         
25104         function cleanAttr(n,v)
25105         {
25106             
25107             if (v.match(/^\./) || v.match(/^\//)) {
25108                 return;
25109             }
25110             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25111                 return;
25112             }
25113             if (v.match(/^#/)) {
25114                 return;
25115             }
25116             if (v.match(/^\{/)) { // allow template editing.
25117                 return;
25118             }
25119 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25120             node.removeAttribute(n);
25121             
25122         }
25123         
25124         var cwhite = this.cwhite;
25125         var cblack = this.cblack;
25126             
25127         function cleanStyle(n,v)
25128         {
25129             if (v.match(/expression/)) { //XSS?? should we even bother..
25130                 node.removeAttribute(n);
25131                 return;
25132             }
25133             
25134             var parts = v.split(/;/);
25135             var clean = [];
25136             
25137             Roo.each(parts, function(p) {
25138                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25139                 if (!p.length) {
25140                     return true;
25141                 }
25142                 var l = p.split(':').shift().replace(/\s+/g,'');
25143                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25144                 
25145                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25146 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25147                     //node.removeAttribute(n);
25148                     return true;
25149                 }
25150                 //Roo.log()
25151                 // only allow 'c whitelisted system attributes'
25152                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25153 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25154                     //node.removeAttribute(n);
25155                     return true;
25156                 }
25157                 
25158                 
25159                  
25160                 
25161                 clean.push(p);
25162                 return true;
25163             });
25164             if (clean.length) { 
25165                 node.setAttribute(n, clean.join(';'));
25166             } else {
25167                 node.removeAttribute(n);
25168             }
25169             
25170         }
25171         
25172         
25173         for (var i = node.attributes.length-1; i > -1 ; i--) {
25174             var a = node.attributes[i];
25175             //console.log(a);
25176             
25177             if (a.name.toLowerCase().substr(0,2)=='on')  {
25178                 node.removeAttribute(a.name);
25179                 continue;
25180             }
25181             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25182                 node.removeAttribute(a.name);
25183                 continue;
25184             }
25185             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25186                 cleanAttr(a.name,a.value); // fixme..
25187                 continue;
25188             }
25189             if (a.name == 'style') {
25190                 cleanStyle(a.name,a.value);
25191                 continue;
25192             }
25193             /// clean up MS crap..
25194             // tecnically this should be a list of valid class'es..
25195             
25196             
25197             if (a.name == 'class') {
25198                 if (a.value.match(/^Mso/)) {
25199                     node.removeAttribute('class');
25200                 }
25201                 
25202                 if (a.value.match(/^body$/)) {
25203                     node.removeAttribute('class');
25204                 }
25205                 continue;
25206             }
25207             
25208             // style cleanup!?
25209             // class cleanup?
25210             
25211         }
25212         
25213         
25214         this.cleanUpChildren(node);
25215         
25216         
25217     },
25218     
25219     /**
25220      * Clean up MS wordisms...
25221      */
25222     cleanWord : function(node)
25223     {
25224         if (!node) {
25225             this.cleanWord(this.doc.body);
25226             return;
25227         }
25228         
25229         if(
25230                 node.nodeName == 'SPAN' &&
25231                 !node.hasAttributes() &&
25232                 node.childNodes.length == 1 &&
25233                 node.firstChild.nodeName == "#text"  
25234         ) {
25235             var textNode = node.firstChild;
25236             node.removeChild(textNode);
25237             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25238                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25239             }
25240             node.parentNode.insertBefore(textNode, node);
25241             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25242                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25243             }
25244             node.parentNode.removeChild(node);
25245         }
25246         
25247         if (node.nodeName == "#text") {
25248             // clean up silly Windows -- stuff?
25249             return; 
25250         }
25251         if (node.nodeName == "#comment") {
25252             node.parentNode.removeChild(node);
25253             // clean up silly Windows -- stuff?
25254             return; 
25255         }
25256         
25257         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25258             node.parentNode.removeChild(node);
25259             return;
25260         }
25261         //Roo.log(node.tagName);
25262         // remove - but keep children..
25263         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25264             //Roo.log('-- removed');
25265             while (node.childNodes.length) {
25266                 var cn = node.childNodes[0];
25267                 node.removeChild(cn);
25268                 node.parentNode.insertBefore(cn, node);
25269                 // move node to parent - and clean it..
25270                 this.cleanWord(cn);
25271             }
25272             node.parentNode.removeChild(node);
25273             /// no need to iterate chidlren = it's got none..
25274             //this.iterateChildren(node, this.cleanWord);
25275             return;
25276         }
25277         // clean styles
25278         if (node.className.length) {
25279             
25280             var cn = node.className.split(/\W+/);
25281             var cna = [];
25282             Roo.each(cn, function(cls) {
25283                 if (cls.match(/Mso[a-zA-Z]+/)) {
25284                     return;
25285                 }
25286                 cna.push(cls);
25287             });
25288             node.className = cna.length ? cna.join(' ') : '';
25289             if (!cna.length) {
25290                 node.removeAttribute("class");
25291             }
25292         }
25293         
25294         if (node.hasAttribute("lang")) {
25295             node.removeAttribute("lang");
25296         }
25297         
25298         if (node.hasAttribute("style")) {
25299             
25300             var styles = node.getAttribute("style").split(";");
25301             var nstyle = [];
25302             Roo.each(styles, function(s) {
25303                 if (!s.match(/:/)) {
25304                     return;
25305                 }
25306                 var kv = s.split(":");
25307                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25308                     return;
25309                 }
25310                 // what ever is left... we allow.
25311                 nstyle.push(s);
25312             });
25313             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25314             if (!nstyle.length) {
25315                 node.removeAttribute('style');
25316             }
25317         }
25318         this.iterateChildren(node, this.cleanWord);
25319         
25320         
25321         
25322     },
25323     /**
25324      * iterateChildren of a Node, calling fn each time, using this as the scole..
25325      * @param {DomNode} node node to iterate children of.
25326      * @param {Function} fn method of this class to call on each item.
25327      */
25328     iterateChildren : function(node, fn)
25329     {
25330         if (!node.childNodes.length) {
25331                 return;
25332         }
25333         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25334            fn.call(this, node.childNodes[i])
25335         }
25336     },
25337     
25338     
25339     /**
25340      * cleanTableWidths.
25341      *
25342      * Quite often pasting from word etc.. results in tables with column and widths.
25343      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25344      *
25345      */
25346     cleanTableWidths : function(node)
25347     {
25348          
25349          
25350         if (!node) {
25351             this.cleanTableWidths(this.doc.body);
25352             return;
25353         }
25354         
25355         // ignore list...
25356         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25357             return; 
25358         }
25359         Roo.log(node.tagName);
25360         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25361             this.iterateChildren(node, this.cleanTableWidths);
25362             return;
25363         }
25364         if (node.hasAttribute('width')) {
25365             node.removeAttribute('width');
25366         }
25367         
25368          
25369         if (node.hasAttribute("style")) {
25370             // pretty basic...
25371             
25372             var styles = node.getAttribute("style").split(";");
25373             var nstyle = [];
25374             Roo.each(styles, function(s) {
25375                 if (!s.match(/:/)) {
25376                     return;
25377                 }
25378                 var kv = s.split(":");
25379                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25380                     return;
25381                 }
25382                 // what ever is left... we allow.
25383                 nstyle.push(s);
25384             });
25385             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25386             if (!nstyle.length) {
25387                 node.removeAttribute('style');
25388             }
25389         }
25390         
25391         this.iterateChildren(node, this.cleanTableWidths);
25392         
25393         
25394     },
25395     
25396     
25397     
25398     
25399     domToHTML : function(currentElement, depth, nopadtext) {
25400         
25401         depth = depth || 0;
25402         nopadtext = nopadtext || false;
25403     
25404         if (!currentElement) {
25405             return this.domToHTML(this.doc.body);
25406         }
25407         
25408         //Roo.log(currentElement);
25409         var j;
25410         var allText = false;
25411         var nodeName = currentElement.nodeName;
25412         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25413         
25414         if  (nodeName == '#text') {
25415             
25416             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25417         }
25418         
25419         
25420         var ret = '';
25421         if (nodeName != 'BODY') {
25422              
25423             var i = 0;
25424             // Prints the node tagName, such as <A>, <IMG>, etc
25425             if (tagName) {
25426                 var attr = [];
25427                 for(i = 0; i < currentElement.attributes.length;i++) {
25428                     // quoting?
25429                     var aname = currentElement.attributes.item(i).name;
25430                     if (!currentElement.attributes.item(i).value.length) {
25431                         continue;
25432                     }
25433                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25434                 }
25435                 
25436                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25437             } 
25438             else {
25439                 
25440                 // eack
25441             }
25442         } else {
25443             tagName = false;
25444         }
25445         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25446             return ret;
25447         }
25448         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25449             nopadtext = true;
25450         }
25451         
25452         
25453         // Traverse the tree
25454         i = 0;
25455         var currentElementChild = currentElement.childNodes.item(i);
25456         var allText = true;
25457         var innerHTML  = '';
25458         lastnode = '';
25459         while (currentElementChild) {
25460             // Formatting code (indent the tree so it looks nice on the screen)
25461             var nopad = nopadtext;
25462             if (lastnode == 'SPAN') {
25463                 nopad  = true;
25464             }
25465             // text
25466             if  (currentElementChild.nodeName == '#text') {
25467                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25468                 toadd = nopadtext ? toadd : toadd.trim();
25469                 if (!nopad && toadd.length > 80) {
25470                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25471                 }
25472                 innerHTML  += toadd;
25473                 
25474                 i++;
25475                 currentElementChild = currentElement.childNodes.item(i);
25476                 lastNode = '';
25477                 continue;
25478             }
25479             allText = false;
25480             
25481             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25482                 
25483             // Recursively traverse the tree structure of the child node
25484             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25485             lastnode = currentElementChild.nodeName;
25486             i++;
25487             currentElementChild=currentElement.childNodes.item(i);
25488         }
25489         
25490         ret += innerHTML;
25491         
25492         if (!allText) {
25493                 // The remaining code is mostly for formatting the tree
25494             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25495         }
25496         
25497         
25498         if (tagName) {
25499             ret+= "</"+tagName+">";
25500         }
25501         return ret;
25502         
25503     },
25504         
25505     applyBlacklists : function()
25506     {
25507         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25508         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25509         
25510         this.white = [];
25511         this.black = [];
25512         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25513             if (b.indexOf(tag) > -1) {
25514                 return;
25515             }
25516             this.white.push(tag);
25517             
25518         }, this);
25519         
25520         Roo.each(w, function(tag) {
25521             if (b.indexOf(tag) > -1) {
25522                 return;
25523             }
25524             if (this.white.indexOf(tag) > -1) {
25525                 return;
25526             }
25527             this.white.push(tag);
25528             
25529         }, this);
25530         
25531         
25532         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25533             if (w.indexOf(tag) > -1) {
25534                 return;
25535             }
25536             this.black.push(tag);
25537             
25538         }, this);
25539         
25540         Roo.each(b, function(tag) {
25541             if (w.indexOf(tag) > -1) {
25542                 return;
25543             }
25544             if (this.black.indexOf(tag) > -1) {
25545                 return;
25546             }
25547             this.black.push(tag);
25548             
25549         }, this);
25550         
25551         
25552         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25553         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25554         
25555         this.cwhite = [];
25556         this.cblack = [];
25557         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25558             if (b.indexOf(tag) > -1) {
25559                 return;
25560             }
25561             this.cwhite.push(tag);
25562             
25563         }, this);
25564         
25565         Roo.each(w, function(tag) {
25566             if (b.indexOf(tag) > -1) {
25567                 return;
25568             }
25569             if (this.cwhite.indexOf(tag) > -1) {
25570                 return;
25571             }
25572             this.cwhite.push(tag);
25573             
25574         }, this);
25575         
25576         
25577         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25578             if (w.indexOf(tag) > -1) {
25579                 return;
25580             }
25581             this.cblack.push(tag);
25582             
25583         }, this);
25584         
25585         Roo.each(b, function(tag) {
25586             if (w.indexOf(tag) > -1) {
25587                 return;
25588             }
25589             if (this.cblack.indexOf(tag) > -1) {
25590                 return;
25591             }
25592             this.cblack.push(tag);
25593             
25594         }, this);
25595     },
25596     
25597     setStylesheets : function(stylesheets)
25598     {
25599         if(typeof(stylesheets) == 'string'){
25600             Roo.get(this.iframe.contentDocument.head).createChild({
25601                 tag : 'link',
25602                 rel : 'stylesheet',
25603                 type : 'text/css',
25604                 href : stylesheets
25605             });
25606             
25607             return;
25608         }
25609         var _this = this;
25610      
25611         Roo.each(stylesheets, function(s) {
25612             if(!s.length){
25613                 return;
25614             }
25615             
25616             Roo.get(_this.iframe.contentDocument.head).createChild({
25617                 tag : 'link',
25618                 rel : 'stylesheet',
25619                 type : 'text/css',
25620                 href : s
25621             });
25622         });
25623
25624         
25625     },
25626     
25627     removeStylesheets : function()
25628     {
25629         var _this = this;
25630         
25631         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25632             s.remove();
25633         });
25634     },
25635     
25636     setStyle : function(style)
25637     {
25638         Roo.get(this.iframe.contentDocument.head).createChild({
25639             tag : 'style',
25640             type : 'text/css',
25641             html : style
25642         });
25643
25644         return;
25645     }
25646     
25647     // hide stuff that is not compatible
25648     /**
25649      * @event blur
25650      * @hide
25651      */
25652     /**
25653      * @event change
25654      * @hide
25655      */
25656     /**
25657      * @event focus
25658      * @hide
25659      */
25660     /**
25661      * @event specialkey
25662      * @hide
25663      */
25664     /**
25665      * @cfg {String} fieldClass @hide
25666      */
25667     /**
25668      * @cfg {String} focusClass @hide
25669      */
25670     /**
25671      * @cfg {String} autoCreate @hide
25672      */
25673     /**
25674      * @cfg {String} inputType @hide
25675      */
25676     /**
25677      * @cfg {String} invalidClass @hide
25678      */
25679     /**
25680      * @cfg {String} invalidText @hide
25681      */
25682     /**
25683      * @cfg {String} msgFx @hide
25684      */
25685     /**
25686      * @cfg {String} validateOnBlur @hide
25687      */
25688 });
25689
25690 Roo.HtmlEditorCore.white = [
25691         'area', 'br', 'img', 'input', 'hr', 'wbr',
25692         
25693        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25694        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25695        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25696        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25697        'table',   'ul',         'xmp', 
25698        
25699        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25700       'thead',   'tr', 
25701      
25702       'dir', 'menu', 'ol', 'ul', 'dl',
25703        
25704       'embed',  'object'
25705 ];
25706
25707
25708 Roo.HtmlEditorCore.black = [
25709     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25710         'applet', // 
25711         'base',   'basefont', 'bgsound', 'blink',  'body', 
25712         'frame',  'frameset', 'head',    'html',   'ilayer', 
25713         'iframe', 'layer',  'link',     'meta',    'object',   
25714         'script', 'style' ,'title',  'xml' // clean later..
25715 ];
25716 Roo.HtmlEditorCore.clean = [
25717     'script', 'style', 'title', 'xml'
25718 ];
25719 Roo.HtmlEditorCore.remove = [
25720     'font'
25721 ];
25722 // attributes..
25723
25724 Roo.HtmlEditorCore.ablack = [
25725     'on'
25726 ];
25727     
25728 Roo.HtmlEditorCore.aclean = [ 
25729     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25730 ];
25731
25732 // protocols..
25733 Roo.HtmlEditorCore.pwhite= [
25734         'http',  'https',  'mailto'
25735 ];
25736
25737 // white listed style attributes.
25738 Roo.HtmlEditorCore.cwhite= [
25739       //  'text-align', /// default is to allow most things..
25740       
25741          
25742 //        'font-size'//??
25743 ];
25744
25745 // black listed style attributes.
25746 Roo.HtmlEditorCore.cblack= [
25747       //  'font-size' -- this can be set by the project 
25748 ];
25749
25750
25751 Roo.HtmlEditorCore.swapCodes   =[ 
25752     [    8211, "--" ], 
25753     [    8212, "--" ], 
25754     [    8216,  "'" ],  
25755     [    8217, "'" ],  
25756     [    8220, '"' ],  
25757     [    8221, '"' ],  
25758     [    8226, "*" ],  
25759     [    8230, "..." ]
25760 ]; 
25761
25762     /*
25763  * - LGPL
25764  *
25765  * HtmlEditor
25766  * 
25767  */
25768
25769 /**
25770  * @class Roo.bootstrap.HtmlEditor
25771  * @extends Roo.bootstrap.TextArea
25772  * Bootstrap HtmlEditor class
25773
25774  * @constructor
25775  * Create a new HtmlEditor
25776  * @param {Object} config The config object
25777  */
25778
25779 Roo.bootstrap.HtmlEditor = function(config){
25780     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25781     if (!this.toolbars) {
25782         this.toolbars = [];
25783     }
25784     
25785     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25786     this.addEvents({
25787             /**
25788              * @event initialize
25789              * Fires when the editor is fully initialized (including the iframe)
25790              * @param {HtmlEditor} this
25791              */
25792             initialize: true,
25793             /**
25794              * @event activate
25795              * Fires when the editor is first receives the focus. Any insertion must wait
25796              * until after this event.
25797              * @param {HtmlEditor} this
25798              */
25799             activate: true,
25800              /**
25801              * @event beforesync
25802              * Fires before the textarea is updated with content from the editor iframe. Return false
25803              * to cancel the sync.
25804              * @param {HtmlEditor} this
25805              * @param {String} html
25806              */
25807             beforesync: true,
25808              /**
25809              * @event beforepush
25810              * Fires before the iframe editor is updated with content from the textarea. Return false
25811              * to cancel the push.
25812              * @param {HtmlEditor} this
25813              * @param {String} html
25814              */
25815             beforepush: true,
25816              /**
25817              * @event sync
25818              * Fires when the textarea is updated with content from the editor iframe.
25819              * @param {HtmlEditor} this
25820              * @param {String} html
25821              */
25822             sync: true,
25823              /**
25824              * @event push
25825              * Fires when the iframe editor is updated with content from the textarea.
25826              * @param {HtmlEditor} this
25827              * @param {String} html
25828              */
25829             push: true,
25830              /**
25831              * @event editmodechange
25832              * Fires when the editor switches edit modes
25833              * @param {HtmlEditor} this
25834              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25835              */
25836             editmodechange: true,
25837             /**
25838              * @event editorevent
25839              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25840              * @param {HtmlEditor} this
25841              */
25842             editorevent: true,
25843             /**
25844              * @event firstfocus
25845              * Fires when on first focus - needed by toolbars..
25846              * @param {HtmlEditor} this
25847              */
25848             firstfocus: true,
25849             /**
25850              * @event autosave
25851              * Auto save the htmlEditor value as a file into Events
25852              * @param {HtmlEditor} this
25853              */
25854             autosave: true,
25855             /**
25856              * @event savedpreview
25857              * preview the saved version of htmlEditor
25858              * @param {HtmlEditor} this
25859              */
25860             savedpreview: true
25861         });
25862 };
25863
25864
25865 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25866     
25867     
25868       /**
25869      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25870      */
25871     toolbars : false,
25872     
25873      /**
25874     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25875     */
25876     btns : [],
25877    
25878      /**
25879      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25880      *                        Roo.resizable.
25881      */
25882     resizable : false,
25883      /**
25884      * @cfg {Number} height (in pixels)
25885      */   
25886     height: 300,
25887    /**
25888      * @cfg {Number} width (in pixels)
25889      */   
25890     width: false,
25891     
25892     /**
25893      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25894      * 
25895      */
25896     stylesheets: false,
25897     
25898     // id of frame..
25899     frameId: false,
25900     
25901     // private properties
25902     validationEvent : false,
25903     deferHeight: true,
25904     initialized : false,
25905     activated : false,
25906     
25907     onFocus : Roo.emptyFn,
25908     iframePad:3,
25909     hideMode:'offsets',
25910     
25911     tbContainer : false,
25912     
25913     bodyCls : '',
25914     
25915     toolbarContainer :function() {
25916         return this.wrap.select('.x-html-editor-tb',true).first();
25917     },
25918
25919     /**
25920      * Protected method that will not generally be called directly. It
25921      * is called when the editor creates its toolbar. Override this method if you need to
25922      * add custom toolbar buttons.
25923      * @param {HtmlEditor} editor
25924      */
25925     createToolbar : function(){
25926         Roo.log('renewing');
25927         Roo.log("create toolbars");
25928         
25929         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25930         this.toolbars[0].render(this.toolbarContainer());
25931         
25932         return;
25933         
25934 //        if (!editor.toolbars || !editor.toolbars.length) {
25935 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25936 //        }
25937 //        
25938 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25939 //            editor.toolbars[i] = Roo.factory(
25940 //                    typeof(editor.toolbars[i]) == 'string' ?
25941 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25942 //                Roo.bootstrap.HtmlEditor);
25943 //            editor.toolbars[i].init(editor);
25944 //        }
25945     },
25946
25947      
25948     // private
25949     onRender : function(ct, position)
25950     {
25951        // Roo.log("Call onRender: " + this.xtype);
25952         var _t = this;
25953         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25954       
25955         this.wrap = this.inputEl().wrap({
25956             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25957         });
25958         
25959         this.editorcore.onRender(ct, position);
25960          
25961         if (this.resizable) {
25962             this.resizeEl = new Roo.Resizable(this.wrap, {
25963                 pinned : true,
25964                 wrap: true,
25965                 dynamic : true,
25966                 minHeight : this.height,
25967                 height: this.height,
25968                 handles : this.resizable,
25969                 width: this.width,
25970                 listeners : {
25971                     resize : function(r, w, h) {
25972                         _t.onResize(w,h); // -something
25973                     }
25974                 }
25975             });
25976             
25977         }
25978         this.createToolbar(this);
25979        
25980         
25981         if(!this.width && this.resizable){
25982             this.setSize(this.wrap.getSize());
25983         }
25984         if (this.resizeEl) {
25985             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25986             // should trigger onReize..
25987         }
25988         
25989     },
25990
25991     // private
25992     onResize : function(w, h)
25993     {
25994         Roo.log('resize: ' +w + ',' + h );
25995         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25996         var ew = false;
25997         var eh = false;
25998         
25999         if(this.inputEl() ){
26000             if(typeof w == 'number'){
26001                 var aw = w - this.wrap.getFrameWidth('lr');
26002                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26003                 ew = aw;
26004             }
26005             if(typeof h == 'number'){
26006                  var tbh = -11;  // fixme it needs to tool bar size!
26007                 for (var i =0; i < this.toolbars.length;i++) {
26008                     // fixme - ask toolbars for heights?
26009                     tbh += this.toolbars[i].el.getHeight();
26010                     //if (this.toolbars[i].footer) {
26011                     //    tbh += this.toolbars[i].footer.el.getHeight();
26012                     //}
26013                 }
26014               
26015                 
26016                 
26017                 
26018                 
26019                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26020                 ah -= 5; // knock a few pixes off for look..
26021                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26022                 var eh = ah;
26023             }
26024         }
26025         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26026         this.editorcore.onResize(ew,eh);
26027         
26028     },
26029
26030     /**
26031      * Toggles the editor between standard and source edit mode.
26032      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26033      */
26034     toggleSourceEdit : function(sourceEditMode)
26035     {
26036         this.editorcore.toggleSourceEdit(sourceEditMode);
26037         
26038         if(this.editorcore.sourceEditMode){
26039             Roo.log('editor - showing textarea');
26040             
26041 //            Roo.log('in');
26042 //            Roo.log(this.syncValue());
26043             this.syncValue();
26044             this.inputEl().removeClass(['hide', 'x-hidden']);
26045             this.inputEl().dom.removeAttribute('tabIndex');
26046             this.inputEl().focus();
26047         }else{
26048             Roo.log('editor - hiding textarea');
26049 //            Roo.log('out')
26050 //            Roo.log(this.pushValue()); 
26051             this.pushValue();
26052             
26053             this.inputEl().addClass(['hide', 'x-hidden']);
26054             this.inputEl().dom.setAttribute('tabIndex', -1);
26055             //this.deferFocus();
26056         }
26057          
26058         if(this.resizable){
26059             this.setSize(this.wrap.getSize());
26060         }
26061         
26062         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26063     },
26064  
26065     // private (for BoxComponent)
26066     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26067
26068     // private (for BoxComponent)
26069     getResizeEl : function(){
26070         return this.wrap;
26071     },
26072
26073     // private (for BoxComponent)
26074     getPositionEl : function(){
26075         return this.wrap;
26076     },
26077
26078     // private
26079     initEvents : function(){
26080         this.originalValue = this.getValue();
26081     },
26082
26083 //    /**
26084 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26085 //     * @method
26086 //     */
26087 //    markInvalid : Roo.emptyFn,
26088 //    /**
26089 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26090 //     * @method
26091 //     */
26092 //    clearInvalid : Roo.emptyFn,
26093
26094     setValue : function(v){
26095         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26096         this.editorcore.pushValue();
26097     },
26098
26099      
26100     // private
26101     deferFocus : function(){
26102         this.focus.defer(10, this);
26103     },
26104
26105     // doc'ed in Field
26106     focus : function(){
26107         this.editorcore.focus();
26108         
26109     },
26110       
26111
26112     // private
26113     onDestroy : function(){
26114         
26115         
26116         
26117         if(this.rendered){
26118             
26119             for (var i =0; i < this.toolbars.length;i++) {
26120                 // fixme - ask toolbars for heights?
26121                 this.toolbars[i].onDestroy();
26122             }
26123             
26124             this.wrap.dom.innerHTML = '';
26125             this.wrap.remove();
26126         }
26127     },
26128
26129     // private
26130     onFirstFocus : function(){
26131         //Roo.log("onFirstFocus");
26132         this.editorcore.onFirstFocus();
26133          for (var i =0; i < this.toolbars.length;i++) {
26134             this.toolbars[i].onFirstFocus();
26135         }
26136         
26137     },
26138     
26139     // private
26140     syncValue : function()
26141     {   
26142         this.editorcore.syncValue();
26143     },
26144     
26145     pushValue : function()
26146     {   
26147         this.editorcore.pushValue();
26148     }
26149      
26150     
26151     // hide stuff that is not compatible
26152     /**
26153      * @event blur
26154      * @hide
26155      */
26156     /**
26157      * @event change
26158      * @hide
26159      */
26160     /**
26161      * @event focus
26162      * @hide
26163      */
26164     /**
26165      * @event specialkey
26166      * @hide
26167      */
26168     /**
26169      * @cfg {String} fieldClass @hide
26170      */
26171     /**
26172      * @cfg {String} focusClass @hide
26173      */
26174     /**
26175      * @cfg {String} autoCreate @hide
26176      */
26177     /**
26178      * @cfg {String} inputType @hide
26179      */
26180      
26181     /**
26182      * @cfg {String} invalidText @hide
26183      */
26184     /**
26185      * @cfg {String} msgFx @hide
26186      */
26187     /**
26188      * @cfg {String} validateOnBlur @hide
26189      */
26190 });
26191  
26192     
26193    
26194    
26195    
26196       
26197 Roo.namespace('Roo.bootstrap.htmleditor');
26198 /**
26199  * @class Roo.bootstrap.HtmlEditorToolbar1
26200  * Basic Toolbar
26201  * 
26202  * @example
26203  * Usage:
26204  *
26205  new Roo.bootstrap.HtmlEditor({
26206     ....
26207     toolbars : [
26208         new Roo.bootstrap.HtmlEditorToolbar1({
26209             disable : { fonts: 1 , format: 1, ..., ... , ...],
26210             btns : [ .... ]
26211         })
26212     }
26213      
26214  * 
26215  * @cfg {Object} disable List of elements to disable..
26216  * @cfg {Array} btns List of additional buttons.
26217  * 
26218  * 
26219  * NEEDS Extra CSS? 
26220  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26221  */
26222  
26223 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26224 {
26225     
26226     Roo.apply(this, config);
26227     
26228     // default disabled, based on 'good practice'..
26229     this.disable = this.disable || {};
26230     Roo.applyIf(this.disable, {
26231         fontSize : true,
26232         colors : true,
26233         specialElements : true
26234     });
26235     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26236     
26237     this.editor = config.editor;
26238     this.editorcore = config.editor.editorcore;
26239     
26240     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26241     
26242     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26243     // dont call parent... till later.
26244 }
26245 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26246      
26247     bar : true,
26248     
26249     editor : false,
26250     editorcore : false,
26251     
26252     
26253     formats : [
26254         "p" ,  
26255         "h1","h2","h3","h4","h5","h6", 
26256         "pre", "code", 
26257         "abbr", "acronym", "address", "cite", "samp", "var",
26258         'div','span'
26259     ],
26260     
26261     onRender : function(ct, position)
26262     {
26263        // Roo.log("Call onRender: " + this.xtype);
26264         
26265        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26266        Roo.log(this.el);
26267        this.el.dom.style.marginBottom = '0';
26268        var _this = this;
26269        var editorcore = this.editorcore;
26270        var editor= this.editor;
26271        
26272        var children = [];
26273        var btn = function(id,cmd , toggle, handler, html){
26274        
26275             var  event = toggle ? 'toggle' : 'click';
26276        
26277             var a = {
26278                 size : 'sm',
26279                 xtype: 'Button',
26280                 xns: Roo.bootstrap,
26281                 //glyphicon : id,
26282                 fa: id,
26283                 cmd : id || cmd,
26284                 enableToggle:toggle !== false,
26285                 html : html || '',
26286                 pressed : toggle ? false : null,
26287                 listeners : {}
26288             };
26289             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26290                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26291             };
26292             children.push(a);
26293             return a;
26294        }
26295        
26296     //    var cb_box = function...
26297         
26298         var style = {
26299                 xtype: 'Button',
26300                 size : 'sm',
26301                 xns: Roo.bootstrap,
26302                 fa : 'font',
26303                 //html : 'submit'
26304                 menu : {
26305                     xtype: 'Menu',
26306                     xns: Roo.bootstrap,
26307                     items:  []
26308                 }
26309         };
26310         Roo.each(this.formats, function(f) {
26311             style.menu.items.push({
26312                 xtype :'MenuItem',
26313                 xns: Roo.bootstrap,
26314                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26315                 tagname : f,
26316                 listeners : {
26317                     click : function()
26318                     {
26319                         editorcore.insertTag(this.tagname);
26320                         editor.focus();
26321                     }
26322                 }
26323                 
26324             });
26325         });
26326         children.push(style);   
26327         
26328         btn('bold',false,true);
26329         btn('italic',false,true);
26330         btn('align-left', 'justifyleft',true);
26331         btn('align-center', 'justifycenter',true);
26332         btn('align-right' , 'justifyright',true);
26333         btn('link', false, false, function(btn) {
26334             //Roo.log("create link?");
26335             var url = prompt(this.createLinkText, this.defaultLinkValue);
26336             if(url && url != 'http:/'+'/'){
26337                 this.editorcore.relayCmd('createlink', url);
26338             }
26339         }),
26340         btn('list','insertunorderedlist',true);
26341         btn('pencil', false,true, function(btn){
26342                 Roo.log(this);
26343                 this.toggleSourceEdit(btn.pressed);
26344         });
26345         
26346         if (this.editor.btns.length > 0) {
26347             for (var i = 0; i<this.editor.btns.length; i++) {
26348                 children.push(this.editor.btns[i]);
26349             }
26350         }
26351         
26352         /*
26353         var cog = {
26354                 xtype: 'Button',
26355                 size : 'sm',
26356                 xns: Roo.bootstrap,
26357                 glyphicon : 'cog',
26358                 //html : 'submit'
26359                 menu : {
26360                     xtype: 'Menu',
26361                     xns: Roo.bootstrap,
26362                     items:  []
26363                 }
26364         };
26365         
26366         cog.menu.items.push({
26367             xtype :'MenuItem',
26368             xns: Roo.bootstrap,
26369             html : Clean styles,
26370             tagname : f,
26371             listeners : {
26372                 click : function()
26373                 {
26374                     editorcore.insertTag(this.tagname);
26375                     editor.focus();
26376                 }
26377             }
26378             
26379         });
26380        */
26381         
26382          
26383        this.xtype = 'NavSimplebar';
26384         
26385         for(var i=0;i< children.length;i++) {
26386             
26387             this.buttons.add(this.addxtypeChild(children[i]));
26388             
26389         }
26390         
26391         editor.on('editorevent', this.updateToolbar, this);
26392     },
26393     onBtnClick : function(id)
26394     {
26395        this.editorcore.relayCmd(id);
26396        this.editorcore.focus();
26397     },
26398     
26399     /**
26400      * Protected method that will not generally be called directly. It triggers
26401      * a toolbar update by reading the markup state of the current selection in the editor.
26402      */
26403     updateToolbar: function(){
26404
26405         if(!this.editorcore.activated){
26406             this.editor.onFirstFocus(); // is this neeed?
26407             return;
26408         }
26409
26410         var btns = this.buttons; 
26411         var doc = this.editorcore.doc;
26412         btns.get('bold').setActive(doc.queryCommandState('bold'));
26413         btns.get('italic').setActive(doc.queryCommandState('italic'));
26414         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26415         
26416         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26417         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26418         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26419         
26420         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26421         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26422          /*
26423         
26424         var ans = this.editorcore.getAllAncestors();
26425         if (this.formatCombo) {
26426             
26427             
26428             var store = this.formatCombo.store;
26429             this.formatCombo.setValue("");
26430             for (var i =0; i < ans.length;i++) {
26431                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26432                     // select it..
26433                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26434                     break;
26435                 }
26436             }
26437         }
26438         
26439         
26440         
26441         // hides menus... - so this cant be on a menu...
26442         Roo.bootstrap.MenuMgr.hideAll();
26443         */
26444         Roo.bootstrap.MenuMgr.hideAll();
26445         //this.editorsyncValue();
26446     },
26447     onFirstFocus: function() {
26448         this.buttons.each(function(item){
26449            item.enable();
26450         });
26451     },
26452     toggleSourceEdit : function(sourceEditMode){
26453         
26454           
26455         if(sourceEditMode){
26456             Roo.log("disabling buttons");
26457            this.buttons.each( function(item){
26458                 if(item.cmd != 'pencil'){
26459                     item.disable();
26460                 }
26461             });
26462           
26463         }else{
26464             Roo.log("enabling buttons");
26465             if(this.editorcore.initialized){
26466                 this.buttons.each( function(item){
26467                     item.enable();
26468                 });
26469             }
26470             
26471         }
26472         Roo.log("calling toggole on editor");
26473         // tell the editor that it's been pressed..
26474         this.editor.toggleSourceEdit(sourceEditMode);
26475        
26476     }
26477 });
26478
26479
26480
26481
26482  
26483 /*
26484  * - LGPL
26485  */
26486
26487 /**
26488  * @class Roo.bootstrap.Markdown
26489  * @extends Roo.bootstrap.TextArea
26490  * Bootstrap Showdown editable area
26491  * @cfg {string} content
26492  * 
26493  * @constructor
26494  * Create a new Showdown
26495  */
26496
26497 Roo.bootstrap.Markdown = function(config){
26498     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26499    
26500 };
26501
26502 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26503     
26504     editing :false,
26505     
26506     initEvents : function()
26507     {
26508         
26509         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26510         this.markdownEl = this.el.createChild({
26511             cls : 'roo-markdown-area'
26512         });
26513         this.inputEl().addClass('d-none');
26514         if (this.getValue() == '') {
26515             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26516             
26517         } else {
26518             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26519         }
26520         this.markdownEl.on('click', this.toggleTextEdit, this);
26521         this.on('blur', this.toggleTextEdit, this);
26522         this.on('specialkey', this.resizeTextArea, this);
26523     },
26524     
26525     toggleTextEdit : function()
26526     {
26527         var sh = this.markdownEl.getHeight();
26528         this.inputEl().addClass('d-none');
26529         this.markdownEl.addClass('d-none');
26530         if (!this.editing) {
26531             // show editor?
26532             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26533             this.inputEl().removeClass('d-none');
26534             this.inputEl().focus();
26535             this.editing = true;
26536             return;
26537         }
26538         // show showdown...
26539         this.updateMarkdown();
26540         this.markdownEl.removeClass('d-none');
26541         this.editing = false;
26542         return;
26543     },
26544     updateMarkdown : function()
26545     {
26546         if (this.getValue() == '') {
26547             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26548             return;
26549         }
26550  
26551         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26552     },
26553     
26554     resizeTextArea: function () {
26555         
26556         var sh = 100;
26557         Roo.log([sh, this.getValue().split("\n").length * 30]);
26558         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26559     },
26560     setValue : function(val)
26561     {
26562         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26563         if (!this.editing) {
26564             this.updateMarkdown();
26565         }
26566         
26567     },
26568     focus : function()
26569     {
26570         if (!this.editing) {
26571             this.toggleTextEdit();
26572         }
26573         
26574     }
26575
26576
26577 });
26578 /**
26579  * @class Roo.bootstrap.Table.AbstractSelectionModel
26580  * @extends Roo.util.Observable
26581  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26582  * implemented by descendant classes.  This class should not be directly instantiated.
26583  * @constructor
26584  */
26585 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26586     this.locked = false;
26587     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26588 };
26589
26590
26591 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26592     /** @ignore Called by the grid automatically. Do not call directly. */
26593     init : function(grid){
26594         this.grid = grid;
26595         this.initEvents();
26596     },
26597
26598     /**
26599      * Locks the selections.
26600      */
26601     lock : function(){
26602         this.locked = true;
26603     },
26604
26605     /**
26606      * Unlocks the selections.
26607      */
26608     unlock : function(){
26609         this.locked = false;
26610     },
26611
26612     /**
26613      * Returns true if the selections are locked.
26614      * @return {Boolean}
26615      */
26616     isLocked : function(){
26617         return this.locked;
26618     },
26619     
26620     
26621     initEvents : function ()
26622     {
26623         
26624     }
26625 });
26626 /**
26627  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26628  * @class Roo.bootstrap.Table.RowSelectionModel
26629  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26630  * It supports multiple selections and keyboard selection/navigation. 
26631  * @constructor
26632  * @param {Object} config
26633  */
26634
26635 Roo.bootstrap.Table.RowSelectionModel = function(config){
26636     Roo.apply(this, config);
26637     this.selections = new Roo.util.MixedCollection(false, function(o){
26638         return o.id;
26639     });
26640
26641     this.last = false;
26642     this.lastActive = false;
26643
26644     this.addEvents({
26645         /**
26646              * @event selectionchange
26647              * Fires when the selection changes
26648              * @param {SelectionModel} this
26649              */
26650             "selectionchange" : true,
26651         /**
26652              * @event afterselectionchange
26653              * Fires after the selection changes (eg. by key press or clicking)
26654              * @param {SelectionModel} this
26655              */
26656             "afterselectionchange" : true,
26657         /**
26658              * @event beforerowselect
26659              * Fires when a row is selected being selected, return false to cancel.
26660              * @param {SelectionModel} this
26661              * @param {Number} rowIndex The selected index
26662              * @param {Boolean} keepExisting False if other selections will be cleared
26663              */
26664             "beforerowselect" : true,
26665         /**
26666              * @event rowselect
26667              * Fires when a row is selected.
26668              * @param {SelectionModel} this
26669              * @param {Number} rowIndex The selected index
26670              * @param {Roo.data.Record} r The record
26671              */
26672             "rowselect" : true,
26673         /**
26674              * @event rowdeselect
26675              * Fires when a row is deselected.
26676              * @param {SelectionModel} this
26677              * @param {Number} rowIndex The selected index
26678              */
26679         "rowdeselect" : true
26680     });
26681     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26682     this.locked = false;
26683  };
26684
26685 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26686     /**
26687      * @cfg {Boolean} singleSelect
26688      * True to allow selection of only one row at a time (defaults to false)
26689      */
26690     singleSelect : false,
26691
26692     // private
26693     initEvents : function()
26694     {
26695
26696         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26697         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26698         //}else{ // allow click to work like normal
26699          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26700         //}
26701         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26702         this.grid.on("rowclick", this.handleMouseDown, this);
26703         
26704         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26705             "up" : function(e){
26706                 if(!e.shiftKey){
26707                     this.selectPrevious(e.shiftKey);
26708                 }else if(this.last !== false && this.lastActive !== false){
26709                     var last = this.last;
26710                     this.selectRange(this.last,  this.lastActive-1);
26711                     this.grid.getView().focusRow(this.lastActive);
26712                     if(last !== false){
26713                         this.last = last;
26714                     }
26715                 }else{
26716                     this.selectFirstRow();
26717                 }
26718                 this.fireEvent("afterselectionchange", this);
26719             },
26720             "down" : function(e){
26721                 if(!e.shiftKey){
26722                     this.selectNext(e.shiftKey);
26723                 }else if(this.last !== false && this.lastActive !== false){
26724                     var last = this.last;
26725                     this.selectRange(this.last,  this.lastActive+1);
26726                     this.grid.getView().focusRow(this.lastActive);
26727                     if(last !== false){
26728                         this.last = last;
26729                     }
26730                 }else{
26731                     this.selectFirstRow();
26732                 }
26733                 this.fireEvent("afterselectionchange", this);
26734             },
26735             scope: this
26736         });
26737         this.grid.store.on('load', function(){
26738             this.selections.clear();
26739         },this);
26740         /*
26741         var view = this.grid.view;
26742         view.on("refresh", this.onRefresh, this);
26743         view.on("rowupdated", this.onRowUpdated, this);
26744         view.on("rowremoved", this.onRemove, this);
26745         */
26746     },
26747
26748     // private
26749     onRefresh : function()
26750     {
26751         var ds = this.grid.store, i, v = this.grid.view;
26752         var s = this.selections;
26753         s.each(function(r){
26754             if((i = ds.indexOfId(r.id)) != -1){
26755                 v.onRowSelect(i);
26756             }else{
26757                 s.remove(r);
26758             }
26759         });
26760     },
26761
26762     // private
26763     onRemove : function(v, index, r){
26764         this.selections.remove(r);
26765     },
26766
26767     // private
26768     onRowUpdated : function(v, index, r){
26769         if(this.isSelected(r)){
26770             v.onRowSelect(index);
26771         }
26772     },
26773
26774     /**
26775      * Select records.
26776      * @param {Array} records The records to select
26777      * @param {Boolean} keepExisting (optional) True to keep existing selections
26778      */
26779     selectRecords : function(records, keepExisting)
26780     {
26781         if(!keepExisting){
26782             this.clearSelections();
26783         }
26784             var ds = this.grid.store;
26785         for(var i = 0, len = records.length; i < len; i++){
26786             this.selectRow(ds.indexOf(records[i]), true);
26787         }
26788     },
26789
26790     /**
26791      * Gets the number of selected rows.
26792      * @return {Number}
26793      */
26794     getCount : function(){
26795         return this.selections.length;
26796     },
26797
26798     /**
26799      * Selects the first row in the grid.
26800      */
26801     selectFirstRow : function(){
26802         this.selectRow(0);
26803     },
26804
26805     /**
26806      * Select the last row.
26807      * @param {Boolean} keepExisting (optional) True to keep existing selections
26808      */
26809     selectLastRow : function(keepExisting){
26810         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26811         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26812     },
26813
26814     /**
26815      * Selects the row immediately following the last selected row.
26816      * @param {Boolean} keepExisting (optional) True to keep existing selections
26817      */
26818     selectNext : function(keepExisting)
26819     {
26820             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26821             this.selectRow(this.last+1, keepExisting);
26822             this.grid.getView().focusRow(this.last);
26823         }
26824     },
26825
26826     /**
26827      * Selects the row that precedes the last selected row.
26828      * @param {Boolean} keepExisting (optional) True to keep existing selections
26829      */
26830     selectPrevious : function(keepExisting){
26831         if(this.last){
26832             this.selectRow(this.last-1, keepExisting);
26833             this.grid.getView().focusRow(this.last);
26834         }
26835     },
26836
26837     /**
26838      * Returns the selected records
26839      * @return {Array} Array of selected records
26840      */
26841     getSelections : function(){
26842         return [].concat(this.selections.items);
26843     },
26844
26845     /**
26846      * Returns the first selected record.
26847      * @return {Record}
26848      */
26849     getSelected : function(){
26850         return this.selections.itemAt(0);
26851     },
26852
26853
26854     /**
26855      * Clears all selections.
26856      */
26857     clearSelections : function(fast)
26858     {
26859         if(this.locked) {
26860             return;
26861         }
26862         if(fast !== true){
26863                 var ds = this.grid.store;
26864             var s = this.selections;
26865             s.each(function(r){
26866                 this.deselectRow(ds.indexOfId(r.id));
26867             }, this);
26868             s.clear();
26869         }else{
26870             this.selections.clear();
26871         }
26872         this.last = false;
26873     },
26874
26875
26876     /**
26877      * Selects all rows.
26878      */
26879     selectAll : function(){
26880         if(this.locked) {
26881             return;
26882         }
26883         this.selections.clear();
26884         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26885             this.selectRow(i, true);
26886         }
26887     },
26888
26889     /**
26890      * Returns True if there is a selection.
26891      * @return {Boolean}
26892      */
26893     hasSelection : function(){
26894         return this.selections.length > 0;
26895     },
26896
26897     /**
26898      * Returns True if the specified row is selected.
26899      * @param {Number/Record} record The record or index of the record to check
26900      * @return {Boolean}
26901      */
26902     isSelected : function(index){
26903             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26904         return (r && this.selections.key(r.id) ? true : false);
26905     },
26906
26907     /**
26908      * Returns True if the specified record id is selected.
26909      * @param {String} id The id of record to check
26910      * @return {Boolean}
26911      */
26912     isIdSelected : function(id){
26913         return (this.selections.key(id) ? true : false);
26914     },
26915
26916
26917     // private
26918     handleMouseDBClick : function(e, t){
26919         
26920     },
26921     // private
26922     handleMouseDown : function(e, t)
26923     {
26924             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26925         if(this.isLocked() || rowIndex < 0 ){
26926             return;
26927         };
26928         if(e.shiftKey && this.last !== false){
26929             var last = this.last;
26930             this.selectRange(last, rowIndex, e.ctrlKey);
26931             this.last = last; // reset the last
26932             t.focus();
26933     
26934         }else{
26935             var isSelected = this.isSelected(rowIndex);
26936             //Roo.log("select row:" + rowIndex);
26937             if(isSelected){
26938                 this.deselectRow(rowIndex);
26939             } else {
26940                         this.selectRow(rowIndex, true);
26941             }
26942     
26943             /*
26944                 if(e.button !== 0 && isSelected){
26945                 alert('rowIndex 2: ' + rowIndex);
26946                     view.focusRow(rowIndex);
26947                 }else if(e.ctrlKey && isSelected){
26948                     this.deselectRow(rowIndex);
26949                 }else if(!isSelected){
26950                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26951                     view.focusRow(rowIndex);
26952                 }
26953             */
26954         }
26955         this.fireEvent("afterselectionchange", this);
26956     },
26957     // private
26958     handleDragableRowClick :  function(grid, rowIndex, e) 
26959     {
26960         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26961             this.selectRow(rowIndex, false);
26962             grid.view.focusRow(rowIndex);
26963              this.fireEvent("afterselectionchange", this);
26964         }
26965     },
26966     
26967     /**
26968      * Selects multiple rows.
26969      * @param {Array} rows Array of the indexes of the row to select
26970      * @param {Boolean} keepExisting (optional) True to keep existing selections
26971      */
26972     selectRows : function(rows, keepExisting){
26973         if(!keepExisting){
26974             this.clearSelections();
26975         }
26976         for(var i = 0, len = rows.length; i < len; i++){
26977             this.selectRow(rows[i], true);
26978         }
26979     },
26980
26981     /**
26982      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26983      * @param {Number} startRow The index of the first row in the range
26984      * @param {Number} endRow The index of the last row in the range
26985      * @param {Boolean} keepExisting (optional) True to retain existing selections
26986      */
26987     selectRange : function(startRow, endRow, keepExisting){
26988         if(this.locked) {
26989             return;
26990         }
26991         if(!keepExisting){
26992             this.clearSelections();
26993         }
26994         if(startRow <= endRow){
26995             for(var i = startRow; i <= endRow; i++){
26996                 this.selectRow(i, true);
26997             }
26998         }else{
26999             for(var i = startRow; i >= endRow; i--){
27000                 this.selectRow(i, true);
27001             }
27002         }
27003     },
27004
27005     /**
27006      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27007      * @param {Number} startRow The index of the first row in the range
27008      * @param {Number} endRow The index of the last row in the range
27009      */
27010     deselectRange : function(startRow, endRow, preventViewNotify){
27011         if(this.locked) {
27012             return;
27013         }
27014         for(var i = startRow; i <= endRow; i++){
27015             this.deselectRow(i, preventViewNotify);
27016         }
27017     },
27018
27019     /**
27020      * Selects a row.
27021      * @param {Number} row The index of the row to select
27022      * @param {Boolean} keepExisting (optional) True to keep existing selections
27023      */
27024     selectRow : function(index, keepExisting, preventViewNotify)
27025     {
27026             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27027             return;
27028         }
27029         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27030             if(!keepExisting || this.singleSelect){
27031                 this.clearSelections();
27032             }
27033             
27034             var r = this.grid.store.getAt(index);
27035             //console.log('selectRow - record id :' + r.id);
27036             
27037             this.selections.add(r);
27038             this.last = this.lastActive = index;
27039             if(!preventViewNotify){
27040                 var proxy = new Roo.Element(
27041                                 this.grid.getRowDom(index)
27042                 );
27043                 proxy.addClass('bg-info info');
27044             }
27045             this.fireEvent("rowselect", this, index, r);
27046             this.fireEvent("selectionchange", this);
27047         }
27048     },
27049
27050     /**
27051      * Deselects a row.
27052      * @param {Number} row The index of the row to deselect
27053      */
27054     deselectRow : function(index, preventViewNotify)
27055     {
27056         if(this.locked) {
27057             return;
27058         }
27059         if(this.last == index){
27060             this.last = false;
27061         }
27062         if(this.lastActive == index){
27063             this.lastActive = false;
27064         }
27065         
27066         var r = this.grid.store.getAt(index);
27067         if (!r) {
27068             return;
27069         }
27070         
27071         this.selections.remove(r);
27072         //.console.log('deselectRow - record id :' + r.id);
27073         if(!preventViewNotify){
27074         
27075             var proxy = new Roo.Element(
27076                 this.grid.getRowDom(index)
27077             );
27078             proxy.removeClass('bg-info info');
27079         }
27080         this.fireEvent("rowdeselect", this, index);
27081         this.fireEvent("selectionchange", this);
27082     },
27083
27084     // private
27085     restoreLast : function(){
27086         if(this._last){
27087             this.last = this._last;
27088         }
27089     },
27090
27091     // private
27092     acceptsNav : function(row, col, cm){
27093         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27094     },
27095
27096     // private
27097     onEditorKey : function(field, e){
27098         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27099         if(k == e.TAB){
27100             e.stopEvent();
27101             ed.completeEdit();
27102             if(e.shiftKey){
27103                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27104             }else{
27105                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27106             }
27107         }else if(k == e.ENTER && !e.ctrlKey){
27108             e.stopEvent();
27109             ed.completeEdit();
27110             if(e.shiftKey){
27111                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27112             }else{
27113                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27114             }
27115         }else if(k == e.ESC){
27116             ed.cancelEdit();
27117         }
27118         if(newCell){
27119             g.startEditing(newCell[0], newCell[1]);
27120         }
27121     }
27122 });
27123 /*
27124  * Based on:
27125  * Ext JS Library 1.1.1
27126  * Copyright(c) 2006-2007, Ext JS, LLC.
27127  *
27128  * Originally Released Under LGPL - original licence link has changed is not relivant.
27129  *
27130  * Fork - LGPL
27131  * <script type="text/javascript">
27132  */
27133  
27134 /**
27135  * @class Roo.bootstrap.PagingToolbar
27136  * @extends Roo.bootstrap.NavSimplebar
27137  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27138  * @constructor
27139  * Create a new PagingToolbar
27140  * @param {Object} config The config object
27141  * @param {Roo.data.Store} store
27142  */
27143 Roo.bootstrap.PagingToolbar = function(config)
27144 {
27145     // old args format still supported... - xtype is prefered..
27146         // created from xtype...
27147     
27148     this.ds = config.dataSource;
27149     
27150     if (config.store && !this.ds) {
27151         this.store= Roo.factory(config.store, Roo.data);
27152         this.ds = this.store;
27153         this.ds.xmodule = this.xmodule || false;
27154     }
27155     
27156     this.toolbarItems = [];
27157     if (config.items) {
27158         this.toolbarItems = config.items;
27159     }
27160     
27161     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27162     
27163     this.cursor = 0;
27164     
27165     if (this.ds) { 
27166         this.bind(this.ds);
27167     }
27168     
27169     if (Roo.bootstrap.version == 4) {
27170         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27171     } else {
27172         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27173     }
27174     
27175 };
27176
27177 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27178     /**
27179      * @cfg {Roo.data.Store} dataSource
27180      * The underlying data store providing the paged data
27181      */
27182     /**
27183      * @cfg {String/HTMLElement/Element} container
27184      * container The id or element that will contain the toolbar
27185      */
27186     /**
27187      * @cfg {Boolean} displayInfo
27188      * True to display the displayMsg (defaults to false)
27189      */
27190     /**
27191      * @cfg {Number} pageSize
27192      * The number of records to display per page (defaults to 20)
27193      */
27194     pageSize: 20,
27195     /**
27196      * @cfg {String} displayMsg
27197      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27198      */
27199     displayMsg : 'Displaying {0} - {1} of {2}',
27200     /**
27201      * @cfg {String} emptyMsg
27202      * The message to display when no records are found (defaults to "No data to display")
27203      */
27204     emptyMsg : 'No data to display',
27205     /**
27206      * Customizable piece of the default paging text (defaults to "Page")
27207      * @type String
27208      */
27209     beforePageText : "Page",
27210     /**
27211      * Customizable piece of the default paging text (defaults to "of %0")
27212      * @type String
27213      */
27214     afterPageText : "of {0}",
27215     /**
27216      * Customizable piece of the default paging text (defaults to "First Page")
27217      * @type String
27218      */
27219     firstText : "First Page",
27220     /**
27221      * Customizable piece of the default paging text (defaults to "Previous Page")
27222      * @type String
27223      */
27224     prevText : "Previous Page",
27225     /**
27226      * Customizable piece of the default paging text (defaults to "Next Page")
27227      * @type String
27228      */
27229     nextText : "Next Page",
27230     /**
27231      * Customizable piece of the default paging text (defaults to "Last Page")
27232      * @type String
27233      */
27234     lastText : "Last Page",
27235     /**
27236      * Customizable piece of the default paging text (defaults to "Refresh")
27237      * @type String
27238      */
27239     refreshText : "Refresh",
27240
27241     buttons : false,
27242     // private
27243     onRender : function(ct, position) 
27244     {
27245         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27246         this.navgroup.parentId = this.id;
27247         this.navgroup.onRender(this.el, null);
27248         // add the buttons to the navgroup
27249         
27250         if(this.displayInfo){
27251             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27252             this.displayEl = this.el.select('.x-paging-info', true).first();
27253 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27254 //            this.displayEl = navel.el.select('span',true).first();
27255         }
27256         
27257         var _this = this;
27258         
27259         if(this.buttons){
27260             Roo.each(_this.buttons, function(e){ // this might need to use render????
27261                Roo.factory(e).render(_this.el);
27262             });
27263         }
27264             
27265         Roo.each(_this.toolbarItems, function(e) {
27266             _this.navgroup.addItem(e);
27267         });
27268         
27269         
27270         this.first = this.navgroup.addItem({
27271             tooltip: this.firstText,
27272             cls: "prev btn-outline-secondary",
27273             html : ' <i class="fa fa-step-backward"></i>',
27274             disabled: true,
27275             preventDefault: true,
27276             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27277         });
27278         
27279         this.prev =  this.navgroup.addItem({
27280             tooltip: this.prevText,
27281             cls: "prev btn-outline-secondary",
27282             html : ' <i class="fa fa-backward"></i>',
27283             disabled: true,
27284             preventDefault: true,
27285             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27286         });
27287     //this.addSeparator();
27288         
27289         
27290         var field = this.navgroup.addItem( {
27291             tagtype : 'span',
27292             cls : 'x-paging-position  btn-outline-secondary',
27293              disabled: true,
27294             html : this.beforePageText  +
27295                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27296                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27297          } ); //?? escaped?
27298         
27299         this.field = field.el.select('input', true).first();
27300         this.field.on("keydown", this.onPagingKeydown, this);
27301         this.field.on("focus", function(){this.dom.select();});
27302     
27303     
27304         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27305         //this.field.setHeight(18);
27306         //this.addSeparator();
27307         this.next = this.navgroup.addItem({
27308             tooltip: this.nextText,
27309             cls: "next btn-outline-secondary",
27310             html : ' <i class="fa fa-forward"></i>',
27311             disabled: true,
27312             preventDefault: true,
27313             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27314         });
27315         this.last = this.navgroup.addItem({
27316             tooltip: this.lastText,
27317             html : ' <i class="fa fa-step-forward"></i>',
27318             cls: "next btn-outline-secondary",
27319             disabled: true,
27320             preventDefault: true,
27321             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27322         });
27323     //this.addSeparator();
27324         this.loading = this.navgroup.addItem({
27325             tooltip: this.refreshText,
27326             cls: "btn-outline-secondary",
27327             html : ' <i class="fa fa-refresh"></i>',
27328             preventDefault: true,
27329             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27330         });
27331         
27332     },
27333
27334     // private
27335     updateInfo : function(){
27336         if(this.displayEl){
27337             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27338             var msg = count == 0 ?
27339                 this.emptyMsg :
27340                 String.format(
27341                     this.displayMsg,
27342                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27343                 );
27344             this.displayEl.update(msg);
27345         }
27346     },
27347
27348     // private
27349     onLoad : function(ds, r, o)
27350     {
27351         this.cursor = o.params && o.params.start ? o.params.start : 0;
27352         
27353         var d = this.getPageData(),
27354             ap = d.activePage,
27355             ps = d.pages;
27356         
27357         
27358         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27359         this.field.dom.value = ap;
27360         this.first.setDisabled(ap == 1);
27361         this.prev.setDisabled(ap == 1);
27362         this.next.setDisabled(ap == ps);
27363         this.last.setDisabled(ap == ps);
27364         this.loading.enable();
27365         this.updateInfo();
27366     },
27367
27368     // private
27369     getPageData : function(){
27370         var total = this.ds.getTotalCount();
27371         return {
27372             total : total,
27373             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27374             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27375         };
27376     },
27377
27378     // private
27379     onLoadError : function(){
27380         this.loading.enable();
27381     },
27382
27383     // private
27384     onPagingKeydown : function(e){
27385         var k = e.getKey();
27386         var d = this.getPageData();
27387         if(k == e.RETURN){
27388             var v = this.field.dom.value, pageNum;
27389             if(!v || isNaN(pageNum = parseInt(v, 10))){
27390                 this.field.dom.value = d.activePage;
27391                 return;
27392             }
27393             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27394             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27395             e.stopEvent();
27396         }
27397         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))
27398         {
27399           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27400           this.field.dom.value = pageNum;
27401           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27402           e.stopEvent();
27403         }
27404         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27405         {
27406           var v = this.field.dom.value, pageNum; 
27407           var increment = (e.shiftKey) ? 10 : 1;
27408           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27409                 increment *= -1;
27410           }
27411           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27412             this.field.dom.value = d.activePage;
27413             return;
27414           }
27415           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27416           {
27417             this.field.dom.value = parseInt(v, 10) + increment;
27418             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27419             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27420           }
27421           e.stopEvent();
27422         }
27423     },
27424
27425     // private
27426     beforeLoad : function(){
27427         if(this.loading){
27428             this.loading.disable();
27429         }
27430     },
27431
27432     // private
27433     onClick : function(which){
27434         
27435         var ds = this.ds;
27436         if (!ds) {
27437             return;
27438         }
27439         
27440         switch(which){
27441             case "first":
27442                 ds.load({params:{start: 0, limit: this.pageSize}});
27443             break;
27444             case "prev":
27445                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27446             break;
27447             case "next":
27448                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27449             break;
27450             case "last":
27451                 var total = ds.getTotalCount();
27452                 var extra = total % this.pageSize;
27453                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27454                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27455             break;
27456             case "refresh":
27457                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27458             break;
27459         }
27460     },
27461
27462     /**
27463      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27464      * @param {Roo.data.Store} store The data store to unbind
27465      */
27466     unbind : function(ds){
27467         ds.un("beforeload", this.beforeLoad, this);
27468         ds.un("load", this.onLoad, this);
27469         ds.un("loadexception", this.onLoadError, this);
27470         ds.un("remove", this.updateInfo, this);
27471         ds.un("add", this.updateInfo, this);
27472         this.ds = undefined;
27473     },
27474
27475     /**
27476      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27477      * @param {Roo.data.Store} store The data store to bind
27478      */
27479     bind : function(ds){
27480         ds.on("beforeload", this.beforeLoad, this);
27481         ds.on("load", this.onLoad, this);
27482         ds.on("loadexception", this.onLoadError, this);
27483         ds.on("remove", this.updateInfo, this);
27484         ds.on("add", this.updateInfo, this);
27485         this.ds = ds;
27486     }
27487 });/*
27488  * - LGPL
27489  *
27490  * element
27491  * 
27492  */
27493
27494 /**
27495  * @class Roo.bootstrap.MessageBar
27496  * @extends Roo.bootstrap.Component
27497  * Bootstrap MessageBar class
27498  * @cfg {String} html contents of the MessageBar
27499  * @cfg {String} weight (info | success | warning | danger) default info
27500  * @cfg {String} beforeClass insert the bar before the given class
27501  * @cfg {Boolean} closable (true | false) default false
27502  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27503  * 
27504  * @constructor
27505  * Create a new Element
27506  * @param {Object} config The config object
27507  */
27508
27509 Roo.bootstrap.MessageBar = function(config){
27510     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27511 };
27512
27513 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27514     
27515     html: '',
27516     weight: 'info',
27517     closable: false,
27518     fixed: false,
27519     beforeClass: 'bootstrap-sticky-wrap',
27520     
27521     getAutoCreate : function(){
27522         
27523         var cfg = {
27524             tag: 'div',
27525             cls: 'alert alert-dismissable alert-' + this.weight,
27526             cn: [
27527                 {
27528                     tag: 'span',
27529                     cls: 'message',
27530                     html: this.html || ''
27531                 }
27532             ]
27533         };
27534         
27535         if(this.fixed){
27536             cfg.cls += ' alert-messages-fixed';
27537         }
27538         
27539         if(this.closable){
27540             cfg.cn.push({
27541                 tag: 'button',
27542                 cls: 'close',
27543                 html: 'x'
27544             });
27545         }
27546         
27547         return cfg;
27548     },
27549     
27550     onRender : function(ct, position)
27551     {
27552         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27553         
27554         if(!this.el){
27555             var cfg = Roo.apply({},  this.getAutoCreate());
27556             cfg.id = Roo.id();
27557             
27558             if (this.cls) {
27559                 cfg.cls += ' ' + this.cls;
27560             }
27561             if (this.style) {
27562                 cfg.style = this.style;
27563             }
27564             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27565             
27566             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27567         }
27568         
27569         this.el.select('>button.close').on('click', this.hide, this);
27570         
27571     },
27572     
27573     show : function()
27574     {
27575         if (!this.rendered) {
27576             this.render();
27577         }
27578         
27579         this.el.show();
27580         
27581         this.fireEvent('show', this);
27582         
27583     },
27584     
27585     hide : function()
27586     {
27587         if (!this.rendered) {
27588             this.render();
27589         }
27590         
27591         this.el.hide();
27592         
27593         this.fireEvent('hide', this);
27594     },
27595     
27596     update : function()
27597     {
27598 //        var e = this.el.dom.firstChild;
27599 //        
27600 //        if(this.closable){
27601 //            e = e.nextSibling;
27602 //        }
27603 //        
27604 //        e.data = this.html || '';
27605
27606         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27607     }
27608    
27609 });
27610
27611  
27612
27613      /*
27614  * - LGPL
27615  *
27616  * Graph
27617  * 
27618  */
27619
27620
27621 /**
27622  * @class Roo.bootstrap.Graph
27623  * @extends Roo.bootstrap.Component
27624  * Bootstrap Graph class
27625 > Prameters
27626  -sm {number} sm 4
27627  -md {number} md 5
27628  @cfg {String} graphtype  bar | vbar | pie
27629  @cfg {number} g_x coodinator | centre x (pie)
27630  @cfg {number} g_y coodinator | centre y (pie)
27631  @cfg {number} g_r radius (pie)
27632  @cfg {number} g_height height of the chart (respected by all elements in the set)
27633  @cfg {number} g_width width of the chart (respected by all elements in the set)
27634  @cfg {Object} title The title of the chart
27635     
27636  -{Array}  values
27637  -opts (object) options for the chart 
27638      o {
27639      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27640      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27641      o vgutter (number)
27642      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.
27643      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27644      o to
27645      o stretch (boolean)
27646      o }
27647  -opts (object) options for the pie
27648      o{
27649      o cut
27650      o startAngle (number)
27651      o endAngle (number)
27652      } 
27653  *
27654  * @constructor
27655  * Create a new Input
27656  * @param {Object} config The config object
27657  */
27658
27659 Roo.bootstrap.Graph = function(config){
27660     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27661     
27662     this.addEvents({
27663         // img events
27664         /**
27665          * @event click
27666          * The img click event for the img.
27667          * @param {Roo.EventObject} e
27668          */
27669         "click" : true
27670     });
27671 };
27672
27673 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27674     
27675     sm: 4,
27676     md: 5,
27677     graphtype: 'bar',
27678     g_height: 250,
27679     g_width: 400,
27680     g_x: 50,
27681     g_y: 50,
27682     g_r: 30,
27683     opts:{
27684         //g_colors: this.colors,
27685         g_type: 'soft',
27686         g_gutter: '20%'
27687
27688     },
27689     title : false,
27690
27691     getAutoCreate : function(){
27692         
27693         var cfg = {
27694             tag: 'div',
27695             html : null
27696         };
27697         
27698         
27699         return  cfg;
27700     },
27701
27702     onRender : function(ct,position){
27703         
27704         
27705         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27706         
27707         if (typeof(Raphael) == 'undefined') {
27708             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27709             return;
27710         }
27711         
27712         this.raphael = Raphael(this.el.dom);
27713         
27714                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27715                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27716                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27717                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27718                 /*
27719                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27720                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27721                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27722                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27723                 
27724                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27725                 r.barchart(330, 10, 300, 220, data1);
27726                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27727                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27728                 */
27729                 
27730                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27731                 // r.barchart(30, 30, 560, 250,  xdata, {
27732                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27733                 //     axis : "0 0 1 1",
27734                 //     axisxlabels :  xdata
27735                 //     //yvalues : cols,
27736                    
27737                 // });
27738 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27739 //        
27740 //        this.load(null,xdata,{
27741 //                axis : "0 0 1 1",
27742 //                axisxlabels :  xdata
27743 //                });
27744
27745     },
27746
27747     load : function(graphtype,xdata,opts)
27748     {
27749         this.raphael.clear();
27750         if(!graphtype) {
27751             graphtype = this.graphtype;
27752         }
27753         if(!opts){
27754             opts = this.opts;
27755         }
27756         var r = this.raphael,
27757             fin = function () {
27758                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27759             },
27760             fout = function () {
27761                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27762             },
27763             pfin = function() {
27764                 this.sector.stop();
27765                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27766
27767                 if (this.label) {
27768                     this.label[0].stop();
27769                     this.label[0].attr({ r: 7.5 });
27770                     this.label[1].attr({ "font-weight": 800 });
27771                 }
27772             },
27773             pfout = function() {
27774                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27775
27776                 if (this.label) {
27777                     this.label[0].animate({ r: 5 }, 500, "bounce");
27778                     this.label[1].attr({ "font-weight": 400 });
27779                 }
27780             };
27781
27782         switch(graphtype){
27783             case 'bar':
27784                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27785                 break;
27786             case 'hbar':
27787                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27788                 break;
27789             case 'pie':
27790 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27791 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27792 //            
27793                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27794                 
27795                 break;
27796
27797         }
27798         
27799         if(this.title){
27800             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27801         }
27802         
27803     },
27804     
27805     setTitle: function(o)
27806     {
27807         this.title = o;
27808     },
27809     
27810     initEvents: function() {
27811         
27812         if(!this.href){
27813             this.el.on('click', this.onClick, this);
27814         }
27815     },
27816     
27817     onClick : function(e)
27818     {
27819         Roo.log('img onclick');
27820         this.fireEvent('click', this, e);
27821     }
27822    
27823 });
27824
27825  
27826 /*
27827  * - LGPL
27828  *
27829  * numberBox
27830  * 
27831  */
27832 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27833
27834 /**
27835  * @class Roo.bootstrap.dash.NumberBox
27836  * @extends Roo.bootstrap.Component
27837  * Bootstrap NumberBox class
27838  * @cfg {String} headline Box headline
27839  * @cfg {String} content Box content
27840  * @cfg {String} icon Box icon
27841  * @cfg {String} footer Footer text
27842  * @cfg {String} fhref Footer href
27843  * 
27844  * @constructor
27845  * Create a new NumberBox
27846  * @param {Object} config The config object
27847  */
27848
27849
27850 Roo.bootstrap.dash.NumberBox = function(config){
27851     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27852     
27853 };
27854
27855 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27856     
27857     headline : '',
27858     content : '',
27859     icon : '',
27860     footer : '',
27861     fhref : '',
27862     ficon : '',
27863     
27864     getAutoCreate : function(){
27865         
27866         var cfg = {
27867             tag : 'div',
27868             cls : 'small-box ',
27869             cn : [
27870                 {
27871                     tag : 'div',
27872                     cls : 'inner',
27873                     cn :[
27874                         {
27875                             tag : 'h3',
27876                             cls : 'roo-headline',
27877                             html : this.headline
27878                         },
27879                         {
27880                             tag : 'p',
27881                             cls : 'roo-content',
27882                             html : this.content
27883                         }
27884                     ]
27885                 }
27886             ]
27887         };
27888         
27889         if(this.icon){
27890             cfg.cn.push({
27891                 tag : 'div',
27892                 cls : 'icon',
27893                 cn :[
27894                     {
27895                         tag : 'i',
27896                         cls : 'ion ' + this.icon
27897                     }
27898                 ]
27899             });
27900         }
27901         
27902         if(this.footer){
27903             var footer = {
27904                 tag : 'a',
27905                 cls : 'small-box-footer',
27906                 href : this.fhref || '#',
27907                 html : this.footer
27908             };
27909             
27910             cfg.cn.push(footer);
27911             
27912         }
27913         
27914         return  cfg;
27915     },
27916
27917     onRender : function(ct,position){
27918         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27919
27920
27921        
27922                 
27923     },
27924
27925     setHeadline: function (value)
27926     {
27927         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27928     },
27929     
27930     setFooter: function (value, href)
27931     {
27932         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27933         
27934         if(href){
27935             this.el.select('a.small-box-footer',true).first().attr('href', href);
27936         }
27937         
27938     },
27939
27940     setContent: function (value)
27941     {
27942         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27943     },
27944
27945     initEvents: function() 
27946     {   
27947         
27948     }
27949     
27950 });
27951
27952  
27953 /*
27954  * - LGPL
27955  *
27956  * TabBox
27957  * 
27958  */
27959 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27960
27961 /**
27962  * @class Roo.bootstrap.dash.TabBox
27963  * @extends Roo.bootstrap.Component
27964  * Bootstrap TabBox class
27965  * @cfg {String} title Title of the TabBox
27966  * @cfg {String} icon Icon of the TabBox
27967  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27968  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27969  * 
27970  * @constructor
27971  * Create a new TabBox
27972  * @param {Object} config The config object
27973  */
27974
27975
27976 Roo.bootstrap.dash.TabBox = function(config){
27977     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27978     this.addEvents({
27979         // raw events
27980         /**
27981          * @event addpane
27982          * When a pane is added
27983          * @param {Roo.bootstrap.dash.TabPane} pane
27984          */
27985         "addpane" : true,
27986         /**
27987          * @event activatepane
27988          * When a pane is activated
27989          * @param {Roo.bootstrap.dash.TabPane} pane
27990          */
27991         "activatepane" : true
27992         
27993          
27994     });
27995     
27996     this.panes = [];
27997 };
27998
27999 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28000
28001     title : '',
28002     icon : false,
28003     showtabs : true,
28004     tabScrollable : false,
28005     
28006     getChildContainer : function()
28007     {
28008         return this.el.select('.tab-content', true).first();
28009     },
28010     
28011     getAutoCreate : function(){
28012         
28013         var header = {
28014             tag: 'li',
28015             cls: 'pull-left header',
28016             html: this.title,
28017             cn : []
28018         };
28019         
28020         if(this.icon){
28021             header.cn.push({
28022                 tag: 'i',
28023                 cls: 'fa ' + this.icon
28024             });
28025         }
28026         
28027         var h = {
28028             tag: 'ul',
28029             cls: 'nav nav-tabs pull-right',
28030             cn: [
28031                 header
28032             ]
28033         };
28034         
28035         if(this.tabScrollable){
28036             h = {
28037                 tag: 'div',
28038                 cls: 'tab-header',
28039                 cn: [
28040                     {
28041                         tag: 'ul',
28042                         cls: 'nav nav-tabs pull-right',
28043                         cn: [
28044                             header
28045                         ]
28046                     }
28047                 ]
28048             };
28049         }
28050         
28051         var cfg = {
28052             tag: 'div',
28053             cls: 'nav-tabs-custom',
28054             cn: [
28055                 h,
28056                 {
28057                     tag: 'div',
28058                     cls: 'tab-content no-padding',
28059                     cn: []
28060                 }
28061             ]
28062         };
28063
28064         return  cfg;
28065     },
28066     initEvents : function()
28067     {
28068         //Roo.log('add add pane handler');
28069         this.on('addpane', this.onAddPane, this);
28070     },
28071      /**
28072      * Updates the box title
28073      * @param {String} html to set the title to.
28074      */
28075     setTitle : function(value)
28076     {
28077         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28078     },
28079     onAddPane : function(pane)
28080     {
28081         this.panes.push(pane);
28082         //Roo.log('addpane');
28083         //Roo.log(pane);
28084         // tabs are rendere left to right..
28085         if(!this.showtabs){
28086             return;
28087         }
28088         
28089         var ctr = this.el.select('.nav-tabs', true).first();
28090          
28091          
28092         var existing = ctr.select('.nav-tab',true);
28093         var qty = existing.getCount();;
28094         
28095         
28096         var tab = ctr.createChild({
28097             tag : 'li',
28098             cls : 'nav-tab' + (qty ? '' : ' active'),
28099             cn : [
28100                 {
28101                     tag : 'a',
28102                     href:'#',
28103                     html : pane.title
28104                 }
28105             ]
28106         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28107         pane.tab = tab;
28108         
28109         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28110         if (!qty) {
28111             pane.el.addClass('active');
28112         }
28113         
28114                 
28115     },
28116     onTabClick : function(ev,un,ob,pane)
28117     {
28118         //Roo.log('tab - prev default');
28119         ev.preventDefault();
28120         
28121         
28122         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28123         pane.tab.addClass('active');
28124         //Roo.log(pane.title);
28125         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28126         // technically we should have a deactivate event.. but maybe add later.
28127         // and it should not de-activate the selected tab...
28128         this.fireEvent('activatepane', pane);
28129         pane.el.addClass('active');
28130         pane.fireEvent('activate');
28131         
28132         
28133     },
28134     
28135     getActivePane : function()
28136     {
28137         var r = false;
28138         Roo.each(this.panes, function(p) {
28139             if(p.el.hasClass('active')){
28140                 r = p;
28141                 return false;
28142             }
28143             
28144             return;
28145         });
28146         
28147         return r;
28148     }
28149     
28150     
28151 });
28152
28153  
28154 /*
28155  * - LGPL
28156  *
28157  * Tab pane
28158  * 
28159  */
28160 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28161 /**
28162  * @class Roo.bootstrap.TabPane
28163  * @extends Roo.bootstrap.Component
28164  * Bootstrap TabPane class
28165  * @cfg {Boolean} active (false | true) Default false
28166  * @cfg {String} title title of panel
28167
28168  * 
28169  * @constructor
28170  * Create a new TabPane
28171  * @param {Object} config The config object
28172  */
28173
28174 Roo.bootstrap.dash.TabPane = function(config){
28175     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28176     
28177     this.addEvents({
28178         // raw events
28179         /**
28180          * @event activate
28181          * When a pane is activated
28182          * @param {Roo.bootstrap.dash.TabPane} pane
28183          */
28184         "activate" : true
28185          
28186     });
28187 };
28188
28189 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28190     
28191     active : false,
28192     title : '',
28193     
28194     // the tabBox that this is attached to.
28195     tab : false,
28196      
28197     getAutoCreate : function() 
28198     {
28199         var cfg = {
28200             tag: 'div',
28201             cls: 'tab-pane'
28202         };
28203         
28204         if(this.active){
28205             cfg.cls += ' active';
28206         }
28207         
28208         return cfg;
28209     },
28210     initEvents  : function()
28211     {
28212         //Roo.log('trigger add pane handler');
28213         this.parent().fireEvent('addpane', this)
28214     },
28215     
28216      /**
28217      * Updates the tab title 
28218      * @param {String} html to set the title to.
28219      */
28220     setTitle: function(str)
28221     {
28222         if (!this.tab) {
28223             return;
28224         }
28225         this.title = str;
28226         this.tab.select('a', true).first().dom.innerHTML = str;
28227         
28228     }
28229     
28230     
28231     
28232 });
28233
28234  
28235
28236
28237  /*
28238  * - LGPL
28239  *
28240  * menu
28241  * 
28242  */
28243 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28244
28245 /**
28246  * @class Roo.bootstrap.menu.Menu
28247  * @extends Roo.bootstrap.Component
28248  * Bootstrap Menu class - container for Menu
28249  * @cfg {String} html Text of the menu
28250  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28251  * @cfg {String} icon Font awesome icon
28252  * @cfg {String} pos Menu align to (top | bottom) default bottom
28253  * 
28254  * 
28255  * @constructor
28256  * Create a new Menu
28257  * @param {Object} config The config object
28258  */
28259
28260
28261 Roo.bootstrap.menu.Menu = function(config){
28262     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28263     
28264     this.addEvents({
28265         /**
28266          * @event beforeshow
28267          * Fires before this menu is displayed
28268          * @param {Roo.bootstrap.menu.Menu} this
28269          */
28270         beforeshow : true,
28271         /**
28272          * @event beforehide
28273          * Fires before this menu is hidden
28274          * @param {Roo.bootstrap.menu.Menu} this
28275          */
28276         beforehide : true,
28277         /**
28278          * @event show
28279          * Fires after this menu is displayed
28280          * @param {Roo.bootstrap.menu.Menu} this
28281          */
28282         show : true,
28283         /**
28284          * @event hide
28285          * Fires after this menu is hidden
28286          * @param {Roo.bootstrap.menu.Menu} this
28287          */
28288         hide : true,
28289         /**
28290          * @event click
28291          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28292          * @param {Roo.bootstrap.menu.Menu} this
28293          * @param {Roo.EventObject} e
28294          */
28295         click : true
28296     });
28297     
28298 };
28299
28300 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28301     
28302     submenu : false,
28303     html : '',
28304     weight : 'default',
28305     icon : false,
28306     pos : 'bottom',
28307     
28308     
28309     getChildContainer : function() {
28310         if(this.isSubMenu){
28311             return this.el;
28312         }
28313         
28314         return this.el.select('ul.dropdown-menu', true).first();  
28315     },
28316     
28317     getAutoCreate : function()
28318     {
28319         var text = [
28320             {
28321                 tag : 'span',
28322                 cls : 'roo-menu-text',
28323                 html : this.html
28324             }
28325         ];
28326         
28327         if(this.icon){
28328             text.unshift({
28329                 tag : 'i',
28330                 cls : 'fa ' + this.icon
28331             })
28332         }
28333         
28334         
28335         var cfg = {
28336             tag : 'div',
28337             cls : 'btn-group',
28338             cn : [
28339                 {
28340                     tag : 'button',
28341                     cls : 'dropdown-button btn btn-' + this.weight,
28342                     cn : text
28343                 },
28344                 {
28345                     tag : 'button',
28346                     cls : 'dropdown-toggle btn btn-' + this.weight,
28347                     cn : [
28348                         {
28349                             tag : 'span',
28350                             cls : 'caret'
28351                         }
28352                     ]
28353                 },
28354                 {
28355                     tag : 'ul',
28356                     cls : 'dropdown-menu'
28357                 }
28358             ]
28359             
28360         };
28361         
28362         if(this.pos == 'top'){
28363             cfg.cls += ' dropup';
28364         }
28365         
28366         if(this.isSubMenu){
28367             cfg = {
28368                 tag : 'ul',
28369                 cls : 'dropdown-menu'
28370             }
28371         }
28372         
28373         return cfg;
28374     },
28375     
28376     onRender : function(ct, position)
28377     {
28378         this.isSubMenu = ct.hasClass('dropdown-submenu');
28379         
28380         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28381     },
28382     
28383     initEvents : function() 
28384     {
28385         if(this.isSubMenu){
28386             return;
28387         }
28388         
28389         this.hidden = true;
28390         
28391         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28392         this.triggerEl.on('click', this.onTriggerPress, this);
28393         
28394         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28395         this.buttonEl.on('click', this.onClick, this);
28396         
28397     },
28398     
28399     list : function()
28400     {
28401         if(this.isSubMenu){
28402             return this.el;
28403         }
28404         
28405         return this.el.select('ul.dropdown-menu', true).first();
28406     },
28407     
28408     onClick : function(e)
28409     {
28410         this.fireEvent("click", this, e);
28411     },
28412     
28413     onTriggerPress  : function(e)
28414     {   
28415         if (this.isVisible()) {
28416             this.hide();
28417         } else {
28418             this.show();
28419         }
28420     },
28421     
28422     isVisible : function(){
28423         return !this.hidden;
28424     },
28425     
28426     show : function()
28427     {
28428         this.fireEvent("beforeshow", this);
28429         
28430         this.hidden = false;
28431         this.el.addClass('open');
28432         
28433         Roo.get(document).on("mouseup", this.onMouseUp, this);
28434         
28435         this.fireEvent("show", this);
28436         
28437         
28438     },
28439     
28440     hide : function()
28441     {
28442         this.fireEvent("beforehide", this);
28443         
28444         this.hidden = true;
28445         this.el.removeClass('open');
28446         
28447         Roo.get(document).un("mouseup", this.onMouseUp);
28448         
28449         this.fireEvent("hide", this);
28450     },
28451     
28452     onMouseUp : function()
28453     {
28454         this.hide();
28455     }
28456     
28457 });
28458
28459  
28460  /*
28461  * - LGPL
28462  *
28463  * menu item
28464  * 
28465  */
28466 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28467
28468 /**
28469  * @class Roo.bootstrap.menu.Item
28470  * @extends Roo.bootstrap.Component
28471  * Bootstrap MenuItem class
28472  * @cfg {Boolean} submenu (true | false) default false
28473  * @cfg {String} html text of the item
28474  * @cfg {String} href the link
28475  * @cfg {Boolean} disable (true | false) default false
28476  * @cfg {Boolean} preventDefault (true | false) default true
28477  * @cfg {String} icon Font awesome icon
28478  * @cfg {String} pos Submenu align to (left | right) default right 
28479  * 
28480  * 
28481  * @constructor
28482  * Create a new Item
28483  * @param {Object} config The config object
28484  */
28485
28486
28487 Roo.bootstrap.menu.Item = function(config){
28488     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28489     this.addEvents({
28490         /**
28491          * @event mouseover
28492          * Fires when the mouse is hovering over this menu
28493          * @param {Roo.bootstrap.menu.Item} this
28494          * @param {Roo.EventObject} e
28495          */
28496         mouseover : true,
28497         /**
28498          * @event mouseout
28499          * Fires when the mouse exits this menu
28500          * @param {Roo.bootstrap.menu.Item} this
28501          * @param {Roo.EventObject} e
28502          */
28503         mouseout : true,
28504         // raw events
28505         /**
28506          * @event click
28507          * The raw click event for the entire grid.
28508          * @param {Roo.EventObject} e
28509          */
28510         click : true
28511     });
28512 };
28513
28514 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28515     
28516     submenu : false,
28517     href : '',
28518     html : '',
28519     preventDefault: true,
28520     disable : false,
28521     icon : false,
28522     pos : 'right',
28523     
28524     getAutoCreate : function()
28525     {
28526         var text = [
28527             {
28528                 tag : 'span',
28529                 cls : 'roo-menu-item-text',
28530                 html : this.html
28531             }
28532         ];
28533         
28534         if(this.icon){
28535             text.unshift({
28536                 tag : 'i',
28537                 cls : 'fa ' + this.icon
28538             })
28539         }
28540         
28541         var cfg = {
28542             tag : 'li',
28543             cn : [
28544                 {
28545                     tag : 'a',
28546                     href : this.href || '#',
28547                     cn : text
28548                 }
28549             ]
28550         };
28551         
28552         if(this.disable){
28553             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28554         }
28555         
28556         if(this.submenu){
28557             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28558             
28559             if(this.pos == 'left'){
28560                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28561             }
28562         }
28563         
28564         return cfg;
28565     },
28566     
28567     initEvents : function() 
28568     {
28569         this.el.on('mouseover', this.onMouseOver, this);
28570         this.el.on('mouseout', this.onMouseOut, this);
28571         
28572         this.el.select('a', true).first().on('click', this.onClick, this);
28573         
28574     },
28575     
28576     onClick : function(e)
28577     {
28578         if(this.preventDefault){
28579             e.preventDefault();
28580         }
28581         
28582         this.fireEvent("click", this, e);
28583     },
28584     
28585     onMouseOver : function(e)
28586     {
28587         if(this.submenu && this.pos == 'left'){
28588             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28589         }
28590         
28591         this.fireEvent("mouseover", this, e);
28592     },
28593     
28594     onMouseOut : function(e)
28595     {
28596         this.fireEvent("mouseout", this, e);
28597     }
28598 });
28599
28600  
28601
28602  /*
28603  * - LGPL
28604  *
28605  * menu separator
28606  * 
28607  */
28608 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28609
28610 /**
28611  * @class Roo.bootstrap.menu.Separator
28612  * @extends Roo.bootstrap.Component
28613  * Bootstrap Separator class
28614  * 
28615  * @constructor
28616  * Create a new Separator
28617  * @param {Object} config The config object
28618  */
28619
28620
28621 Roo.bootstrap.menu.Separator = function(config){
28622     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28623 };
28624
28625 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28626     
28627     getAutoCreate : function(){
28628         var cfg = {
28629             tag : 'li',
28630             cls: 'divider'
28631         };
28632         
28633         return cfg;
28634     }
28635    
28636 });
28637
28638  
28639
28640  /*
28641  * - LGPL
28642  *
28643  * Tooltip
28644  * 
28645  */
28646
28647 /**
28648  * @class Roo.bootstrap.Tooltip
28649  * Bootstrap Tooltip class
28650  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28651  * to determine which dom element triggers the tooltip.
28652  * 
28653  * It needs to add support for additional attributes like tooltip-position
28654  * 
28655  * @constructor
28656  * Create a new Toolti
28657  * @param {Object} config The config object
28658  */
28659
28660 Roo.bootstrap.Tooltip = function(config){
28661     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28662     
28663     this.alignment = Roo.bootstrap.Tooltip.alignment;
28664     
28665     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28666         this.alignment = config.alignment;
28667     }
28668     
28669 };
28670
28671 Roo.apply(Roo.bootstrap.Tooltip, {
28672     /**
28673      * @function init initialize tooltip monitoring.
28674      * @static
28675      */
28676     currentEl : false,
28677     currentTip : false,
28678     currentRegion : false,
28679     
28680     //  init : delay?
28681     
28682     init : function()
28683     {
28684         Roo.get(document).on('mouseover', this.enter ,this);
28685         Roo.get(document).on('mouseout', this.leave, this);
28686          
28687         
28688         this.currentTip = new Roo.bootstrap.Tooltip();
28689     },
28690     
28691     enter : function(ev)
28692     {
28693         var dom = ev.getTarget();
28694         
28695         //Roo.log(['enter',dom]);
28696         var el = Roo.fly(dom);
28697         if (this.currentEl) {
28698             //Roo.log(dom);
28699             //Roo.log(this.currentEl);
28700             //Roo.log(this.currentEl.contains(dom));
28701             if (this.currentEl == el) {
28702                 return;
28703             }
28704             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28705                 return;
28706             }
28707
28708         }
28709         
28710         if (this.currentTip.el) {
28711             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28712         }    
28713         //Roo.log(ev);
28714         
28715         if(!el || el.dom == document){
28716             return;
28717         }
28718         
28719         var bindEl = el;
28720         
28721         // you can not look for children, as if el is the body.. then everythign is the child..
28722         if (!el.attr('tooltip')) { //
28723             if (!el.select("[tooltip]").elements.length) {
28724                 return;
28725             }
28726             // is the mouse over this child...?
28727             bindEl = el.select("[tooltip]").first();
28728             var xy = ev.getXY();
28729             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28730                 //Roo.log("not in region.");
28731                 return;
28732             }
28733             //Roo.log("child element over..");
28734             
28735         }
28736         this.currentEl = bindEl;
28737         this.currentTip.bind(bindEl);
28738         this.currentRegion = Roo.lib.Region.getRegion(dom);
28739         this.currentTip.enter();
28740         
28741     },
28742     leave : function(ev)
28743     {
28744         var dom = ev.getTarget();
28745         //Roo.log(['leave',dom]);
28746         if (!this.currentEl) {
28747             return;
28748         }
28749         
28750         
28751         if (dom != this.currentEl.dom) {
28752             return;
28753         }
28754         var xy = ev.getXY();
28755         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28756             return;
28757         }
28758         // only activate leave if mouse cursor is outside... bounding box..
28759         
28760         
28761         
28762         
28763         if (this.currentTip) {
28764             this.currentTip.leave();
28765         }
28766         //Roo.log('clear currentEl');
28767         this.currentEl = false;
28768         
28769         
28770     },
28771     alignment : {
28772         'left' : ['r-l', [-2,0], 'right'],
28773         'right' : ['l-r', [2,0], 'left'],
28774         'bottom' : ['t-b', [0,2], 'top'],
28775         'top' : [ 'b-t', [0,-2], 'bottom']
28776     }
28777     
28778 });
28779
28780
28781 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28782     
28783     
28784     bindEl : false,
28785     
28786     delay : null, // can be { show : 300 , hide: 500}
28787     
28788     timeout : null,
28789     
28790     hoverState : null, //???
28791     
28792     placement : 'bottom', 
28793     
28794     alignment : false,
28795     
28796     getAutoCreate : function(){
28797     
28798         var cfg = {
28799            cls : 'tooltip',   
28800            role : 'tooltip',
28801            cn : [
28802                 {
28803                     cls : 'tooltip-arrow arrow'
28804                 },
28805                 {
28806                     cls : 'tooltip-inner'
28807                 }
28808            ]
28809         };
28810         
28811         return cfg;
28812     },
28813     bind : function(el)
28814     {
28815         this.bindEl = el;
28816     },
28817     
28818     initEvents : function()
28819     {
28820         this.arrowEl = this.el.select('.arrow', true).first();
28821         this.innerEl = this.el.select('.tooltip-inner', true).first();
28822     },
28823     
28824     enter : function () {
28825        
28826         if (this.timeout != null) {
28827             clearTimeout(this.timeout);
28828         }
28829         
28830         this.hoverState = 'in';
28831          //Roo.log("enter - show");
28832         if (!this.delay || !this.delay.show) {
28833             this.show();
28834             return;
28835         }
28836         var _t = this;
28837         this.timeout = setTimeout(function () {
28838             if (_t.hoverState == 'in') {
28839                 _t.show();
28840             }
28841         }, this.delay.show);
28842     },
28843     leave : function()
28844     {
28845         clearTimeout(this.timeout);
28846     
28847         this.hoverState = 'out';
28848          if (!this.delay || !this.delay.hide) {
28849             this.hide();
28850             return;
28851         }
28852        
28853         var _t = this;
28854         this.timeout = setTimeout(function () {
28855             //Roo.log("leave - timeout");
28856             
28857             if (_t.hoverState == 'out') {
28858                 _t.hide();
28859                 Roo.bootstrap.Tooltip.currentEl = false;
28860             }
28861         }, delay);
28862     },
28863     
28864     show : function (msg)
28865     {
28866         if (!this.el) {
28867             this.render(document.body);
28868         }
28869         // set content.
28870         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28871         
28872         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28873         
28874         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28875         
28876         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28877                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28878         
28879         var placement = typeof this.placement == 'function' ?
28880             this.placement.call(this, this.el, on_el) :
28881             this.placement;
28882             
28883         var autoToken = /\s?auto?\s?/i;
28884         var autoPlace = autoToken.test(placement);
28885         if (autoPlace) {
28886             placement = placement.replace(autoToken, '') || 'top';
28887         }
28888         
28889         //this.el.detach()
28890         //this.el.setXY([0,0]);
28891         this.el.show();
28892         //this.el.dom.style.display='block';
28893         
28894         //this.el.appendTo(on_el);
28895         
28896         var p = this.getPosition();
28897         var box = this.el.getBox();
28898         
28899         if (autoPlace) {
28900             // fixme..
28901         }
28902         
28903         var align = this.alignment[placement];
28904         
28905         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28906         
28907         if(placement == 'top' || placement == 'bottom'){
28908             if(xy[0] < 0){
28909                 placement = 'right';
28910             }
28911             
28912             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28913                 placement = 'left';
28914             }
28915             
28916             var scroll = Roo.select('body', true).first().getScroll();
28917             
28918             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28919                 placement = 'top';
28920             }
28921             
28922             align = this.alignment[placement];
28923             
28924             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28925             
28926         }
28927         
28928         this.el.alignTo(this.bindEl, align[0],align[1]);
28929         //var arrow = this.el.select('.arrow',true).first();
28930         //arrow.set(align[2], 
28931         
28932         this.el.addClass(placement);
28933         this.el.addClass("bs-tooltip-"+ placement);
28934         
28935         this.el.addClass('in fade show');
28936         
28937         this.hoverState = null;
28938         
28939         if (this.el.hasClass('fade')) {
28940             // fade it?
28941         }
28942         
28943         
28944         
28945         
28946         
28947     },
28948     hide : function()
28949     {
28950          
28951         if (!this.el) {
28952             return;
28953         }
28954         //this.el.setXY([0,0]);
28955         this.el.removeClass(['show', 'in']);
28956         //this.el.hide();
28957         
28958     }
28959     
28960 });
28961  
28962
28963  /*
28964  * - LGPL
28965  *
28966  * Location Picker
28967  * 
28968  */
28969
28970 /**
28971  * @class Roo.bootstrap.LocationPicker
28972  * @extends Roo.bootstrap.Component
28973  * Bootstrap LocationPicker class
28974  * @cfg {Number} latitude Position when init default 0
28975  * @cfg {Number} longitude Position when init default 0
28976  * @cfg {Number} zoom default 15
28977  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28978  * @cfg {Boolean} mapTypeControl default false
28979  * @cfg {Boolean} disableDoubleClickZoom default false
28980  * @cfg {Boolean} scrollwheel default true
28981  * @cfg {Boolean} streetViewControl default false
28982  * @cfg {Number} radius default 0
28983  * @cfg {String} locationName
28984  * @cfg {Boolean} draggable default true
28985  * @cfg {Boolean} enableAutocomplete default false
28986  * @cfg {Boolean} enableReverseGeocode default true
28987  * @cfg {String} markerTitle
28988  * 
28989  * @constructor
28990  * Create a new LocationPicker
28991  * @param {Object} config The config object
28992  */
28993
28994
28995 Roo.bootstrap.LocationPicker = function(config){
28996     
28997     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28998     
28999     this.addEvents({
29000         /**
29001          * @event initial
29002          * Fires when the picker initialized.
29003          * @param {Roo.bootstrap.LocationPicker} this
29004          * @param {Google Location} location
29005          */
29006         initial : true,
29007         /**
29008          * @event positionchanged
29009          * Fires when the picker position changed.
29010          * @param {Roo.bootstrap.LocationPicker} this
29011          * @param {Google Location} location
29012          */
29013         positionchanged : true,
29014         /**
29015          * @event resize
29016          * Fires when the map resize.
29017          * @param {Roo.bootstrap.LocationPicker} this
29018          */
29019         resize : true,
29020         /**
29021          * @event show
29022          * Fires when the map show.
29023          * @param {Roo.bootstrap.LocationPicker} this
29024          */
29025         show : true,
29026         /**
29027          * @event hide
29028          * Fires when the map hide.
29029          * @param {Roo.bootstrap.LocationPicker} this
29030          */
29031         hide : true,
29032         /**
29033          * @event mapClick
29034          * Fires when click the map.
29035          * @param {Roo.bootstrap.LocationPicker} this
29036          * @param {Map event} e
29037          */
29038         mapClick : true,
29039         /**
29040          * @event mapRightClick
29041          * Fires when right click the map.
29042          * @param {Roo.bootstrap.LocationPicker} this
29043          * @param {Map event} e
29044          */
29045         mapRightClick : true,
29046         /**
29047          * @event markerClick
29048          * Fires when click the marker.
29049          * @param {Roo.bootstrap.LocationPicker} this
29050          * @param {Map event} e
29051          */
29052         markerClick : true,
29053         /**
29054          * @event markerRightClick
29055          * Fires when right click the marker.
29056          * @param {Roo.bootstrap.LocationPicker} this
29057          * @param {Map event} e
29058          */
29059         markerRightClick : true,
29060         /**
29061          * @event OverlayViewDraw
29062          * Fires when OverlayView Draw
29063          * @param {Roo.bootstrap.LocationPicker} this
29064          */
29065         OverlayViewDraw : true,
29066         /**
29067          * @event OverlayViewOnAdd
29068          * Fires when OverlayView Draw
29069          * @param {Roo.bootstrap.LocationPicker} this
29070          */
29071         OverlayViewOnAdd : true,
29072         /**
29073          * @event OverlayViewOnRemove
29074          * Fires when OverlayView Draw
29075          * @param {Roo.bootstrap.LocationPicker} this
29076          */
29077         OverlayViewOnRemove : true,
29078         /**
29079          * @event OverlayViewShow
29080          * Fires when OverlayView Draw
29081          * @param {Roo.bootstrap.LocationPicker} this
29082          * @param {Pixel} cpx
29083          */
29084         OverlayViewShow : true,
29085         /**
29086          * @event OverlayViewHide
29087          * Fires when OverlayView Draw
29088          * @param {Roo.bootstrap.LocationPicker} this
29089          */
29090         OverlayViewHide : true,
29091         /**
29092          * @event loadexception
29093          * Fires when load google lib failed.
29094          * @param {Roo.bootstrap.LocationPicker} this
29095          */
29096         loadexception : true
29097     });
29098         
29099 };
29100
29101 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29102     
29103     gMapContext: false,
29104     
29105     latitude: 0,
29106     longitude: 0,
29107     zoom: 15,
29108     mapTypeId: false,
29109     mapTypeControl: false,
29110     disableDoubleClickZoom: false,
29111     scrollwheel: true,
29112     streetViewControl: false,
29113     radius: 0,
29114     locationName: '',
29115     draggable: true,
29116     enableAutocomplete: false,
29117     enableReverseGeocode: true,
29118     markerTitle: '',
29119     
29120     getAutoCreate: function()
29121     {
29122
29123         var cfg = {
29124             tag: 'div',
29125             cls: 'roo-location-picker'
29126         };
29127         
29128         return cfg
29129     },
29130     
29131     initEvents: function(ct, position)
29132     {       
29133         if(!this.el.getWidth() || this.isApplied()){
29134             return;
29135         }
29136         
29137         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29138         
29139         this.initial();
29140     },
29141     
29142     initial: function()
29143     {
29144         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29145             this.fireEvent('loadexception', this);
29146             return;
29147         }
29148         
29149         if(!this.mapTypeId){
29150             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29151         }
29152         
29153         this.gMapContext = this.GMapContext();
29154         
29155         this.initOverlayView();
29156         
29157         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29158         
29159         var _this = this;
29160                 
29161         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29162             _this.setPosition(_this.gMapContext.marker.position);
29163         });
29164         
29165         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29166             _this.fireEvent('mapClick', this, event);
29167             
29168         });
29169
29170         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29171             _this.fireEvent('mapRightClick', this, event);
29172             
29173         });
29174         
29175         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29176             _this.fireEvent('markerClick', this, event);
29177             
29178         });
29179
29180         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29181             _this.fireEvent('markerRightClick', this, event);
29182             
29183         });
29184         
29185         this.setPosition(this.gMapContext.location);
29186         
29187         this.fireEvent('initial', this, this.gMapContext.location);
29188     },
29189     
29190     initOverlayView: function()
29191     {
29192         var _this = this;
29193         
29194         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29195             
29196             draw: function()
29197             {
29198                 _this.fireEvent('OverlayViewDraw', _this);
29199             },
29200             
29201             onAdd: function()
29202             {
29203                 _this.fireEvent('OverlayViewOnAdd', _this);
29204             },
29205             
29206             onRemove: function()
29207             {
29208                 _this.fireEvent('OverlayViewOnRemove', _this);
29209             },
29210             
29211             show: function(cpx)
29212             {
29213                 _this.fireEvent('OverlayViewShow', _this, cpx);
29214             },
29215             
29216             hide: function()
29217             {
29218                 _this.fireEvent('OverlayViewHide', _this);
29219             }
29220             
29221         });
29222     },
29223     
29224     fromLatLngToContainerPixel: function(event)
29225     {
29226         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29227     },
29228     
29229     isApplied: function() 
29230     {
29231         return this.getGmapContext() == false ? false : true;
29232     },
29233     
29234     getGmapContext: function() 
29235     {
29236         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29237     },
29238     
29239     GMapContext: function() 
29240     {
29241         var position = new google.maps.LatLng(this.latitude, this.longitude);
29242         
29243         var _map = new google.maps.Map(this.el.dom, {
29244             center: position,
29245             zoom: this.zoom,
29246             mapTypeId: this.mapTypeId,
29247             mapTypeControl: this.mapTypeControl,
29248             disableDoubleClickZoom: this.disableDoubleClickZoom,
29249             scrollwheel: this.scrollwheel,
29250             streetViewControl: this.streetViewControl,
29251             locationName: this.locationName,
29252             draggable: this.draggable,
29253             enableAutocomplete: this.enableAutocomplete,
29254             enableReverseGeocode: this.enableReverseGeocode
29255         });
29256         
29257         var _marker = new google.maps.Marker({
29258             position: position,
29259             map: _map,
29260             title: this.markerTitle,
29261             draggable: this.draggable
29262         });
29263         
29264         return {
29265             map: _map,
29266             marker: _marker,
29267             circle: null,
29268             location: position,
29269             radius: this.radius,
29270             locationName: this.locationName,
29271             addressComponents: {
29272                 formatted_address: null,
29273                 addressLine1: null,
29274                 addressLine2: null,
29275                 streetName: null,
29276                 streetNumber: null,
29277                 city: null,
29278                 district: null,
29279                 state: null,
29280                 stateOrProvince: null
29281             },
29282             settings: this,
29283             domContainer: this.el.dom,
29284             geodecoder: new google.maps.Geocoder()
29285         };
29286     },
29287     
29288     drawCircle: function(center, radius, options) 
29289     {
29290         if (this.gMapContext.circle != null) {
29291             this.gMapContext.circle.setMap(null);
29292         }
29293         if (radius > 0) {
29294             radius *= 1;
29295             options = Roo.apply({}, options, {
29296                 strokeColor: "#0000FF",
29297                 strokeOpacity: .35,
29298                 strokeWeight: 2,
29299                 fillColor: "#0000FF",
29300                 fillOpacity: .2
29301             });
29302             
29303             options.map = this.gMapContext.map;
29304             options.radius = radius;
29305             options.center = center;
29306             this.gMapContext.circle = new google.maps.Circle(options);
29307             return this.gMapContext.circle;
29308         }
29309         
29310         return null;
29311     },
29312     
29313     setPosition: function(location) 
29314     {
29315         this.gMapContext.location = location;
29316         this.gMapContext.marker.setPosition(location);
29317         this.gMapContext.map.panTo(location);
29318         this.drawCircle(location, this.gMapContext.radius, {});
29319         
29320         var _this = this;
29321         
29322         if (this.gMapContext.settings.enableReverseGeocode) {
29323             this.gMapContext.geodecoder.geocode({
29324                 latLng: this.gMapContext.location
29325             }, function(results, status) {
29326                 
29327                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29328                     _this.gMapContext.locationName = results[0].formatted_address;
29329                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29330                     
29331                     _this.fireEvent('positionchanged', this, location);
29332                 }
29333             });
29334             
29335             return;
29336         }
29337         
29338         this.fireEvent('positionchanged', this, location);
29339     },
29340     
29341     resize: function()
29342     {
29343         google.maps.event.trigger(this.gMapContext.map, "resize");
29344         
29345         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29346         
29347         this.fireEvent('resize', this);
29348     },
29349     
29350     setPositionByLatLng: function(latitude, longitude)
29351     {
29352         this.setPosition(new google.maps.LatLng(latitude, longitude));
29353     },
29354     
29355     getCurrentPosition: function() 
29356     {
29357         return {
29358             latitude: this.gMapContext.location.lat(),
29359             longitude: this.gMapContext.location.lng()
29360         };
29361     },
29362     
29363     getAddressName: function() 
29364     {
29365         return this.gMapContext.locationName;
29366     },
29367     
29368     getAddressComponents: function() 
29369     {
29370         return this.gMapContext.addressComponents;
29371     },
29372     
29373     address_component_from_google_geocode: function(address_components) 
29374     {
29375         var result = {};
29376         
29377         for (var i = 0; i < address_components.length; i++) {
29378             var component = address_components[i];
29379             if (component.types.indexOf("postal_code") >= 0) {
29380                 result.postalCode = component.short_name;
29381             } else if (component.types.indexOf("street_number") >= 0) {
29382                 result.streetNumber = component.short_name;
29383             } else if (component.types.indexOf("route") >= 0) {
29384                 result.streetName = component.short_name;
29385             } else if (component.types.indexOf("neighborhood") >= 0) {
29386                 result.city = component.short_name;
29387             } else if (component.types.indexOf("locality") >= 0) {
29388                 result.city = component.short_name;
29389             } else if (component.types.indexOf("sublocality") >= 0) {
29390                 result.district = component.short_name;
29391             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29392                 result.stateOrProvince = component.short_name;
29393             } else if (component.types.indexOf("country") >= 0) {
29394                 result.country = component.short_name;
29395             }
29396         }
29397         
29398         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29399         result.addressLine2 = "";
29400         return result;
29401     },
29402     
29403     setZoomLevel: function(zoom)
29404     {
29405         this.gMapContext.map.setZoom(zoom);
29406     },
29407     
29408     show: function()
29409     {
29410         if(!this.el){
29411             return;
29412         }
29413         
29414         this.el.show();
29415         
29416         this.resize();
29417         
29418         this.fireEvent('show', this);
29419     },
29420     
29421     hide: function()
29422     {
29423         if(!this.el){
29424             return;
29425         }
29426         
29427         this.el.hide();
29428         
29429         this.fireEvent('hide', this);
29430     }
29431     
29432 });
29433
29434 Roo.apply(Roo.bootstrap.LocationPicker, {
29435     
29436     OverlayView : function(map, options)
29437     {
29438         options = options || {};
29439         
29440         this.setMap(map);
29441     }
29442     
29443     
29444 });/**
29445  * @class Roo.bootstrap.Alert
29446  * @extends Roo.bootstrap.Component
29447  * Bootstrap Alert class - shows an alert area box
29448  * eg
29449  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29450   Enter a valid email address
29451 </div>
29452  * @licence LGPL
29453  * @cfg {String} title The title of alert
29454  * @cfg {String} html The content of alert
29455  * @cfg {String} weight (  success | info | warning | danger )
29456  * @cfg {String} faicon font-awesomeicon
29457  * 
29458  * @constructor
29459  * Create a new alert
29460  * @param {Object} config The config object
29461  */
29462
29463
29464 Roo.bootstrap.Alert = function(config){
29465     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29466     
29467 };
29468
29469 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29470     
29471     title: '',
29472     html: '',
29473     weight: false,
29474     faicon: false,
29475     
29476     getAutoCreate : function()
29477     {
29478         
29479         var cfg = {
29480             tag : 'div',
29481             cls : 'alert',
29482             cn : [
29483                 {
29484                     tag : 'i',
29485                     cls : 'roo-alert-icon'
29486                     
29487                 },
29488                 {
29489                     tag : 'b',
29490                     cls : 'roo-alert-title',
29491                     html : this.title
29492                 },
29493                 {
29494                     tag : 'span',
29495                     cls : 'roo-alert-text',
29496                     html : this.html
29497                 }
29498             ]
29499         };
29500         
29501         if(this.faicon){
29502             cfg.cn[0].cls += ' fa ' + this.faicon;
29503         }
29504         
29505         if(this.weight){
29506             cfg.cls += ' alert-' + this.weight;
29507         }
29508         
29509         return cfg;
29510     },
29511     
29512     initEvents: function() 
29513     {
29514         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29515     },
29516     
29517     setTitle : function(str)
29518     {
29519         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29520     },
29521     
29522     setText : function(str)
29523     {
29524         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29525     },
29526     
29527     setWeight : function(weight)
29528     {
29529         if(this.weight){
29530             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29531         }
29532         
29533         this.weight = weight;
29534         
29535         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29536     },
29537     
29538     setIcon : function(icon)
29539     {
29540         if(this.faicon){
29541             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29542         }
29543         
29544         this.faicon = icon;
29545         
29546         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29547     },
29548     
29549     hide: function() 
29550     {
29551         this.el.hide();   
29552     },
29553     
29554     show: function() 
29555     {  
29556         this.el.show();   
29557     }
29558     
29559 });
29560
29561  
29562 /*
29563 * Licence: LGPL
29564 */
29565
29566 /**
29567  * @class Roo.bootstrap.UploadCropbox
29568  * @extends Roo.bootstrap.Component
29569  * Bootstrap UploadCropbox class
29570  * @cfg {String} emptyText show when image has been loaded
29571  * @cfg {String} rotateNotify show when image too small to rotate
29572  * @cfg {Number} errorTimeout default 3000
29573  * @cfg {Number} minWidth default 300
29574  * @cfg {Number} minHeight default 300
29575  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29576  * @cfg {Boolean} isDocument (true|false) default false
29577  * @cfg {String} url action url
29578  * @cfg {String} paramName default 'imageUpload'
29579  * @cfg {String} method default POST
29580  * @cfg {Boolean} loadMask (true|false) default true
29581  * @cfg {Boolean} loadingText default 'Loading...'
29582  * 
29583  * @constructor
29584  * Create a new UploadCropbox
29585  * @param {Object} config The config object
29586  */
29587
29588 Roo.bootstrap.UploadCropbox = function(config){
29589     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29590     
29591     this.addEvents({
29592         /**
29593          * @event beforeselectfile
29594          * Fire before select file
29595          * @param {Roo.bootstrap.UploadCropbox} this
29596          */
29597         "beforeselectfile" : true,
29598         /**
29599          * @event initial
29600          * Fire after initEvent
29601          * @param {Roo.bootstrap.UploadCropbox} this
29602          */
29603         "initial" : true,
29604         /**
29605          * @event crop
29606          * Fire after initEvent
29607          * @param {Roo.bootstrap.UploadCropbox} this
29608          * @param {String} data
29609          */
29610         "crop" : true,
29611         /**
29612          * @event prepare
29613          * Fire when preparing the file data
29614          * @param {Roo.bootstrap.UploadCropbox} this
29615          * @param {Object} file
29616          */
29617         "prepare" : true,
29618         /**
29619          * @event exception
29620          * Fire when get exception
29621          * @param {Roo.bootstrap.UploadCropbox} this
29622          * @param {XMLHttpRequest} xhr
29623          */
29624         "exception" : true,
29625         /**
29626          * @event beforeloadcanvas
29627          * Fire before load the canvas
29628          * @param {Roo.bootstrap.UploadCropbox} this
29629          * @param {String} src
29630          */
29631         "beforeloadcanvas" : true,
29632         /**
29633          * @event trash
29634          * Fire when trash image
29635          * @param {Roo.bootstrap.UploadCropbox} this
29636          */
29637         "trash" : true,
29638         /**
29639          * @event download
29640          * Fire when download the image
29641          * @param {Roo.bootstrap.UploadCropbox} this
29642          */
29643         "download" : true,
29644         /**
29645          * @event footerbuttonclick
29646          * Fire when footerbuttonclick
29647          * @param {Roo.bootstrap.UploadCropbox} this
29648          * @param {String} type
29649          */
29650         "footerbuttonclick" : true,
29651         /**
29652          * @event resize
29653          * Fire when resize
29654          * @param {Roo.bootstrap.UploadCropbox} this
29655          */
29656         "resize" : true,
29657         /**
29658          * @event rotate
29659          * Fire when rotate the image
29660          * @param {Roo.bootstrap.UploadCropbox} this
29661          * @param {String} pos
29662          */
29663         "rotate" : true,
29664         /**
29665          * @event inspect
29666          * Fire when inspect the file
29667          * @param {Roo.bootstrap.UploadCropbox} this
29668          * @param {Object} file
29669          */
29670         "inspect" : true,
29671         /**
29672          * @event upload
29673          * Fire when xhr upload the file
29674          * @param {Roo.bootstrap.UploadCropbox} this
29675          * @param {Object} data
29676          */
29677         "upload" : true,
29678         /**
29679          * @event arrange
29680          * Fire when arrange the file data
29681          * @param {Roo.bootstrap.UploadCropbox} this
29682          * @param {Object} formData
29683          */
29684         "arrange" : true
29685     });
29686     
29687     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29688 };
29689
29690 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29691     
29692     emptyText : 'Click to upload image',
29693     rotateNotify : 'Image is too small to rotate',
29694     errorTimeout : 3000,
29695     scale : 0,
29696     baseScale : 1,
29697     rotate : 0,
29698     dragable : false,
29699     pinching : false,
29700     mouseX : 0,
29701     mouseY : 0,
29702     cropData : false,
29703     minWidth : 300,
29704     minHeight : 300,
29705     file : false,
29706     exif : {},
29707     baseRotate : 1,
29708     cropType : 'image/jpeg',
29709     buttons : false,
29710     canvasLoaded : false,
29711     isDocument : false,
29712     method : 'POST',
29713     paramName : 'imageUpload',
29714     loadMask : true,
29715     loadingText : 'Loading...',
29716     maskEl : false,
29717     
29718     getAutoCreate : function()
29719     {
29720         var cfg = {
29721             tag : 'div',
29722             cls : 'roo-upload-cropbox',
29723             cn : [
29724                 {
29725                     tag : 'input',
29726                     cls : 'roo-upload-cropbox-selector',
29727                     type : 'file'
29728                 },
29729                 {
29730                     tag : 'div',
29731                     cls : 'roo-upload-cropbox-body',
29732                     style : 'cursor:pointer',
29733                     cn : [
29734                         {
29735                             tag : 'div',
29736                             cls : 'roo-upload-cropbox-preview'
29737                         },
29738                         {
29739                             tag : 'div',
29740                             cls : 'roo-upload-cropbox-thumb'
29741                         },
29742                         {
29743                             tag : 'div',
29744                             cls : 'roo-upload-cropbox-empty-notify',
29745                             html : this.emptyText
29746                         },
29747                         {
29748                             tag : 'div',
29749                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29750                             html : this.rotateNotify
29751                         }
29752                     ]
29753                 },
29754                 {
29755                     tag : 'div',
29756                     cls : 'roo-upload-cropbox-footer',
29757                     cn : {
29758                         tag : 'div',
29759                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29760                         cn : []
29761                     }
29762                 }
29763             ]
29764         };
29765         
29766         return cfg;
29767     },
29768     
29769     onRender : function(ct, position)
29770     {
29771         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29772         
29773         if (this.buttons.length) {
29774             
29775             Roo.each(this.buttons, function(bb) {
29776                 
29777                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29778                 
29779                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29780                 
29781             }, this);
29782         }
29783         
29784         if(this.loadMask){
29785             this.maskEl = this.el;
29786         }
29787     },
29788     
29789     initEvents : function()
29790     {
29791         this.urlAPI = (window.createObjectURL && window) || 
29792                                 (window.URL && URL.revokeObjectURL && URL) || 
29793                                 (window.webkitURL && webkitURL);
29794                         
29795         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29796         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29797         
29798         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29799         this.selectorEl.hide();
29800         
29801         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29802         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29803         
29804         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29805         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29806         this.thumbEl.hide();
29807         
29808         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29809         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29810         
29811         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29812         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29813         this.errorEl.hide();
29814         
29815         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29816         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29817         this.footerEl.hide();
29818         
29819         this.setThumbBoxSize();
29820         
29821         this.bind();
29822         
29823         this.resize();
29824         
29825         this.fireEvent('initial', this);
29826     },
29827
29828     bind : function()
29829     {
29830         var _this = this;
29831         
29832         window.addEventListener("resize", function() { _this.resize(); } );
29833         
29834         this.bodyEl.on('click', this.beforeSelectFile, this);
29835         
29836         if(Roo.isTouch){
29837             this.bodyEl.on('touchstart', this.onTouchStart, this);
29838             this.bodyEl.on('touchmove', this.onTouchMove, this);
29839             this.bodyEl.on('touchend', this.onTouchEnd, this);
29840         }
29841         
29842         if(!Roo.isTouch){
29843             this.bodyEl.on('mousedown', this.onMouseDown, this);
29844             this.bodyEl.on('mousemove', this.onMouseMove, this);
29845             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29846             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29847             Roo.get(document).on('mouseup', this.onMouseUp, this);
29848         }
29849         
29850         this.selectorEl.on('change', this.onFileSelected, this);
29851     },
29852     
29853     reset : function()
29854     {    
29855         this.scale = 0;
29856         this.baseScale = 1;
29857         this.rotate = 0;
29858         this.baseRotate = 1;
29859         this.dragable = false;
29860         this.pinching = false;
29861         this.mouseX = 0;
29862         this.mouseY = 0;
29863         this.cropData = false;
29864         this.notifyEl.dom.innerHTML = this.emptyText;
29865         
29866         this.selectorEl.dom.value = '';
29867         
29868     },
29869     
29870     resize : function()
29871     {
29872         if(this.fireEvent('resize', this) != false){
29873             this.setThumbBoxPosition();
29874             this.setCanvasPosition();
29875         }
29876     },
29877     
29878     onFooterButtonClick : function(e, el, o, type)
29879     {
29880         switch (type) {
29881             case 'rotate-left' :
29882                 this.onRotateLeft(e);
29883                 break;
29884             case 'rotate-right' :
29885                 this.onRotateRight(e);
29886                 break;
29887             case 'picture' :
29888                 this.beforeSelectFile(e);
29889                 break;
29890             case 'trash' :
29891                 this.trash(e);
29892                 break;
29893             case 'crop' :
29894                 this.crop(e);
29895                 break;
29896             case 'download' :
29897                 this.download(e);
29898                 break;
29899             default :
29900                 break;
29901         }
29902         
29903         this.fireEvent('footerbuttonclick', this, type);
29904     },
29905     
29906     beforeSelectFile : function(e)
29907     {
29908         e.preventDefault();
29909         
29910         if(this.fireEvent('beforeselectfile', this) != false){
29911             this.selectorEl.dom.click();
29912         }
29913     },
29914     
29915     onFileSelected : function(e)
29916     {
29917         e.preventDefault();
29918         
29919         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29920             return;
29921         }
29922         
29923         var file = this.selectorEl.dom.files[0];
29924         
29925         if(this.fireEvent('inspect', this, file) != false){
29926             this.prepare(file);
29927         }
29928         
29929     },
29930     
29931     trash : function(e)
29932     {
29933         this.fireEvent('trash', this);
29934     },
29935     
29936     download : function(e)
29937     {
29938         this.fireEvent('download', this);
29939     },
29940     
29941     loadCanvas : function(src)
29942     {   
29943         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29944             
29945             this.reset();
29946             
29947             this.imageEl = document.createElement('img');
29948             
29949             var _this = this;
29950             
29951             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29952             
29953             this.imageEl.src = src;
29954         }
29955     },
29956     
29957     onLoadCanvas : function()
29958     {   
29959         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29960         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29961         
29962         this.bodyEl.un('click', this.beforeSelectFile, this);
29963         
29964         this.notifyEl.hide();
29965         this.thumbEl.show();
29966         this.footerEl.show();
29967         
29968         this.baseRotateLevel();
29969         
29970         if(this.isDocument){
29971             this.setThumbBoxSize();
29972         }
29973         
29974         this.setThumbBoxPosition();
29975         
29976         this.baseScaleLevel();
29977         
29978         this.draw();
29979         
29980         this.resize();
29981         
29982         this.canvasLoaded = true;
29983         
29984         if(this.loadMask){
29985             this.maskEl.unmask();
29986         }
29987         
29988     },
29989     
29990     setCanvasPosition : function()
29991     {   
29992         if(!this.canvasEl){
29993             return;
29994         }
29995         
29996         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29997         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29998         
29999         this.previewEl.setLeft(pw);
30000         this.previewEl.setTop(ph);
30001         
30002     },
30003     
30004     onMouseDown : function(e)
30005     {   
30006         e.stopEvent();
30007         
30008         this.dragable = true;
30009         this.pinching = false;
30010         
30011         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30012             this.dragable = false;
30013             return;
30014         }
30015         
30016         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30017         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30018         
30019     },
30020     
30021     onMouseMove : function(e)
30022     {   
30023         e.stopEvent();
30024         
30025         if(!this.canvasLoaded){
30026             return;
30027         }
30028         
30029         if (!this.dragable){
30030             return;
30031         }
30032         
30033         var minX = Math.ceil(this.thumbEl.getLeft(true));
30034         var minY = Math.ceil(this.thumbEl.getTop(true));
30035         
30036         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30037         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30038         
30039         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30040         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30041         
30042         x = x - this.mouseX;
30043         y = y - this.mouseY;
30044         
30045         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30046         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30047         
30048         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30049         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30050         
30051         this.previewEl.setLeft(bgX);
30052         this.previewEl.setTop(bgY);
30053         
30054         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30055         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30056     },
30057     
30058     onMouseUp : function(e)
30059     {   
30060         e.stopEvent();
30061         
30062         this.dragable = false;
30063     },
30064     
30065     onMouseWheel : function(e)
30066     {   
30067         e.stopEvent();
30068         
30069         this.startScale = this.scale;
30070         
30071         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30072         
30073         if(!this.zoomable()){
30074             this.scale = this.startScale;
30075             return;
30076         }
30077         
30078         this.draw();
30079         
30080         return;
30081     },
30082     
30083     zoomable : function()
30084     {
30085         var minScale = this.thumbEl.getWidth() / this.minWidth;
30086         
30087         if(this.minWidth < this.minHeight){
30088             minScale = this.thumbEl.getHeight() / this.minHeight;
30089         }
30090         
30091         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30092         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30093         
30094         if(
30095                 this.isDocument &&
30096                 (this.rotate == 0 || this.rotate == 180) && 
30097                 (
30098                     width > this.imageEl.OriginWidth || 
30099                     height > this.imageEl.OriginHeight ||
30100                     (width < this.minWidth && height < this.minHeight)
30101                 )
30102         ){
30103             return false;
30104         }
30105         
30106         if(
30107                 this.isDocument &&
30108                 (this.rotate == 90 || this.rotate == 270) && 
30109                 (
30110                     width > this.imageEl.OriginWidth || 
30111                     height > this.imageEl.OriginHeight ||
30112                     (width < this.minHeight && height < this.minWidth)
30113                 )
30114         ){
30115             return false;
30116         }
30117         
30118         if(
30119                 !this.isDocument &&
30120                 (this.rotate == 0 || this.rotate == 180) && 
30121                 (
30122                     width < this.minWidth || 
30123                     width > this.imageEl.OriginWidth || 
30124                     height < this.minHeight || 
30125                     height > this.imageEl.OriginHeight
30126                 )
30127         ){
30128             return false;
30129         }
30130         
30131         if(
30132                 !this.isDocument &&
30133                 (this.rotate == 90 || this.rotate == 270) && 
30134                 (
30135                     width < this.minHeight || 
30136                     width > this.imageEl.OriginWidth || 
30137                     height < this.minWidth || 
30138                     height > this.imageEl.OriginHeight
30139                 )
30140         ){
30141             return false;
30142         }
30143         
30144         return true;
30145         
30146     },
30147     
30148     onRotateLeft : function(e)
30149     {   
30150         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30151             
30152             var minScale = this.thumbEl.getWidth() / this.minWidth;
30153             
30154             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30155             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30156             
30157             this.startScale = this.scale;
30158             
30159             while (this.getScaleLevel() < minScale){
30160             
30161                 this.scale = this.scale + 1;
30162                 
30163                 if(!this.zoomable()){
30164                     break;
30165                 }
30166                 
30167                 if(
30168                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30169                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30170                 ){
30171                     continue;
30172                 }
30173                 
30174                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30175
30176                 this.draw();
30177                 
30178                 return;
30179             }
30180             
30181             this.scale = this.startScale;
30182             
30183             this.onRotateFail();
30184             
30185             return false;
30186         }
30187         
30188         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30189
30190         if(this.isDocument){
30191             this.setThumbBoxSize();
30192             this.setThumbBoxPosition();
30193             this.setCanvasPosition();
30194         }
30195         
30196         this.draw();
30197         
30198         this.fireEvent('rotate', this, 'left');
30199         
30200     },
30201     
30202     onRotateRight : function(e)
30203     {
30204         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30205             
30206             var minScale = this.thumbEl.getWidth() / this.minWidth;
30207         
30208             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30209             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30210             
30211             this.startScale = this.scale;
30212             
30213             while (this.getScaleLevel() < minScale){
30214             
30215                 this.scale = this.scale + 1;
30216                 
30217                 if(!this.zoomable()){
30218                     break;
30219                 }
30220                 
30221                 if(
30222                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30223                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30224                 ){
30225                     continue;
30226                 }
30227                 
30228                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30229
30230                 this.draw();
30231                 
30232                 return;
30233             }
30234             
30235             this.scale = this.startScale;
30236             
30237             this.onRotateFail();
30238             
30239             return false;
30240         }
30241         
30242         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30243
30244         if(this.isDocument){
30245             this.setThumbBoxSize();
30246             this.setThumbBoxPosition();
30247             this.setCanvasPosition();
30248         }
30249         
30250         this.draw();
30251         
30252         this.fireEvent('rotate', this, 'right');
30253     },
30254     
30255     onRotateFail : function()
30256     {
30257         this.errorEl.show(true);
30258         
30259         var _this = this;
30260         
30261         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30262     },
30263     
30264     draw : function()
30265     {
30266         this.previewEl.dom.innerHTML = '';
30267         
30268         var canvasEl = document.createElement("canvas");
30269         
30270         var contextEl = canvasEl.getContext("2d");
30271         
30272         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30273         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30274         var center = this.imageEl.OriginWidth / 2;
30275         
30276         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30277             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30278             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30279             center = this.imageEl.OriginHeight / 2;
30280         }
30281         
30282         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30283         
30284         contextEl.translate(center, center);
30285         contextEl.rotate(this.rotate * Math.PI / 180);
30286
30287         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30288         
30289         this.canvasEl = document.createElement("canvas");
30290         
30291         this.contextEl = this.canvasEl.getContext("2d");
30292         
30293         switch (this.rotate) {
30294             case 0 :
30295                 
30296                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30297                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30298                 
30299                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30300                 
30301                 break;
30302             case 90 : 
30303                 
30304                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30305                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30306                 
30307                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30308                     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);
30309                     break;
30310                 }
30311                 
30312                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30313                 
30314                 break;
30315             case 180 :
30316                 
30317                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30318                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30319                 
30320                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30321                     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);
30322                     break;
30323                 }
30324                 
30325                 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);
30326                 
30327                 break;
30328             case 270 :
30329                 
30330                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30331                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30332         
30333                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30334                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30335                     break;
30336                 }
30337                 
30338                 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);
30339                 
30340                 break;
30341             default : 
30342                 break;
30343         }
30344         
30345         this.previewEl.appendChild(this.canvasEl);
30346         
30347         this.setCanvasPosition();
30348     },
30349     
30350     crop : function()
30351     {
30352         if(!this.canvasLoaded){
30353             return;
30354         }
30355         
30356         var imageCanvas = document.createElement("canvas");
30357         
30358         var imageContext = imageCanvas.getContext("2d");
30359         
30360         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30361         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30362         
30363         var center = imageCanvas.width / 2;
30364         
30365         imageContext.translate(center, center);
30366         
30367         imageContext.rotate(this.rotate * Math.PI / 180);
30368         
30369         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30370         
30371         var canvas = document.createElement("canvas");
30372         
30373         var context = canvas.getContext("2d");
30374                 
30375         canvas.width = this.minWidth;
30376         canvas.height = this.minHeight;
30377
30378         switch (this.rotate) {
30379             case 0 :
30380                 
30381                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30382                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30383                 
30384                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30385                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30386                 
30387                 var targetWidth = this.minWidth - 2 * x;
30388                 var targetHeight = this.minHeight - 2 * y;
30389                 
30390                 var scale = 1;
30391                 
30392                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30393                     scale = targetWidth / width;
30394                 }
30395                 
30396                 if(x > 0 && y == 0){
30397                     scale = targetHeight / height;
30398                 }
30399                 
30400                 if(x > 0 && y > 0){
30401                     scale = targetWidth / width;
30402                     
30403                     if(width < height){
30404                         scale = targetHeight / height;
30405                     }
30406                 }
30407                 
30408                 context.scale(scale, scale);
30409                 
30410                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30411                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30412
30413                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30414                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30415
30416                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30417                 
30418                 break;
30419             case 90 : 
30420                 
30421                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30422                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30423                 
30424                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30425                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30426                 
30427                 var targetWidth = this.minWidth - 2 * x;
30428                 var targetHeight = this.minHeight - 2 * y;
30429                 
30430                 var scale = 1;
30431                 
30432                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30433                     scale = targetWidth / width;
30434                 }
30435                 
30436                 if(x > 0 && y == 0){
30437                     scale = targetHeight / height;
30438                 }
30439                 
30440                 if(x > 0 && y > 0){
30441                     scale = targetWidth / width;
30442                     
30443                     if(width < height){
30444                         scale = targetHeight / height;
30445                     }
30446                 }
30447                 
30448                 context.scale(scale, scale);
30449                 
30450                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30451                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30452
30453                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30454                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30455                 
30456                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30457                 
30458                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30459                 
30460                 break;
30461             case 180 :
30462                 
30463                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30464                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30465                 
30466                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30467                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30468                 
30469                 var targetWidth = this.minWidth - 2 * x;
30470                 var targetHeight = this.minHeight - 2 * y;
30471                 
30472                 var scale = 1;
30473                 
30474                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30475                     scale = targetWidth / width;
30476                 }
30477                 
30478                 if(x > 0 && y == 0){
30479                     scale = targetHeight / height;
30480                 }
30481                 
30482                 if(x > 0 && y > 0){
30483                     scale = targetWidth / width;
30484                     
30485                     if(width < height){
30486                         scale = targetHeight / height;
30487                     }
30488                 }
30489                 
30490                 context.scale(scale, scale);
30491                 
30492                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30493                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30494
30495                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30496                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30497
30498                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30499                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30500                 
30501                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30502                 
30503                 break;
30504             case 270 :
30505                 
30506                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30507                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30508                 
30509                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30510                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30511                 
30512                 var targetWidth = this.minWidth - 2 * x;
30513                 var targetHeight = this.minHeight - 2 * y;
30514                 
30515                 var scale = 1;
30516                 
30517                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30518                     scale = targetWidth / width;
30519                 }
30520                 
30521                 if(x > 0 && y == 0){
30522                     scale = targetHeight / height;
30523                 }
30524                 
30525                 if(x > 0 && y > 0){
30526                     scale = targetWidth / width;
30527                     
30528                     if(width < height){
30529                         scale = targetHeight / height;
30530                     }
30531                 }
30532                 
30533                 context.scale(scale, scale);
30534                 
30535                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30536                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30537
30538                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30539                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30540                 
30541                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30542                 
30543                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30544                 
30545                 break;
30546             default : 
30547                 break;
30548         }
30549         
30550         this.cropData = canvas.toDataURL(this.cropType);
30551         
30552         if(this.fireEvent('crop', this, this.cropData) !== false){
30553             this.process(this.file, this.cropData);
30554         }
30555         
30556         return;
30557         
30558     },
30559     
30560     setThumbBoxSize : function()
30561     {
30562         var width, height;
30563         
30564         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30565             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30566             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30567             
30568             this.minWidth = width;
30569             this.minHeight = height;
30570             
30571             if(this.rotate == 90 || this.rotate == 270){
30572                 this.minWidth = height;
30573                 this.minHeight = width;
30574             }
30575         }
30576         
30577         height = 300;
30578         width = Math.ceil(this.minWidth * height / this.minHeight);
30579         
30580         if(this.minWidth > this.minHeight){
30581             width = 300;
30582             height = Math.ceil(this.minHeight * width / this.minWidth);
30583         }
30584         
30585         this.thumbEl.setStyle({
30586             width : width + 'px',
30587             height : height + 'px'
30588         });
30589
30590         return;
30591             
30592     },
30593     
30594     setThumbBoxPosition : function()
30595     {
30596         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30597         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30598         
30599         this.thumbEl.setLeft(x);
30600         this.thumbEl.setTop(y);
30601         
30602     },
30603     
30604     baseRotateLevel : function()
30605     {
30606         this.baseRotate = 1;
30607         
30608         if(
30609                 typeof(this.exif) != 'undefined' &&
30610                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30611                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30612         ){
30613             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30614         }
30615         
30616         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30617         
30618     },
30619     
30620     baseScaleLevel : function()
30621     {
30622         var width, height;
30623         
30624         if(this.isDocument){
30625             
30626             if(this.baseRotate == 6 || this.baseRotate == 8){
30627             
30628                 height = this.thumbEl.getHeight();
30629                 this.baseScale = height / this.imageEl.OriginWidth;
30630
30631                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30632                     width = this.thumbEl.getWidth();
30633                     this.baseScale = width / this.imageEl.OriginHeight;
30634                 }
30635
30636                 return;
30637             }
30638
30639             height = this.thumbEl.getHeight();
30640             this.baseScale = height / this.imageEl.OriginHeight;
30641
30642             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30643                 width = this.thumbEl.getWidth();
30644                 this.baseScale = width / this.imageEl.OriginWidth;
30645             }
30646
30647             return;
30648         }
30649         
30650         if(this.baseRotate == 6 || this.baseRotate == 8){
30651             
30652             width = this.thumbEl.getHeight();
30653             this.baseScale = width / this.imageEl.OriginHeight;
30654             
30655             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30656                 height = this.thumbEl.getWidth();
30657                 this.baseScale = height / this.imageEl.OriginHeight;
30658             }
30659             
30660             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30661                 height = this.thumbEl.getWidth();
30662                 this.baseScale = height / this.imageEl.OriginHeight;
30663                 
30664                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30665                     width = this.thumbEl.getHeight();
30666                     this.baseScale = width / this.imageEl.OriginWidth;
30667                 }
30668             }
30669             
30670             return;
30671         }
30672         
30673         width = this.thumbEl.getWidth();
30674         this.baseScale = width / this.imageEl.OriginWidth;
30675         
30676         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30677             height = this.thumbEl.getHeight();
30678             this.baseScale = height / this.imageEl.OriginHeight;
30679         }
30680         
30681         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30682             
30683             height = this.thumbEl.getHeight();
30684             this.baseScale = height / this.imageEl.OriginHeight;
30685             
30686             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30687                 width = this.thumbEl.getWidth();
30688                 this.baseScale = width / this.imageEl.OriginWidth;
30689             }
30690             
30691         }
30692         
30693         return;
30694     },
30695     
30696     getScaleLevel : function()
30697     {
30698         return this.baseScale * Math.pow(1.1, this.scale);
30699     },
30700     
30701     onTouchStart : function(e)
30702     {
30703         if(!this.canvasLoaded){
30704             this.beforeSelectFile(e);
30705             return;
30706         }
30707         
30708         var touches = e.browserEvent.touches;
30709         
30710         if(!touches){
30711             return;
30712         }
30713         
30714         if(touches.length == 1){
30715             this.onMouseDown(e);
30716             return;
30717         }
30718         
30719         if(touches.length != 2){
30720             return;
30721         }
30722         
30723         var coords = [];
30724         
30725         for(var i = 0, finger; finger = touches[i]; i++){
30726             coords.push(finger.pageX, finger.pageY);
30727         }
30728         
30729         var x = Math.pow(coords[0] - coords[2], 2);
30730         var y = Math.pow(coords[1] - coords[3], 2);
30731         
30732         this.startDistance = Math.sqrt(x + y);
30733         
30734         this.startScale = this.scale;
30735         
30736         this.pinching = true;
30737         this.dragable = false;
30738         
30739     },
30740     
30741     onTouchMove : function(e)
30742     {
30743         if(!this.pinching && !this.dragable){
30744             return;
30745         }
30746         
30747         var touches = e.browserEvent.touches;
30748         
30749         if(!touches){
30750             return;
30751         }
30752         
30753         if(this.dragable){
30754             this.onMouseMove(e);
30755             return;
30756         }
30757         
30758         var coords = [];
30759         
30760         for(var i = 0, finger; finger = touches[i]; i++){
30761             coords.push(finger.pageX, finger.pageY);
30762         }
30763         
30764         var x = Math.pow(coords[0] - coords[2], 2);
30765         var y = Math.pow(coords[1] - coords[3], 2);
30766         
30767         this.endDistance = Math.sqrt(x + y);
30768         
30769         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30770         
30771         if(!this.zoomable()){
30772             this.scale = this.startScale;
30773             return;
30774         }
30775         
30776         this.draw();
30777         
30778     },
30779     
30780     onTouchEnd : function(e)
30781     {
30782         this.pinching = false;
30783         this.dragable = false;
30784         
30785     },
30786     
30787     process : function(file, crop)
30788     {
30789         if(this.loadMask){
30790             this.maskEl.mask(this.loadingText);
30791         }
30792         
30793         this.xhr = new XMLHttpRequest();
30794         
30795         file.xhr = this.xhr;
30796
30797         this.xhr.open(this.method, this.url, true);
30798         
30799         var headers = {
30800             "Accept": "application/json",
30801             "Cache-Control": "no-cache",
30802             "X-Requested-With": "XMLHttpRequest"
30803         };
30804         
30805         for (var headerName in headers) {
30806             var headerValue = headers[headerName];
30807             if (headerValue) {
30808                 this.xhr.setRequestHeader(headerName, headerValue);
30809             }
30810         }
30811         
30812         var _this = this;
30813         
30814         this.xhr.onload = function()
30815         {
30816             _this.xhrOnLoad(_this.xhr);
30817         }
30818         
30819         this.xhr.onerror = function()
30820         {
30821             _this.xhrOnError(_this.xhr);
30822         }
30823         
30824         var formData = new FormData();
30825
30826         formData.append('returnHTML', 'NO');
30827         
30828         if(crop){
30829             formData.append('crop', crop);
30830         }
30831         
30832         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30833             formData.append(this.paramName, file, file.name);
30834         }
30835         
30836         if(typeof(file.filename) != 'undefined'){
30837             formData.append('filename', file.filename);
30838         }
30839         
30840         if(typeof(file.mimetype) != 'undefined'){
30841             formData.append('mimetype', file.mimetype);
30842         }
30843         
30844         if(this.fireEvent('arrange', this, formData) != false){
30845             this.xhr.send(formData);
30846         };
30847     },
30848     
30849     xhrOnLoad : function(xhr)
30850     {
30851         if(this.loadMask){
30852             this.maskEl.unmask();
30853         }
30854         
30855         if (xhr.readyState !== 4) {
30856             this.fireEvent('exception', this, xhr);
30857             return;
30858         }
30859
30860         var response = Roo.decode(xhr.responseText);
30861         
30862         if(!response.success){
30863             this.fireEvent('exception', this, xhr);
30864             return;
30865         }
30866         
30867         var response = Roo.decode(xhr.responseText);
30868         
30869         this.fireEvent('upload', this, response);
30870         
30871     },
30872     
30873     xhrOnError : function()
30874     {
30875         if(this.loadMask){
30876             this.maskEl.unmask();
30877         }
30878         
30879         Roo.log('xhr on error');
30880         
30881         var response = Roo.decode(xhr.responseText);
30882           
30883         Roo.log(response);
30884         
30885     },
30886     
30887     prepare : function(file)
30888     {   
30889         if(this.loadMask){
30890             this.maskEl.mask(this.loadingText);
30891         }
30892         
30893         this.file = false;
30894         this.exif = {};
30895         
30896         if(typeof(file) === 'string'){
30897             this.loadCanvas(file);
30898             return;
30899         }
30900         
30901         if(!file || !this.urlAPI){
30902             return;
30903         }
30904         
30905         this.file = file;
30906         this.cropType = file.type;
30907         
30908         var _this = this;
30909         
30910         if(this.fireEvent('prepare', this, this.file) != false){
30911             
30912             var reader = new FileReader();
30913             
30914             reader.onload = function (e) {
30915                 if (e.target.error) {
30916                     Roo.log(e.target.error);
30917                     return;
30918                 }
30919                 
30920                 var buffer = e.target.result,
30921                     dataView = new DataView(buffer),
30922                     offset = 2,
30923                     maxOffset = dataView.byteLength - 4,
30924                     markerBytes,
30925                     markerLength;
30926                 
30927                 if (dataView.getUint16(0) === 0xffd8) {
30928                     while (offset < maxOffset) {
30929                         markerBytes = dataView.getUint16(offset);
30930                         
30931                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30932                             markerLength = dataView.getUint16(offset + 2) + 2;
30933                             if (offset + markerLength > dataView.byteLength) {
30934                                 Roo.log('Invalid meta data: Invalid segment size.');
30935                                 break;
30936                             }
30937                             
30938                             if(markerBytes == 0xffe1){
30939                                 _this.parseExifData(
30940                                     dataView,
30941                                     offset,
30942                                     markerLength
30943                                 );
30944                             }
30945                             
30946                             offset += markerLength;
30947                             
30948                             continue;
30949                         }
30950                         
30951                         break;
30952                     }
30953                     
30954                 }
30955                 
30956                 var url = _this.urlAPI.createObjectURL(_this.file);
30957                 
30958                 _this.loadCanvas(url);
30959                 
30960                 return;
30961             }
30962             
30963             reader.readAsArrayBuffer(this.file);
30964             
30965         }
30966         
30967     },
30968     
30969     parseExifData : function(dataView, offset, length)
30970     {
30971         var tiffOffset = offset + 10,
30972             littleEndian,
30973             dirOffset;
30974     
30975         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30976             // No Exif data, might be XMP data instead
30977             return;
30978         }
30979         
30980         // Check for the ASCII code for "Exif" (0x45786966):
30981         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30982             // No Exif data, might be XMP data instead
30983             return;
30984         }
30985         if (tiffOffset + 8 > dataView.byteLength) {
30986             Roo.log('Invalid Exif data: Invalid segment size.');
30987             return;
30988         }
30989         // Check for the two null bytes:
30990         if (dataView.getUint16(offset + 8) !== 0x0000) {
30991             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30992             return;
30993         }
30994         // Check the byte alignment:
30995         switch (dataView.getUint16(tiffOffset)) {
30996         case 0x4949:
30997             littleEndian = true;
30998             break;
30999         case 0x4D4D:
31000             littleEndian = false;
31001             break;
31002         default:
31003             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31004             return;
31005         }
31006         // Check for the TIFF tag marker (0x002A):
31007         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31008             Roo.log('Invalid Exif data: Missing TIFF marker.');
31009             return;
31010         }
31011         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31012         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31013         
31014         this.parseExifTags(
31015             dataView,
31016             tiffOffset,
31017             tiffOffset + dirOffset,
31018             littleEndian
31019         );
31020     },
31021     
31022     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31023     {
31024         var tagsNumber,
31025             dirEndOffset,
31026             i;
31027         if (dirOffset + 6 > dataView.byteLength) {
31028             Roo.log('Invalid Exif data: Invalid directory offset.');
31029             return;
31030         }
31031         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31032         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31033         if (dirEndOffset + 4 > dataView.byteLength) {
31034             Roo.log('Invalid Exif data: Invalid directory size.');
31035             return;
31036         }
31037         for (i = 0; i < tagsNumber; i += 1) {
31038             this.parseExifTag(
31039                 dataView,
31040                 tiffOffset,
31041                 dirOffset + 2 + 12 * i, // tag offset
31042                 littleEndian
31043             );
31044         }
31045         // Return the offset to the next directory:
31046         return dataView.getUint32(dirEndOffset, littleEndian);
31047     },
31048     
31049     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31050     {
31051         var tag = dataView.getUint16(offset, littleEndian);
31052         
31053         this.exif[tag] = this.getExifValue(
31054             dataView,
31055             tiffOffset,
31056             offset,
31057             dataView.getUint16(offset + 2, littleEndian), // tag type
31058             dataView.getUint32(offset + 4, littleEndian), // tag length
31059             littleEndian
31060         );
31061     },
31062     
31063     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31064     {
31065         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31066             tagSize,
31067             dataOffset,
31068             values,
31069             i,
31070             str,
31071             c;
31072     
31073         if (!tagType) {
31074             Roo.log('Invalid Exif data: Invalid tag type.');
31075             return;
31076         }
31077         
31078         tagSize = tagType.size * length;
31079         // Determine if the value is contained in the dataOffset bytes,
31080         // or if the value at the dataOffset is a pointer to the actual data:
31081         dataOffset = tagSize > 4 ?
31082                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31083         if (dataOffset + tagSize > dataView.byteLength) {
31084             Roo.log('Invalid Exif data: Invalid data offset.');
31085             return;
31086         }
31087         if (length === 1) {
31088             return tagType.getValue(dataView, dataOffset, littleEndian);
31089         }
31090         values = [];
31091         for (i = 0; i < length; i += 1) {
31092             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31093         }
31094         
31095         if (tagType.ascii) {
31096             str = '';
31097             // Concatenate the chars:
31098             for (i = 0; i < values.length; i += 1) {
31099                 c = values[i];
31100                 // Ignore the terminating NULL byte(s):
31101                 if (c === '\u0000') {
31102                     break;
31103                 }
31104                 str += c;
31105             }
31106             return str;
31107         }
31108         return values;
31109     }
31110     
31111 });
31112
31113 Roo.apply(Roo.bootstrap.UploadCropbox, {
31114     tags : {
31115         'Orientation': 0x0112
31116     },
31117     
31118     Orientation: {
31119             1: 0, //'top-left',
31120 //            2: 'top-right',
31121             3: 180, //'bottom-right',
31122 //            4: 'bottom-left',
31123 //            5: 'left-top',
31124             6: 90, //'right-top',
31125 //            7: 'right-bottom',
31126             8: 270 //'left-bottom'
31127     },
31128     
31129     exifTagTypes : {
31130         // byte, 8-bit unsigned int:
31131         1: {
31132             getValue: function (dataView, dataOffset) {
31133                 return dataView.getUint8(dataOffset);
31134             },
31135             size: 1
31136         },
31137         // ascii, 8-bit byte:
31138         2: {
31139             getValue: function (dataView, dataOffset) {
31140                 return String.fromCharCode(dataView.getUint8(dataOffset));
31141             },
31142             size: 1,
31143             ascii: true
31144         },
31145         // short, 16 bit int:
31146         3: {
31147             getValue: function (dataView, dataOffset, littleEndian) {
31148                 return dataView.getUint16(dataOffset, littleEndian);
31149             },
31150             size: 2
31151         },
31152         // long, 32 bit int:
31153         4: {
31154             getValue: function (dataView, dataOffset, littleEndian) {
31155                 return dataView.getUint32(dataOffset, littleEndian);
31156             },
31157             size: 4
31158         },
31159         // rational = two long values, first is numerator, second is denominator:
31160         5: {
31161             getValue: function (dataView, dataOffset, littleEndian) {
31162                 return dataView.getUint32(dataOffset, littleEndian) /
31163                     dataView.getUint32(dataOffset + 4, littleEndian);
31164             },
31165             size: 8
31166         },
31167         // slong, 32 bit signed int:
31168         9: {
31169             getValue: function (dataView, dataOffset, littleEndian) {
31170                 return dataView.getInt32(dataOffset, littleEndian);
31171             },
31172             size: 4
31173         },
31174         // srational, two slongs, first is numerator, second is denominator:
31175         10: {
31176             getValue: function (dataView, dataOffset, littleEndian) {
31177                 return dataView.getInt32(dataOffset, littleEndian) /
31178                     dataView.getInt32(dataOffset + 4, littleEndian);
31179             },
31180             size: 8
31181         }
31182     },
31183     
31184     footer : {
31185         STANDARD : [
31186             {
31187                 tag : 'div',
31188                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31189                 action : 'rotate-left',
31190                 cn : [
31191                     {
31192                         tag : 'button',
31193                         cls : 'btn btn-default',
31194                         html : '<i class="fa fa-undo"></i>'
31195                     }
31196                 ]
31197             },
31198             {
31199                 tag : 'div',
31200                 cls : 'btn-group roo-upload-cropbox-picture',
31201                 action : 'picture',
31202                 cn : [
31203                     {
31204                         tag : 'button',
31205                         cls : 'btn btn-default',
31206                         html : '<i class="fa fa-picture-o"></i>'
31207                     }
31208                 ]
31209             },
31210             {
31211                 tag : 'div',
31212                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31213                 action : 'rotate-right',
31214                 cn : [
31215                     {
31216                         tag : 'button',
31217                         cls : 'btn btn-default',
31218                         html : '<i class="fa fa-repeat"></i>'
31219                     }
31220                 ]
31221             }
31222         ],
31223         DOCUMENT : [
31224             {
31225                 tag : 'div',
31226                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31227                 action : 'rotate-left',
31228                 cn : [
31229                     {
31230                         tag : 'button',
31231                         cls : 'btn btn-default',
31232                         html : '<i class="fa fa-undo"></i>'
31233                     }
31234                 ]
31235             },
31236             {
31237                 tag : 'div',
31238                 cls : 'btn-group roo-upload-cropbox-download',
31239                 action : 'download',
31240                 cn : [
31241                     {
31242                         tag : 'button',
31243                         cls : 'btn btn-default',
31244                         html : '<i class="fa fa-download"></i>'
31245                     }
31246                 ]
31247             },
31248             {
31249                 tag : 'div',
31250                 cls : 'btn-group roo-upload-cropbox-crop',
31251                 action : 'crop',
31252                 cn : [
31253                     {
31254                         tag : 'button',
31255                         cls : 'btn btn-default',
31256                         html : '<i class="fa fa-crop"></i>'
31257                     }
31258                 ]
31259             },
31260             {
31261                 tag : 'div',
31262                 cls : 'btn-group roo-upload-cropbox-trash',
31263                 action : 'trash',
31264                 cn : [
31265                     {
31266                         tag : 'button',
31267                         cls : 'btn btn-default',
31268                         html : '<i class="fa fa-trash"></i>'
31269                     }
31270                 ]
31271             },
31272             {
31273                 tag : 'div',
31274                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31275                 action : 'rotate-right',
31276                 cn : [
31277                     {
31278                         tag : 'button',
31279                         cls : 'btn btn-default',
31280                         html : '<i class="fa fa-repeat"></i>'
31281                     }
31282                 ]
31283             }
31284         ],
31285         ROTATOR : [
31286             {
31287                 tag : 'div',
31288                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31289                 action : 'rotate-left',
31290                 cn : [
31291                     {
31292                         tag : 'button',
31293                         cls : 'btn btn-default',
31294                         html : '<i class="fa fa-undo"></i>'
31295                     }
31296                 ]
31297             },
31298             {
31299                 tag : 'div',
31300                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31301                 action : 'rotate-right',
31302                 cn : [
31303                     {
31304                         tag : 'button',
31305                         cls : 'btn btn-default',
31306                         html : '<i class="fa fa-repeat"></i>'
31307                     }
31308                 ]
31309             }
31310         ]
31311     }
31312 });
31313
31314 /*
31315 * Licence: LGPL
31316 */
31317
31318 /**
31319  * @class Roo.bootstrap.DocumentManager
31320  * @extends Roo.bootstrap.Component
31321  * Bootstrap DocumentManager class
31322  * @cfg {String} paramName default 'imageUpload'
31323  * @cfg {String} toolTipName default 'filename'
31324  * @cfg {String} method default POST
31325  * @cfg {String} url action url
31326  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31327  * @cfg {Boolean} multiple multiple upload default true
31328  * @cfg {Number} thumbSize default 300
31329  * @cfg {String} fieldLabel
31330  * @cfg {Number} labelWidth default 4
31331  * @cfg {String} labelAlign (left|top) default left
31332  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31333 * @cfg {Number} labellg set the width of label (1-12)
31334  * @cfg {Number} labelmd set the width of label (1-12)
31335  * @cfg {Number} labelsm set the width of label (1-12)
31336  * @cfg {Number} labelxs set the width of label (1-12)
31337  * 
31338  * @constructor
31339  * Create a new DocumentManager
31340  * @param {Object} config The config object
31341  */
31342
31343 Roo.bootstrap.DocumentManager = function(config){
31344     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31345     
31346     this.files = [];
31347     this.delegates = [];
31348     
31349     this.addEvents({
31350         /**
31351          * @event initial
31352          * Fire when initial the DocumentManager
31353          * @param {Roo.bootstrap.DocumentManager} this
31354          */
31355         "initial" : true,
31356         /**
31357          * @event inspect
31358          * inspect selected file
31359          * @param {Roo.bootstrap.DocumentManager} this
31360          * @param {File} file
31361          */
31362         "inspect" : true,
31363         /**
31364          * @event exception
31365          * Fire when xhr load exception
31366          * @param {Roo.bootstrap.DocumentManager} this
31367          * @param {XMLHttpRequest} xhr
31368          */
31369         "exception" : true,
31370         /**
31371          * @event afterupload
31372          * Fire when xhr load exception
31373          * @param {Roo.bootstrap.DocumentManager} this
31374          * @param {XMLHttpRequest} xhr
31375          */
31376         "afterupload" : true,
31377         /**
31378          * @event prepare
31379          * prepare the form data
31380          * @param {Roo.bootstrap.DocumentManager} this
31381          * @param {Object} formData
31382          */
31383         "prepare" : true,
31384         /**
31385          * @event remove
31386          * Fire when remove the file
31387          * @param {Roo.bootstrap.DocumentManager} this
31388          * @param {Object} file
31389          */
31390         "remove" : true,
31391         /**
31392          * @event refresh
31393          * Fire after refresh the file
31394          * @param {Roo.bootstrap.DocumentManager} this
31395          */
31396         "refresh" : true,
31397         /**
31398          * @event click
31399          * Fire after click the image
31400          * @param {Roo.bootstrap.DocumentManager} this
31401          * @param {Object} file
31402          */
31403         "click" : true,
31404         /**
31405          * @event edit
31406          * Fire when upload a image and editable set to true
31407          * @param {Roo.bootstrap.DocumentManager} this
31408          * @param {Object} file
31409          */
31410         "edit" : true,
31411         /**
31412          * @event beforeselectfile
31413          * Fire before select file
31414          * @param {Roo.bootstrap.DocumentManager} this
31415          */
31416         "beforeselectfile" : true,
31417         /**
31418          * @event process
31419          * Fire before process file
31420          * @param {Roo.bootstrap.DocumentManager} this
31421          * @param {Object} file
31422          */
31423         "process" : true,
31424         /**
31425          * @event previewrendered
31426          * Fire when preview rendered
31427          * @param {Roo.bootstrap.DocumentManager} this
31428          * @param {Object} file
31429          */
31430         "previewrendered" : true,
31431         /**
31432          */
31433         "previewResize" : true
31434         
31435     });
31436 };
31437
31438 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31439     
31440     boxes : 0,
31441     inputName : '',
31442     thumbSize : 300,
31443     multiple : true,
31444     files : false,
31445     method : 'POST',
31446     url : '',
31447     paramName : 'imageUpload',
31448     toolTipName : 'filename',
31449     fieldLabel : '',
31450     labelWidth : 4,
31451     labelAlign : 'left',
31452     editable : true,
31453     delegates : false,
31454     xhr : false, 
31455     
31456     labellg : 0,
31457     labelmd : 0,
31458     labelsm : 0,
31459     labelxs : 0,
31460     
31461     getAutoCreate : function()
31462     {   
31463         var managerWidget = {
31464             tag : 'div',
31465             cls : 'roo-document-manager',
31466             cn : [
31467                 {
31468                     tag : 'input',
31469                     cls : 'roo-document-manager-selector',
31470                     type : 'file'
31471                 },
31472                 {
31473                     tag : 'div',
31474                     cls : 'roo-document-manager-uploader',
31475                     cn : [
31476                         {
31477                             tag : 'div',
31478                             cls : 'roo-document-manager-upload-btn',
31479                             html : '<i class="fa fa-plus"></i>'
31480                         }
31481                     ]
31482                     
31483                 }
31484             ]
31485         };
31486         
31487         var content = [
31488             {
31489                 tag : 'div',
31490                 cls : 'column col-md-12',
31491                 cn : managerWidget
31492             }
31493         ];
31494         
31495         if(this.fieldLabel.length){
31496             
31497             content = [
31498                 {
31499                     tag : 'div',
31500                     cls : 'column col-md-12',
31501                     html : this.fieldLabel
31502                 },
31503                 {
31504                     tag : 'div',
31505                     cls : 'column col-md-12',
31506                     cn : managerWidget
31507                 }
31508             ];
31509
31510             if(this.labelAlign == 'left'){
31511                 content = [
31512                     {
31513                         tag : 'div',
31514                         cls : 'column',
31515                         html : this.fieldLabel
31516                     },
31517                     {
31518                         tag : 'div',
31519                         cls : 'column',
31520                         cn : managerWidget
31521                     }
31522                 ];
31523                 
31524                 if(this.labelWidth > 12){
31525                     content[0].style = "width: " + this.labelWidth + 'px';
31526                 }
31527
31528                 if(this.labelWidth < 13 && this.labelmd == 0){
31529                     this.labelmd = this.labelWidth;
31530                 }
31531
31532                 if(this.labellg > 0){
31533                     content[0].cls += ' col-lg-' + this.labellg;
31534                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31535                 }
31536
31537                 if(this.labelmd > 0){
31538                     content[0].cls += ' col-md-' + this.labelmd;
31539                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31540                 }
31541
31542                 if(this.labelsm > 0){
31543                     content[0].cls += ' col-sm-' + this.labelsm;
31544                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31545                 }
31546
31547                 if(this.labelxs > 0){
31548                     content[0].cls += ' col-xs-' + this.labelxs;
31549                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31550                 }
31551                 
31552             }
31553         }
31554         
31555         var cfg = {
31556             tag : 'div',
31557             cls : 'row clearfix',
31558             cn : content
31559         };
31560         
31561         return cfg;
31562         
31563     },
31564     
31565     initEvents : function()
31566     {
31567         this.managerEl = this.el.select('.roo-document-manager', true).first();
31568         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31569         
31570         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31571         this.selectorEl.hide();
31572         
31573         if(this.multiple){
31574             this.selectorEl.attr('multiple', 'multiple');
31575         }
31576         
31577         this.selectorEl.on('change', this.onFileSelected, this);
31578         
31579         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31580         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31581         
31582         this.uploader.on('click', this.onUploaderClick, this);
31583         
31584         this.renderProgressDialog();
31585         
31586         var _this = this;
31587         
31588         window.addEventListener("resize", function() { _this.refresh(); } );
31589         
31590         this.fireEvent('initial', this);
31591     },
31592     
31593     renderProgressDialog : function()
31594     {
31595         var _this = this;
31596         
31597         this.progressDialog = new Roo.bootstrap.Modal({
31598             cls : 'roo-document-manager-progress-dialog',
31599             allow_close : false,
31600             animate : false,
31601             title : '',
31602             buttons : [
31603                 {
31604                     name  :'cancel',
31605                     weight : 'danger',
31606                     html : 'Cancel'
31607                 }
31608             ], 
31609             listeners : { 
31610                 btnclick : function() {
31611                     _this.uploadCancel();
31612                     this.hide();
31613                 }
31614             }
31615         });
31616          
31617         this.progressDialog.render(Roo.get(document.body));
31618          
31619         this.progress = new Roo.bootstrap.Progress({
31620             cls : 'roo-document-manager-progress',
31621             active : true,
31622             striped : true
31623         });
31624         
31625         this.progress.render(this.progressDialog.getChildContainer());
31626         
31627         this.progressBar = new Roo.bootstrap.ProgressBar({
31628             cls : 'roo-document-manager-progress-bar',
31629             aria_valuenow : 0,
31630             aria_valuemin : 0,
31631             aria_valuemax : 12,
31632             panel : 'success'
31633         });
31634         
31635         this.progressBar.render(this.progress.getChildContainer());
31636     },
31637     
31638     onUploaderClick : function(e)
31639     {
31640         e.preventDefault();
31641      
31642         if(this.fireEvent('beforeselectfile', this) != false){
31643             this.selectorEl.dom.click();
31644         }
31645         
31646     },
31647     
31648     onFileSelected : function(e)
31649     {
31650         e.preventDefault();
31651         
31652         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31653             return;
31654         }
31655         
31656         Roo.each(this.selectorEl.dom.files, function(file){
31657             if(this.fireEvent('inspect', this, file) != false){
31658                 this.files.push(file);
31659             }
31660         }, this);
31661         
31662         this.queue();
31663         
31664     },
31665     
31666     queue : function()
31667     {
31668         this.selectorEl.dom.value = '';
31669         
31670         if(!this.files || !this.files.length){
31671             return;
31672         }
31673         
31674         if(this.boxes > 0 && this.files.length > this.boxes){
31675             this.files = this.files.slice(0, this.boxes);
31676         }
31677         
31678         this.uploader.show();
31679         
31680         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31681             this.uploader.hide();
31682         }
31683         
31684         var _this = this;
31685         
31686         var files = [];
31687         
31688         var docs = [];
31689         
31690         Roo.each(this.files, function(file){
31691             
31692             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31693                 var f = this.renderPreview(file);
31694                 files.push(f);
31695                 return;
31696             }
31697             
31698             if(file.type.indexOf('image') != -1){
31699                 this.delegates.push(
31700                     (function(){
31701                         _this.process(file);
31702                     }).createDelegate(this)
31703                 );
31704         
31705                 return;
31706             }
31707             
31708             docs.push(
31709                 (function(){
31710                     _this.process(file);
31711                 }).createDelegate(this)
31712             );
31713             
31714         }, this);
31715         
31716         this.files = files;
31717         
31718         this.delegates = this.delegates.concat(docs);
31719         
31720         if(!this.delegates.length){
31721             this.refresh();
31722             return;
31723         }
31724         
31725         this.progressBar.aria_valuemax = this.delegates.length;
31726         
31727         this.arrange();
31728         
31729         return;
31730     },
31731     
31732     arrange : function()
31733     {
31734         if(!this.delegates.length){
31735             this.progressDialog.hide();
31736             this.refresh();
31737             return;
31738         }
31739         
31740         var delegate = this.delegates.shift();
31741         
31742         this.progressDialog.show();
31743         
31744         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31745         
31746         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31747         
31748         delegate();
31749     },
31750     
31751     refresh : function()
31752     {
31753         this.uploader.show();
31754         
31755         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31756             this.uploader.hide();
31757         }
31758         
31759         Roo.isTouch ? this.closable(false) : this.closable(true);
31760         
31761         this.fireEvent('refresh', this);
31762     },
31763     
31764     onRemove : function(e, el, o)
31765     {
31766         e.preventDefault();
31767         
31768         this.fireEvent('remove', this, o);
31769         
31770     },
31771     
31772     remove : function(o)
31773     {
31774         var files = [];
31775         
31776         Roo.each(this.files, function(file){
31777             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31778                 files.push(file);
31779                 return;
31780             }
31781
31782             o.target.remove();
31783
31784         }, this);
31785         
31786         this.files = files;
31787         
31788         this.refresh();
31789     },
31790     
31791     clear : function()
31792     {
31793         Roo.each(this.files, function(file){
31794             if(!file.target){
31795                 return;
31796             }
31797             
31798             file.target.remove();
31799
31800         }, this);
31801         
31802         this.files = [];
31803         
31804         this.refresh();
31805     },
31806     
31807     onClick : function(e, el, o)
31808     {
31809         e.preventDefault();
31810         
31811         this.fireEvent('click', this, o);
31812         
31813     },
31814     
31815     closable : function(closable)
31816     {
31817         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31818             
31819             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31820             
31821             if(closable){
31822                 el.show();
31823                 return;
31824             }
31825             
31826             el.hide();
31827             
31828         }, this);
31829     },
31830     
31831     xhrOnLoad : function(xhr)
31832     {
31833         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31834             el.remove();
31835         }, this);
31836         
31837         if (xhr.readyState !== 4) {
31838             this.arrange();
31839             this.fireEvent('exception', this, xhr);
31840             return;
31841         }
31842
31843         var response = Roo.decode(xhr.responseText);
31844         
31845         if(!response.success){
31846             this.arrange();
31847             this.fireEvent('exception', this, xhr);
31848             return;
31849         }
31850         
31851         var file = this.renderPreview(response.data);
31852         
31853         this.files.push(file);
31854         
31855         this.arrange();
31856         
31857         this.fireEvent('afterupload', this, xhr);
31858         
31859     },
31860     
31861     xhrOnError : function(xhr)
31862     {
31863         Roo.log('xhr on error');
31864         
31865         var response = Roo.decode(xhr.responseText);
31866           
31867         Roo.log(response);
31868         
31869         this.arrange();
31870     },
31871     
31872     process : function(file)
31873     {
31874         if(this.fireEvent('process', this, file) !== false){
31875             if(this.editable && file.type.indexOf('image') != -1){
31876                 this.fireEvent('edit', this, file);
31877                 return;
31878             }
31879
31880             this.uploadStart(file, false);
31881
31882             return;
31883         }
31884         
31885     },
31886     
31887     uploadStart : function(file, crop)
31888     {
31889         this.xhr = new XMLHttpRequest();
31890         
31891         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31892             this.arrange();
31893             return;
31894         }
31895         
31896         file.xhr = this.xhr;
31897             
31898         this.managerEl.createChild({
31899             tag : 'div',
31900             cls : 'roo-document-manager-loading',
31901             cn : [
31902                 {
31903                     tag : 'div',
31904                     tooltip : file.name,
31905                     cls : 'roo-document-manager-thumb',
31906                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31907                 }
31908             ]
31909
31910         });
31911
31912         this.xhr.open(this.method, this.url, true);
31913         
31914         var headers = {
31915             "Accept": "application/json",
31916             "Cache-Control": "no-cache",
31917             "X-Requested-With": "XMLHttpRequest"
31918         };
31919         
31920         for (var headerName in headers) {
31921             var headerValue = headers[headerName];
31922             if (headerValue) {
31923                 this.xhr.setRequestHeader(headerName, headerValue);
31924             }
31925         }
31926         
31927         var _this = this;
31928         
31929         this.xhr.onload = function()
31930         {
31931             _this.xhrOnLoad(_this.xhr);
31932         }
31933         
31934         this.xhr.onerror = function()
31935         {
31936             _this.xhrOnError(_this.xhr);
31937         }
31938         
31939         var formData = new FormData();
31940
31941         formData.append('returnHTML', 'NO');
31942         
31943         if(crop){
31944             formData.append('crop', crop);
31945         }
31946         
31947         formData.append(this.paramName, file, file.name);
31948         
31949         var options = {
31950             file : file, 
31951             manually : false
31952         };
31953         
31954         if(this.fireEvent('prepare', this, formData, options) != false){
31955             
31956             if(options.manually){
31957                 return;
31958             }
31959             
31960             this.xhr.send(formData);
31961             return;
31962         };
31963         
31964         this.uploadCancel();
31965     },
31966     
31967     uploadCancel : function()
31968     {
31969         if (this.xhr) {
31970             this.xhr.abort();
31971         }
31972         
31973         this.delegates = [];
31974         
31975         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31976             el.remove();
31977         }, this);
31978         
31979         this.arrange();
31980     },
31981     
31982     renderPreview : function(file)
31983     {
31984         if(typeof(file.target) != 'undefined' && file.target){
31985             return file;
31986         }
31987         
31988         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31989         
31990         var previewEl = this.managerEl.createChild({
31991             tag : 'div',
31992             cls : 'roo-document-manager-preview',
31993             cn : [
31994                 {
31995                     tag : 'div',
31996                     tooltip : file[this.toolTipName],
31997                     cls : 'roo-document-manager-thumb',
31998                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31999                 },
32000                 {
32001                     tag : 'button',
32002                     cls : 'close',
32003                     html : '<i class="fa fa-times-circle"></i>'
32004                 }
32005             ]
32006         });
32007
32008         var close = previewEl.select('button.close', true).first();
32009
32010         close.on('click', this.onRemove, this, file);
32011
32012         file.target = previewEl;
32013
32014         var image = previewEl.select('img', true).first();
32015         
32016         var _this = this;
32017         
32018         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32019         
32020         image.on('click', this.onClick, this, file);
32021         
32022         this.fireEvent('previewrendered', this, file);
32023         
32024         return file;
32025         
32026     },
32027     
32028     onPreviewLoad : function(file, image)
32029     {
32030         if(typeof(file.target) == 'undefined' || !file.target){
32031             return;
32032         }
32033         
32034         var width = image.dom.naturalWidth || image.dom.width;
32035         var height = image.dom.naturalHeight || image.dom.height;
32036         
32037         if(!this.previewResize) {
32038             return;
32039         }
32040         
32041         if(width > height){
32042             file.target.addClass('wide');
32043             return;
32044         }
32045         
32046         file.target.addClass('tall');
32047         return;
32048         
32049     },
32050     
32051     uploadFromSource : function(file, crop)
32052     {
32053         this.xhr = new XMLHttpRequest();
32054         
32055         this.managerEl.createChild({
32056             tag : 'div',
32057             cls : 'roo-document-manager-loading',
32058             cn : [
32059                 {
32060                     tag : 'div',
32061                     tooltip : file.name,
32062                     cls : 'roo-document-manager-thumb',
32063                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32064                 }
32065             ]
32066
32067         });
32068
32069         this.xhr.open(this.method, this.url, true);
32070         
32071         var headers = {
32072             "Accept": "application/json",
32073             "Cache-Control": "no-cache",
32074             "X-Requested-With": "XMLHttpRequest"
32075         };
32076         
32077         for (var headerName in headers) {
32078             var headerValue = headers[headerName];
32079             if (headerValue) {
32080                 this.xhr.setRequestHeader(headerName, headerValue);
32081             }
32082         }
32083         
32084         var _this = this;
32085         
32086         this.xhr.onload = function()
32087         {
32088             _this.xhrOnLoad(_this.xhr);
32089         }
32090         
32091         this.xhr.onerror = function()
32092         {
32093             _this.xhrOnError(_this.xhr);
32094         }
32095         
32096         var formData = new FormData();
32097
32098         formData.append('returnHTML', 'NO');
32099         
32100         formData.append('crop', crop);
32101         
32102         if(typeof(file.filename) != 'undefined'){
32103             formData.append('filename', file.filename);
32104         }
32105         
32106         if(typeof(file.mimetype) != 'undefined'){
32107             formData.append('mimetype', file.mimetype);
32108         }
32109         
32110         Roo.log(formData);
32111         
32112         if(this.fireEvent('prepare', this, formData) != false){
32113             this.xhr.send(formData);
32114         };
32115     }
32116 });
32117
32118 /*
32119 * Licence: LGPL
32120 */
32121
32122 /**
32123  * @class Roo.bootstrap.DocumentViewer
32124  * @extends Roo.bootstrap.Component
32125  * Bootstrap DocumentViewer class
32126  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32127  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32128  * 
32129  * @constructor
32130  * Create a new DocumentViewer
32131  * @param {Object} config The config object
32132  */
32133
32134 Roo.bootstrap.DocumentViewer = function(config){
32135     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32136     
32137     this.addEvents({
32138         /**
32139          * @event initial
32140          * Fire after initEvent
32141          * @param {Roo.bootstrap.DocumentViewer} this
32142          */
32143         "initial" : true,
32144         /**
32145          * @event click
32146          * Fire after click
32147          * @param {Roo.bootstrap.DocumentViewer} this
32148          */
32149         "click" : true,
32150         /**
32151          * @event download
32152          * Fire after download button
32153          * @param {Roo.bootstrap.DocumentViewer} this
32154          */
32155         "download" : true,
32156         /**
32157          * @event trash
32158          * Fire after trash button
32159          * @param {Roo.bootstrap.DocumentViewer} this
32160          */
32161         "trash" : true
32162         
32163     });
32164 };
32165
32166 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32167     
32168     showDownload : true,
32169     
32170     showTrash : true,
32171     
32172     getAutoCreate : function()
32173     {
32174         var cfg = {
32175             tag : 'div',
32176             cls : 'roo-document-viewer',
32177             cn : [
32178                 {
32179                     tag : 'div',
32180                     cls : 'roo-document-viewer-body',
32181                     cn : [
32182                         {
32183                             tag : 'div',
32184                             cls : 'roo-document-viewer-thumb',
32185                             cn : [
32186                                 {
32187                                     tag : 'img',
32188                                     cls : 'roo-document-viewer-image'
32189                                 }
32190                             ]
32191                         }
32192                     ]
32193                 },
32194                 {
32195                     tag : 'div',
32196                     cls : 'roo-document-viewer-footer',
32197                     cn : {
32198                         tag : 'div',
32199                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32200                         cn : [
32201                             {
32202                                 tag : 'div',
32203                                 cls : 'btn-group roo-document-viewer-download',
32204                                 cn : [
32205                                     {
32206                                         tag : 'button',
32207                                         cls : 'btn btn-default',
32208                                         html : '<i class="fa fa-download"></i>'
32209                                     }
32210                                 ]
32211                             },
32212                             {
32213                                 tag : 'div',
32214                                 cls : 'btn-group roo-document-viewer-trash',
32215                                 cn : [
32216                                     {
32217                                         tag : 'button',
32218                                         cls : 'btn btn-default',
32219                                         html : '<i class="fa fa-trash"></i>'
32220                                     }
32221                                 ]
32222                             }
32223                         ]
32224                     }
32225                 }
32226             ]
32227         };
32228         
32229         return cfg;
32230     },
32231     
32232     initEvents : function()
32233     {
32234         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32235         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32236         
32237         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32238         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32239         
32240         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32241         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32242         
32243         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32244         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32245         
32246         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32247         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32248         
32249         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32250         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32251         
32252         this.bodyEl.on('click', this.onClick, this);
32253         this.downloadBtn.on('click', this.onDownload, this);
32254         this.trashBtn.on('click', this.onTrash, this);
32255         
32256         this.downloadBtn.hide();
32257         this.trashBtn.hide();
32258         
32259         if(this.showDownload){
32260             this.downloadBtn.show();
32261         }
32262         
32263         if(this.showTrash){
32264             this.trashBtn.show();
32265         }
32266         
32267         if(!this.showDownload && !this.showTrash) {
32268             this.footerEl.hide();
32269         }
32270         
32271     },
32272     
32273     initial : function()
32274     {
32275         this.fireEvent('initial', this);
32276         
32277     },
32278     
32279     onClick : function(e)
32280     {
32281         e.preventDefault();
32282         
32283         this.fireEvent('click', this);
32284     },
32285     
32286     onDownload : function(e)
32287     {
32288         e.preventDefault();
32289         
32290         this.fireEvent('download', this);
32291     },
32292     
32293     onTrash : function(e)
32294     {
32295         e.preventDefault();
32296         
32297         this.fireEvent('trash', this);
32298     }
32299     
32300 });
32301 /*
32302  * - LGPL
32303  *
32304  * nav progress bar
32305  * 
32306  */
32307
32308 /**
32309  * @class Roo.bootstrap.NavProgressBar
32310  * @extends Roo.bootstrap.Component
32311  * Bootstrap NavProgressBar class
32312  * 
32313  * @constructor
32314  * Create a new nav progress bar
32315  * @param {Object} config The config object
32316  */
32317
32318 Roo.bootstrap.NavProgressBar = function(config){
32319     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32320
32321     this.bullets = this.bullets || [];
32322    
32323 //    Roo.bootstrap.NavProgressBar.register(this);
32324      this.addEvents({
32325         /**
32326              * @event changed
32327              * Fires when the active item changes
32328              * @param {Roo.bootstrap.NavProgressBar} this
32329              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32330              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32331          */
32332         'changed': true
32333      });
32334     
32335 };
32336
32337 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32338     
32339     bullets : [],
32340     barItems : [],
32341     
32342     getAutoCreate : function()
32343     {
32344         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32345         
32346         cfg = {
32347             tag : 'div',
32348             cls : 'roo-navigation-bar-group',
32349             cn : [
32350                 {
32351                     tag : 'div',
32352                     cls : 'roo-navigation-top-bar'
32353                 },
32354                 {
32355                     tag : 'div',
32356                     cls : 'roo-navigation-bullets-bar',
32357                     cn : [
32358                         {
32359                             tag : 'ul',
32360                             cls : 'roo-navigation-bar'
32361                         }
32362                     ]
32363                 },
32364                 
32365                 {
32366                     tag : 'div',
32367                     cls : 'roo-navigation-bottom-bar'
32368                 }
32369             ]
32370             
32371         };
32372         
32373         return cfg;
32374         
32375     },
32376     
32377     initEvents: function() 
32378     {
32379         
32380     },
32381     
32382     onRender : function(ct, position) 
32383     {
32384         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32385         
32386         if(this.bullets.length){
32387             Roo.each(this.bullets, function(b){
32388                this.addItem(b);
32389             }, this);
32390         }
32391         
32392         this.format();
32393         
32394     },
32395     
32396     addItem : function(cfg)
32397     {
32398         var item = new Roo.bootstrap.NavProgressItem(cfg);
32399         
32400         item.parentId = this.id;
32401         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32402         
32403         if(cfg.html){
32404             var top = new Roo.bootstrap.Element({
32405                 tag : 'div',
32406                 cls : 'roo-navigation-bar-text'
32407             });
32408             
32409             var bottom = new Roo.bootstrap.Element({
32410                 tag : 'div',
32411                 cls : 'roo-navigation-bar-text'
32412             });
32413             
32414             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32415             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32416             
32417             var topText = new Roo.bootstrap.Element({
32418                 tag : 'span',
32419                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32420             });
32421             
32422             var bottomText = new Roo.bootstrap.Element({
32423                 tag : 'span',
32424                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32425             });
32426             
32427             topText.onRender(top.el, null);
32428             bottomText.onRender(bottom.el, null);
32429             
32430             item.topEl = top;
32431             item.bottomEl = bottom;
32432         }
32433         
32434         this.barItems.push(item);
32435         
32436         return item;
32437     },
32438     
32439     getActive : function()
32440     {
32441         var active = false;
32442         
32443         Roo.each(this.barItems, function(v){
32444             
32445             if (!v.isActive()) {
32446                 return;
32447             }
32448             
32449             active = v;
32450             return false;
32451             
32452         });
32453         
32454         return active;
32455     },
32456     
32457     setActiveItem : function(item)
32458     {
32459         var prev = false;
32460         
32461         Roo.each(this.barItems, function(v){
32462             if (v.rid == item.rid) {
32463                 return ;
32464             }
32465             
32466             if (v.isActive()) {
32467                 v.setActive(false);
32468                 prev = v;
32469             }
32470         });
32471
32472         item.setActive(true);
32473         
32474         this.fireEvent('changed', this, item, prev);
32475     },
32476     
32477     getBarItem: function(rid)
32478     {
32479         var ret = false;
32480         
32481         Roo.each(this.barItems, function(e) {
32482             if (e.rid != rid) {
32483                 return;
32484             }
32485             
32486             ret =  e;
32487             return false;
32488         });
32489         
32490         return ret;
32491     },
32492     
32493     indexOfItem : function(item)
32494     {
32495         var index = false;
32496         
32497         Roo.each(this.barItems, function(v, i){
32498             
32499             if (v.rid != item.rid) {
32500                 return;
32501             }
32502             
32503             index = i;
32504             return false
32505         });
32506         
32507         return index;
32508     },
32509     
32510     setActiveNext : function()
32511     {
32512         var i = this.indexOfItem(this.getActive());
32513         
32514         if (i > this.barItems.length) {
32515             return;
32516         }
32517         
32518         this.setActiveItem(this.barItems[i+1]);
32519     },
32520     
32521     setActivePrev : function()
32522     {
32523         var i = this.indexOfItem(this.getActive());
32524         
32525         if (i  < 1) {
32526             return;
32527         }
32528         
32529         this.setActiveItem(this.barItems[i-1]);
32530     },
32531     
32532     format : function()
32533     {
32534         if(!this.barItems.length){
32535             return;
32536         }
32537      
32538         var width = 100 / this.barItems.length;
32539         
32540         Roo.each(this.barItems, function(i){
32541             i.el.setStyle('width', width + '%');
32542             i.topEl.el.setStyle('width', width + '%');
32543             i.bottomEl.el.setStyle('width', width + '%');
32544         }, this);
32545         
32546     }
32547     
32548 });
32549 /*
32550  * - LGPL
32551  *
32552  * Nav Progress Item
32553  * 
32554  */
32555
32556 /**
32557  * @class Roo.bootstrap.NavProgressItem
32558  * @extends Roo.bootstrap.Component
32559  * Bootstrap NavProgressItem class
32560  * @cfg {String} rid the reference id
32561  * @cfg {Boolean} active (true|false) Is item active default false
32562  * @cfg {Boolean} disabled (true|false) Is item active default false
32563  * @cfg {String} html
32564  * @cfg {String} position (top|bottom) text position default bottom
32565  * @cfg {String} icon show icon instead of number
32566  * 
32567  * @constructor
32568  * Create a new NavProgressItem
32569  * @param {Object} config The config object
32570  */
32571 Roo.bootstrap.NavProgressItem = function(config){
32572     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32573     this.addEvents({
32574         // raw events
32575         /**
32576          * @event click
32577          * The raw click event for the entire grid.
32578          * @param {Roo.bootstrap.NavProgressItem} this
32579          * @param {Roo.EventObject} e
32580          */
32581         "click" : true
32582     });
32583    
32584 };
32585
32586 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32587     
32588     rid : '',
32589     active : false,
32590     disabled : false,
32591     html : '',
32592     position : 'bottom',
32593     icon : false,
32594     
32595     getAutoCreate : function()
32596     {
32597         var iconCls = 'roo-navigation-bar-item-icon';
32598         
32599         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32600         
32601         var cfg = {
32602             tag: 'li',
32603             cls: 'roo-navigation-bar-item',
32604             cn : [
32605                 {
32606                     tag : 'i',
32607                     cls : iconCls
32608                 }
32609             ]
32610         };
32611         
32612         if(this.active){
32613             cfg.cls += ' active';
32614         }
32615         if(this.disabled){
32616             cfg.cls += ' disabled';
32617         }
32618         
32619         return cfg;
32620     },
32621     
32622     disable : function()
32623     {
32624         this.setDisabled(true);
32625     },
32626     
32627     enable : function()
32628     {
32629         this.setDisabled(false);
32630     },
32631     
32632     initEvents: function() 
32633     {
32634         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32635         
32636         this.iconEl.on('click', this.onClick, this);
32637     },
32638     
32639     onClick : function(e)
32640     {
32641         e.preventDefault();
32642         
32643         if(this.disabled){
32644             return;
32645         }
32646         
32647         if(this.fireEvent('click', this, e) === false){
32648             return;
32649         };
32650         
32651         this.parent().setActiveItem(this);
32652     },
32653     
32654     isActive: function () 
32655     {
32656         return this.active;
32657     },
32658     
32659     setActive : function(state)
32660     {
32661         if(this.active == state){
32662             return;
32663         }
32664         
32665         this.active = state;
32666         
32667         if (state) {
32668             this.el.addClass('active');
32669             return;
32670         }
32671         
32672         this.el.removeClass('active');
32673         
32674         return;
32675     },
32676     
32677     setDisabled : function(state)
32678     {
32679         if(this.disabled == state){
32680             return;
32681         }
32682         
32683         this.disabled = state;
32684         
32685         if (state) {
32686             this.el.addClass('disabled');
32687             return;
32688         }
32689         
32690         this.el.removeClass('disabled');
32691     },
32692     
32693     tooltipEl : function()
32694     {
32695         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32696     }
32697 });
32698  
32699
32700  /*
32701  * - LGPL
32702  *
32703  * FieldLabel
32704  * 
32705  */
32706
32707 /**
32708  * @class Roo.bootstrap.FieldLabel
32709  * @extends Roo.bootstrap.Component
32710  * Bootstrap FieldLabel class
32711  * @cfg {String} html contents of the element
32712  * @cfg {String} tag tag of the element default label
32713  * @cfg {String} cls class of the element
32714  * @cfg {String} target label target 
32715  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32716  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32717  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32718  * @cfg {String} iconTooltip default "This field is required"
32719  * @cfg {String} indicatorpos (left|right) default left
32720  * 
32721  * @constructor
32722  * Create a new FieldLabel
32723  * @param {Object} config The config object
32724  */
32725
32726 Roo.bootstrap.FieldLabel = function(config){
32727     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32728     
32729     this.addEvents({
32730             /**
32731              * @event invalid
32732              * Fires after the field has been marked as invalid.
32733              * @param {Roo.form.FieldLabel} this
32734              * @param {String} msg The validation message
32735              */
32736             invalid : true,
32737             /**
32738              * @event valid
32739              * Fires after the field has been validated with no errors.
32740              * @param {Roo.form.FieldLabel} this
32741              */
32742             valid : true
32743         });
32744 };
32745
32746 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32747     
32748     tag: 'label',
32749     cls: '',
32750     html: '',
32751     target: '',
32752     allowBlank : true,
32753     invalidClass : 'has-warning',
32754     validClass : 'has-success',
32755     iconTooltip : 'This field is required',
32756     indicatorpos : 'left',
32757     
32758     getAutoCreate : function(){
32759         
32760         var cls = "";
32761         if (!this.allowBlank) {
32762             cls  = "visible";
32763         }
32764         
32765         var cfg = {
32766             tag : this.tag,
32767             cls : 'roo-bootstrap-field-label ' + this.cls,
32768             for : this.target,
32769             cn : [
32770                 {
32771                     tag : 'i',
32772                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32773                     tooltip : this.iconTooltip
32774                 },
32775                 {
32776                     tag : 'span',
32777                     html : this.html
32778                 }
32779             ] 
32780         };
32781         
32782         if(this.indicatorpos == 'right'){
32783             var cfg = {
32784                 tag : this.tag,
32785                 cls : 'roo-bootstrap-field-label ' + this.cls,
32786                 for : this.target,
32787                 cn : [
32788                     {
32789                         tag : 'span',
32790                         html : this.html
32791                     },
32792                     {
32793                         tag : 'i',
32794                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32795                         tooltip : this.iconTooltip
32796                     }
32797                 ] 
32798             };
32799         }
32800         
32801         return cfg;
32802     },
32803     
32804     initEvents: function() 
32805     {
32806         Roo.bootstrap.Element.superclass.initEvents.call(this);
32807         
32808         this.indicator = this.indicatorEl();
32809         
32810         if(this.indicator){
32811             this.indicator.removeClass('visible');
32812             this.indicator.addClass('invisible');
32813         }
32814         
32815         Roo.bootstrap.FieldLabel.register(this);
32816     },
32817     
32818     indicatorEl : function()
32819     {
32820         var indicator = this.el.select('i.roo-required-indicator',true).first();
32821         
32822         if(!indicator){
32823             return false;
32824         }
32825         
32826         return indicator;
32827         
32828     },
32829     
32830     /**
32831      * Mark this field as valid
32832      */
32833     markValid : function()
32834     {
32835         if(this.indicator){
32836             this.indicator.removeClass('visible');
32837             this.indicator.addClass('invisible');
32838         }
32839         if (Roo.bootstrap.version == 3) {
32840             this.el.removeClass(this.invalidClass);
32841             this.el.addClass(this.validClass);
32842         } else {
32843             this.el.removeClass('is-invalid');
32844             this.el.addClass('is-valid');
32845         }
32846         
32847         
32848         this.fireEvent('valid', this);
32849     },
32850     
32851     /**
32852      * Mark this field as invalid
32853      * @param {String} msg The validation message
32854      */
32855     markInvalid : function(msg)
32856     {
32857         if(this.indicator){
32858             this.indicator.removeClass('invisible');
32859             this.indicator.addClass('visible');
32860         }
32861           if (Roo.bootstrap.version == 3) {
32862             this.el.removeClass(this.validClass);
32863             this.el.addClass(this.invalidClass);
32864         } else {
32865             this.el.removeClass('is-valid');
32866             this.el.addClass('is-invalid');
32867         }
32868         
32869         
32870         this.fireEvent('invalid', this, msg);
32871     }
32872     
32873    
32874 });
32875
32876 Roo.apply(Roo.bootstrap.FieldLabel, {
32877     
32878     groups: {},
32879     
32880      /**
32881     * register a FieldLabel Group
32882     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32883     */
32884     register : function(label)
32885     {
32886         if(this.groups.hasOwnProperty(label.target)){
32887             return;
32888         }
32889      
32890         this.groups[label.target] = label;
32891         
32892     },
32893     /**
32894     * fetch a FieldLabel Group based on the target
32895     * @param {string} target
32896     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32897     */
32898     get: function(target) {
32899         if (typeof(this.groups[target]) == 'undefined') {
32900             return false;
32901         }
32902         
32903         return this.groups[target] ;
32904     }
32905 });
32906
32907  
32908
32909  /*
32910  * - LGPL
32911  *
32912  * page DateSplitField.
32913  * 
32914  */
32915
32916
32917 /**
32918  * @class Roo.bootstrap.DateSplitField
32919  * @extends Roo.bootstrap.Component
32920  * Bootstrap DateSplitField class
32921  * @cfg {string} fieldLabel - the label associated
32922  * @cfg {Number} labelWidth set the width of label (0-12)
32923  * @cfg {String} labelAlign (top|left)
32924  * @cfg {Boolean} dayAllowBlank (true|false) default false
32925  * @cfg {Boolean} monthAllowBlank (true|false) default false
32926  * @cfg {Boolean} yearAllowBlank (true|false) default false
32927  * @cfg {string} dayPlaceholder 
32928  * @cfg {string} monthPlaceholder
32929  * @cfg {string} yearPlaceholder
32930  * @cfg {string} dayFormat default 'd'
32931  * @cfg {string} monthFormat default 'm'
32932  * @cfg {string} yearFormat default 'Y'
32933  * @cfg {Number} labellg set the width of label (1-12)
32934  * @cfg {Number} labelmd set the width of label (1-12)
32935  * @cfg {Number} labelsm set the width of label (1-12)
32936  * @cfg {Number} labelxs set the width of label (1-12)
32937
32938  *     
32939  * @constructor
32940  * Create a new DateSplitField
32941  * @param {Object} config The config object
32942  */
32943
32944 Roo.bootstrap.DateSplitField = function(config){
32945     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32946     
32947     this.addEvents({
32948         // raw events
32949          /**
32950          * @event years
32951          * getting the data of years
32952          * @param {Roo.bootstrap.DateSplitField} this
32953          * @param {Object} years
32954          */
32955         "years" : true,
32956         /**
32957          * @event days
32958          * getting the data of days
32959          * @param {Roo.bootstrap.DateSplitField} this
32960          * @param {Object} days
32961          */
32962         "days" : true,
32963         /**
32964          * @event invalid
32965          * Fires after the field has been marked as invalid.
32966          * @param {Roo.form.Field} this
32967          * @param {String} msg The validation message
32968          */
32969         invalid : true,
32970        /**
32971          * @event valid
32972          * Fires after the field has been validated with no errors.
32973          * @param {Roo.form.Field} this
32974          */
32975         valid : true
32976     });
32977 };
32978
32979 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32980     
32981     fieldLabel : '',
32982     labelAlign : 'top',
32983     labelWidth : 3,
32984     dayAllowBlank : false,
32985     monthAllowBlank : false,
32986     yearAllowBlank : false,
32987     dayPlaceholder : '',
32988     monthPlaceholder : '',
32989     yearPlaceholder : '',
32990     dayFormat : 'd',
32991     monthFormat : 'm',
32992     yearFormat : 'Y',
32993     isFormField : true,
32994     labellg : 0,
32995     labelmd : 0,
32996     labelsm : 0,
32997     labelxs : 0,
32998     
32999     getAutoCreate : function()
33000     {
33001         var cfg = {
33002             tag : 'div',
33003             cls : 'row roo-date-split-field-group',
33004             cn : [
33005                 {
33006                     tag : 'input',
33007                     type : 'hidden',
33008                     cls : 'form-hidden-field roo-date-split-field-group-value',
33009                     name : this.name
33010                 }
33011             ]
33012         };
33013         
33014         var labelCls = 'col-md-12';
33015         var contentCls = 'col-md-4';
33016         
33017         if(this.fieldLabel){
33018             
33019             var label = {
33020                 tag : 'div',
33021                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33022                 cn : [
33023                     {
33024                         tag : 'label',
33025                         html : this.fieldLabel
33026                     }
33027                 ]
33028             };
33029             
33030             if(this.labelAlign == 'left'){
33031             
33032                 if(this.labelWidth > 12){
33033                     label.style = "width: " + this.labelWidth + 'px';
33034                 }
33035
33036                 if(this.labelWidth < 13 && this.labelmd == 0){
33037                     this.labelmd = this.labelWidth;
33038                 }
33039
33040                 if(this.labellg > 0){
33041                     labelCls = ' col-lg-' + this.labellg;
33042                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33043                 }
33044
33045                 if(this.labelmd > 0){
33046                     labelCls = ' col-md-' + this.labelmd;
33047                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33048                 }
33049
33050                 if(this.labelsm > 0){
33051                     labelCls = ' col-sm-' + this.labelsm;
33052                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33053                 }
33054
33055                 if(this.labelxs > 0){
33056                     labelCls = ' col-xs-' + this.labelxs;
33057                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33058                 }
33059             }
33060             
33061             label.cls += ' ' + labelCls;
33062             
33063             cfg.cn.push(label);
33064         }
33065         
33066         Roo.each(['day', 'month', 'year'], function(t){
33067             cfg.cn.push({
33068                 tag : 'div',
33069                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33070             });
33071         }, this);
33072         
33073         return cfg;
33074     },
33075     
33076     inputEl: function ()
33077     {
33078         return this.el.select('.roo-date-split-field-group-value', true).first();
33079     },
33080     
33081     onRender : function(ct, position) 
33082     {
33083         var _this = this;
33084         
33085         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33086         
33087         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33088         
33089         this.dayField = new Roo.bootstrap.ComboBox({
33090             allowBlank : this.dayAllowBlank,
33091             alwaysQuery : true,
33092             displayField : 'value',
33093             editable : false,
33094             fieldLabel : '',
33095             forceSelection : true,
33096             mode : 'local',
33097             placeholder : this.dayPlaceholder,
33098             selectOnFocus : true,
33099             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33100             triggerAction : 'all',
33101             typeAhead : true,
33102             valueField : 'value',
33103             store : new Roo.data.SimpleStore({
33104                 data : (function() {    
33105                     var days = [];
33106                     _this.fireEvent('days', _this, days);
33107                     return days;
33108                 })(),
33109                 fields : [ 'value' ]
33110             }),
33111             listeners : {
33112                 select : function (_self, record, index)
33113                 {
33114                     _this.setValue(_this.getValue());
33115                 }
33116             }
33117         });
33118
33119         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33120         
33121         this.monthField = new Roo.bootstrap.MonthField({
33122             after : '<i class=\"fa fa-calendar\"></i>',
33123             allowBlank : this.monthAllowBlank,
33124             placeholder : this.monthPlaceholder,
33125             readOnly : true,
33126             listeners : {
33127                 render : function (_self)
33128                 {
33129                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33130                         e.preventDefault();
33131                         _self.focus();
33132                     });
33133                 },
33134                 select : function (_self, oldvalue, newvalue)
33135                 {
33136                     _this.setValue(_this.getValue());
33137                 }
33138             }
33139         });
33140         
33141         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33142         
33143         this.yearField = new Roo.bootstrap.ComboBox({
33144             allowBlank : this.yearAllowBlank,
33145             alwaysQuery : true,
33146             displayField : 'value',
33147             editable : false,
33148             fieldLabel : '',
33149             forceSelection : true,
33150             mode : 'local',
33151             placeholder : this.yearPlaceholder,
33152             selectOnFocus : true,
33153             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33154             triggerAction : 'all',
33155             typeAhead : true,
33156             valueField : 'value',
33157             store : new Roo.data.SimpleStore({
33158                 data : (function() {
33159                     var years = [];
33160                     _this.fireEvent('years', _this, years);
33161                     return years;
33162                 })(),
33163                 fields : [ 'value' ]
33164             }),
33165             listeners : {
33166                 select : function (_self, record, index)
33167                 {
33168                     _this.setValue(_this.getValue());
33169                 }
33170             }
33171         });
33172
33173         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33174     },
33175     
33176     setValue : function(v, format)
33177     {
33178         this.inputEl.dom.value = v;
33179         
33180         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33181         
33182         var d = Date.parseDate(v, f);
33183         
33184         if(!d){
33185             this.validate();
33186             return;
33187         }
33188         
33189         this.setDay(d.format(this.dayFormat));
33190         this.setMonth(d.format(this.monthFormat));
33191         this.setYear(d.format(this.yearFormat));
33192         
33193         this.validate();
33194         
33195         return;
33196     },
33197     
33198     setDay : function(v)
33199     {
33200         this.dayField.setValue(v);
33201         this.inputEl.dom.value = this.getValue();
33202         this.validate();
33203         return;
33204     },
33205     
33206     setMonth : function(v)
33207     {
33208         this.monthField.setValue(v, true);
33209         this.inputEl.dom.value = this.getValue();
33210         this.validate();
33211         return;
33212     },
33213     
33214     setYear : function(v)
33215     {
33216         this.yearField.setValue(v);
33217         this.inputEl.dom.value = this.getValue();
33218         this.validate();
33219         return;
33220     },
33221     
33222     getDay : function()
33223     {
33224         return this.dayField.getValue();
33225     },
33226     
33227     getMonth : function()
33228     {
33229         return this.monthField.getValue();
33230     },
33231     
33232     getYear : function()
33233     {
33234         return this.yearField.getValue();
33235     },
33236     
33237     getValue : function()
33238     {
33239         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33240         
33241         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33242         
33243         return date;
33244     },
33245     
33246     reset : function()
33247     {
33248         this.setDay('');
33249         this.setMonth('');
33250         this.setYear('');
33251         this.inputEl.dom.value = '';
33252         this.validate();
33253         return;
33254     },
33255     
33256     validate : function()
33257     {
33258         var d = this.dayField.validate();
33259         var m = this.monthField.validate();
33260         var y = this.yearField.validate();
33261         
33262         var valid = true;
33263         
33264         if(
33265                 (!this.dayAllowBlank && !d) ||
33266                 (!this.monthAllowBlank && !m) ||
33267                 (!this.yearAllowBlank && !y)
33268         ){
33269             valid = false;
33270         }
33271         
33272         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33273             return valid;
33274         }
33275         
33276         if(valid){
33277             this.markValid();
33278             return valid;
33279         }
33280         
33281         this.markInvalid();
33282         
33283         return valid;
33284     },
33285     
33286     markValid : function()
33287     {
33288         
33289         var label = this.el.select('label', true).first();
33290         var icon = this.el.select('i.fa-star', true).first();
33291
33292         if(label && icon){
33293             icon.remove();
33294         }
33295         
33296         this.fireEvent('valid', this);
33297     },
33298     
33299      /**
33300      * Mark this field as invalid
33301      * @param {String} msg The validation message
33302      */
33303     markInvalid : function(msg)
33304     {
33305         
33306         var label = this.el.select('label', true).first();
33307         var icon = this.el.select('i.fa-star', true).first();
33308
33309         if(label && !icon){
33310             this.el.select('.roo-date-split-field-label', true).createChild({
33311                 tag : 'i',
33312                 cls : 'text-danger fa fa-lg fa-star',
33313                 tooltip : 'This field is required',
33314                 style : 'margin-right:5px;'
33315             }, label, true);
33316         }
33317         
33318         this.fireEvent('invalid', this, msg);
33319     },
33320     
33321     clearInvalid : function()
33322     {
33323         var label = this.el.select('label', true).first();
33324         var icon = this.el.select('i.fa-star', true).first();
33325
33326         if(label && icon){
33327             icon.remove();
33328         }
33329         
33330         this.fireEvent('valid', this);
33331     },
33332     
33333     getName: function()
33334     {
33335         return this.name;
33336     }
33337     
33338 });
33339
33340  /**
33341  *
33342  * This is based on 
33343  * http://masonry.desandro.com
33344  *
33345  * The idea is to render all the bricks based on vertical width...
33346  *
33347  * The original code extends 'outlayer' - we might need to use that....
33348  * 
33349  */
33350
33351
33352 /**
33353  * @class Roo.bootstrap.LayoutMasonry
33354  * @extends Roo.bootstrap.Component
33355  * Bootstrap Layout Masonry class
33356  * 
33357  * @constructor
33358  * Create a new Element
33359  * @param {Object} config The config object
33360  */
33361
33362 Roo.bootstrap.LayoutMasonry = function(config){
33363     
33364     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33365     
33366     this.bricks = [];
33367     
33368     Roo.bootstrap.LayoutMasonry.register(this);
33369     
33370     this.addEvents({
33371         // raw events
33372         /**
33373          * @event layout
33374          * Fire after layout the items
33375          * @param {Roo.bootstrap.LayoutMasonry} this
33376          * @param {Roo.EventObject} e
33377          */
33378         "layout" : true
33379     });
33380     
33381 };
33382
33383 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33384     
33385     /**
33386      * @cfg {Boolean} isLayoutInstant = no animation?
33387      */   
33388     isLayoutInstant : false, // needed?
33389    
33390     /**
33391      * @cfg {Number} boxWidth  width of the columns
33392      */   
33393     boxWidth : 450,
33394     
33395       /**
33396      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33397      */   
33398     boxHeight : 0,
33399     
33400     /**
33401      * @cfg {Number} padWidth padding below box..
33402      */   
33403     padWidth : 10, 
33404     
33405     /**
33406      * @cfg {Number} gutter gutter width..
33407      */   
33408     gutter : 10,
33409     
33410      /**
33411      * @cfg {Number} maxCols maximum number of columns
33412      */   
33413     
33414     maxCols: 0,
33415     
33416     /**
33417      * @cfg {Boolean} isAutoInitial defalut true
33418      */   
33419     isAutoInitial : true, 
33420     
33421     containerWidth: 0,
33422     
33423     /**
33424      * @cfg {Boolean} isHorizontal defalut false
33425      */   
33426     isHorizontal : false, 
33427
33428     currentSize : null,
33429     
33430     tag: 'div',
33431     
33432     cls: '',
33433     
33434     bricks: null, //CompositeElement
33435     
33436     cols : 1,
33437     
33438     _isLayoutInited : false,
33439     
33440 //    isAlternative : false, // only use for vertical layout...
33441     
33442     /**
33443      * @cfg {Number} alternativePadWidth padding below box..
33444      */   
33445     alternativePadWidth : 50,
33446     
33447     selectedBrick : [],
33448     
33449     getAutoCreate : function(){
33450         
33451         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33452         
33453         var cfg = {
33454             tag: this.tag,
33455             cls: 'blog-masonary-wrapper ' + this.cls,
33456             cn : {
33457                 cls : 'mas-boxes masonary'
33458             }
33459         };
33460         
33461         return cfg;
33462     },
33463     
33464     getChildContainer: function( )
33465     {
33466         if (this.boxesEl) {
33467             return this.boxesEl;
33468         }
33469         
33470         this.boxesEl = this.el.select('.mas-boxes').first();
33471         
33472         return this.boxesEl;
33473     },
33474     
33475     
33476     initEvents : function()
33477     {
33478         var _this = this;
33479         
33480         if(this.isAutoInitial){
33481             Roo.log('hook children rendered');
33482             this.on('childrenrendered', function() {
33483                 Roo.log('children rendered');
33484                 _this.initial();
33485             } ,this);
33486         }
33487     },
33488     
33489     initial : function()
33490     {
33491         this.selectedBrick = [];
33492         
33493         this.currentSize = this.el.getBox(true);
33494         
33495         Roo.EventManager.onWindowResize(this.resize, this); 
33496
33497         if(!this.isAutoInitial){
33498             this.layout();
33499             return;
33500         }
33501         
33502         this.layout();
33503         
33504         return;
33505         //this.layout.defer(500,this);
33506         
33507     },
33508     
33509     resize : function()
33510     {
33511         var cs = this.el.getBox(true);
33512         
33513         if (
33514                 this.currentSize.width == cs.width && 
33515                 this.currentSize.x == cs.x && 
33516                 this.currentSize.height == cs.height && 
33517                 this.currentSize.y == cs.y 
33518         ) {
33519             Roo.log("no change in with or X or Y");
33520             return;
33521         }
33522         
33523         this.currentSize = cs;
33524         
33525         this.layout();
33526         
33527     },
33528     
33529     layout : function()
33530     {   
33531         this._resetLayout();
33532         
33533         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33534         
33535         this.layoutItems( isInstant );
33536       
33537         this._isLayoutInited = true;
33538         
33539         this.fireEvent('layout', this);
33540         
33541     },
33542     
33543     _resetLayout : function()
33544     {
33545         if(this.isHorizontal){
33546             this.horizontalMeasureColumns();
33547             return;
33548         }
33549         
33550         this.verticalMeasureColumns();
33551         
33552     },
33553     
33554     verticalMeasureColumns : function()
33555     {
33556         this.getContainerWidth();
33557         
33558 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33559 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33560 //            return;
33561 //        }
33562         
33563         var boxWidth = this.boxWidth + this.padWidth;
33564         
33565         if(this.containerWidth < this.boxWidth){
33566             boxWidth = this.containerWidth
33567         }
33568         
33569         var containerWidth = this.containerWidth;
33570         
33571         var cols = Math.floor(containerWidth / boxWidth);
33572         
33573         this.cols = Math.max( cols, 1 );
33574         
33575         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33576         
33577         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33578         
33579         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33580         
33581         this.colWidth = boxWidth + avail - this.padWidth;
33582         
33583         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33584         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33585     },
33586     
33587     horizontalMeasureColumns : function()
33588     {
33589         this.getContainerWidth();
33590         
33591         var boxWidth = this.boxWidth;
33592         
33593         if(this.containerWidth < boxWidth){
33594             boxWidth = this.containerWidth;
33595         }
33596         
33597         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33598         
33599         this.el.setHeight(boxWidth);
33600         
33601     },
33602     
33603     getContainerWidth : function()
33604     {
33605         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33606     },
33607     
33608     layoutItems : function( isInstant )
33609     {
33610         Roo.log(this.bricks);
33611         
33612         var items = Roo.apply([], this.bricks);
33613         
33614         if(this.isHorizontal){
33615             this._horizontalLayoutItems( items , isInstant );
33616             return;
33617         }
33618         
33619 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33620 //            this._verticalAlternativeLayoutItems( items , isInstant );
33621 //            return;
33622 //        }
33623         
33624         this._verticalLayoutItems( items , isInstant );
33625         
33626     },
33627     
33628     _verticalLayoutItems : function ( items , isInstant)
33629     {
33630         if ( !items || !items.length ) {
33631             return;
33632         }
33633         
33634         var standard = [
33635             ['xs', 'xs', 'xs', 'tall'],
33636             ['xs', 'xs', 'tall'],
33637             ['xs', 'xs', 'sm'],
33638             ['xs', 'xs', 'xs'],
33639             ['xs', 'tall'],
33640             ['xs', 'sm'],
33641             ['xs', 'xs'],
33642             ['xs'],
33643             
33644             ['sm', 'xs', 'xs'],
33645             ['sm', 'xs'],
33646             ['sm'],
33647             
33648             ['tall', 'xs', 'xs', 'xs'],
33649             ['tall', 'xs', 'xs'],
33650             ['tall', 'xs'],
33651             ['tall']
33652             
33653         ];
33654         
33655         var queue = [];
33656         
33657         var boxes = [];
33658         
33659         var box = [];
33660         
33661         Roo.each(items, function(item, k){
33662             
33663             switch (item.size) {
33664                 // these layouts take up a full box,
33665                 case 'md' :
33666                 case 'md-left' :
33667                 case 'md-right' :
33668                 case 'wide' :
33669                     
33670                     if(box.length){
33671                         boxes.push(box);
33672                         box = [];
33673                     }
33674                     
33675                     boxes.push([item]);
33676                     
33677                     break;
33678                     
33679                 case 'xs' :
33680                 case 'sm' :
33681                 case 'tall' :
33682                     
33683                     box.push(item);
33684                     
33685                     break;
33686                 default :
33687                     break;
33688                     
33689             }
33690             
33691         }, this);
33692         
33693         if(box.length){
33694             boxes.push(box);
33695             box = [];
33696         }
33697         
33698         var filterPattern = function(box, length)
33699         {
33700             if(!box.length){
33701                 return;
33702             }
33703             
33704             var match = false;
33705             
33706             var pattern = box.slice(0, length);
33707             
33708             var format = [];
33709             
33710             Roo.each(pattern, function(i){
33711                 format.push(i.size);
33712             }, this);
33713             
33714             Roo.each(standard, function(s){
33715                 
33716                 if(String(s) != String(format)){
33717                     return;
33718                 }
33719                 
33720                 match = true;
33721                 return false;
33722                 
33723             }, this);
33724             
33725             if(!match && length == 1){
33726                 return;
33727             }
33728             
33729             if(!match){
33730                 filterPattern(box, length - 1);
33731                 return;
33732             }
33733                 
33734             queue.push(pattern);
33735
33736             box = box.slice(length, box.length);
33737
33738             filterPattern(box, 4);
33739
33740             return;
33741             
33742         }
33743         
33744         Roo.each(boxes, function(box, k){
33745             
33746             if(!box.length){
33747                 return;
33748             }
33749             
33750             if(box.length == 1){
33751                 queue.push(box);
33752                 return;
33753             }
33754             
33755             filterPattern(box, 4);
33756             
33757         }, this);
33758         
33759         this._processVerticalLayoutQueue( queue, isInstant );
33760         
33761     },
33762     
33763 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33764 //    {
33765 //        if ( !items || !items.length ) {
33766 //            return;
33767 //        }
33768 //
33769 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33770 //        
33771 //    },
33772     
33773     _horizontalLayoutItems : function ( items , isInstant)
33774     {
33775         if ( !items || !items.length || items.length < 3) {
33776             return;
33777         }
33778         
33779         items.reverse();
33780         
33781         var eItems = items.slice(0, 3);
33782         
33783         items = items.slice(3, items.length);
33784         
33785         var standard = [
33786             ['xs', 'xs', 'xs', 'wide'],
33787             ['xs', 'xs', 'wide'],
33788             ['xs', 'xs', 'sm'],
33789             ['xs', 'xs', 'xs'],
33790             ['xs', 'wide'],
33791             ['xs', 'sm'],
33792             ['xs', 'xs'],
33793             ['xs'],
33794             
33795             ['sm', 'xs', 'xs'],
33796             ['sm', 'xs'],
33797             ['sm'],
33798             
33799             ['wide', 'xs', 'xs', 'xs'],
33800             ['wide', 'xs', 'xs'],
33801             ['wide', 'xs'],
33802             ['wide'],
33803             
33804             ['wide-thin']
33805         ];
33806         
33807         var queue = [];
33808         
33809         var boxes = [];
33810         
33811         var box = [];
33812         
33813         Roo.each(items, function(item, k){
33814             
33815             switch (item.size) {
33816                 case 'md' :
33817                 case 'md-left' :
33818                 case 'md-right' :
33819                 case 'tall' :
33820                     
33821                     if(box.length){
33822                         boxes.push(box);
33823                         box = [];
33824                     }
33825                     
33826                     boxes.push([item]);
33827                     
33828                     break;
33829                     
33830                 case 'xs' :
33831                 case 'sm' :
33832                 case 'wide' :
33833                 case 'wide-thin' :
33834                     
33835                     box.push(item);
33836                     
33837                     break;
33838                 default :
33839                     break;
33840                     
33841             }
33842             
33843         }, this);
33844         
33845         if(box.length){
33846             boxes.push(box);
33847             box = [];
33848         }
33849         
33850         var filterPattern = function(box, length)
33851         {
33852             if(!box.length){
33853                 return;
33854             }
33855             
33856             var match = false;
33857             
33858             var pattern = box.slice(0, length);
33859             
33860             var format = [];
33861             
33862             Roo.each(pattern, function(i){
33863                 format.push(i.size);
33864             }, this);
33865             
33866             Roo.each(standard, function(s){
33867                 
33868                 if(String(s) != String(format)){
33869                     return;
33870                 }
33871                 
33872                 match = true;
33873                 return false;
33874                 
33875             }, this);
33876             
33877             if(!match && length == 1){
33878                 return;
33879             }
33880             
33881             if(!match){
33882                 filterPattern(box, length - 1);
33883                 return;
33884             }
33885                 
33886             queue.push(pattern);
33887
33888             box = box.slice(length, box.length);
33889
33890             filterPattern(box, 4);
33891
33892             return;
33893             
33894         }
33895         
33896         Roo.each(boxes, function(box, k){
33897             
33898             if(!box.length){
33899                 return;
33900             }
33901             
33902             if(box.length == 1){
33903                 queue.push(box);
33904                 return;
33905             }
33906             
33907             filterPattern(box, 4);
33908             
33909         }, this);
33910         
33911         
33912         var prune = [];
33913         
33914         var pos = this.el.getBox(true);
33915         
33916         var minX = pos.x;
33917         
33918         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33919         
33920         var hit_end = false;
33921         
33922         Roo.each(queue, function(box){
33923             
33924             if(hit_end){
33925                 
33926                 Roo.each(box, function(b){
33927                 
33928                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33929                     b.el.hide();
33930
33931                 }, this);
33932
33933                 return;
33934             }
33935             
33936             var mx = 0;
33937             
33938             Roo.each(box, function(b){
33939                 
33940                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33941                 b.el.show();
33942
33943                 mx = Math.max(mx, b.x);
33944                 
33945             }, this);
33946             
33947             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33948             
33949             if(maxX < minX){
33950                 
33951                 Roo.each(box, function(b){
33952                 
33953                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33954                     b.el.hide();
33955                     
33956                 }, this);
33957                 
33958                 hit_end = true;
33959                 
33960                 return;
33961             }
33962             
33963             prune.push(box);
33964             
33965         }, this);
33966         
33967         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33968     },
33969     
33970     /** Sets position of item in DOM
33971     * @param {Element} item
33972     * @param {Number} x - horizontal position
33973     * @param {Number} y - vertical position
33974     * @param {Boolean} isInstant - disables transitions
33975     */
33976     _processVerticalLayoutQueue : function( queue, isInstant )
33977     {
33978         var pos = this.el.getBox(true);
33979         var x = pos.x;
33980         var y = pos.y;
33981         var maxY = [];
33982         
33983         for (var i = 0; i < this.cols; i++){
33984             maxY[i] = pos.y;
33985         }
33986         
33987         Roo.each(queue, function(box, k){
33988             
33989             var col = k % this.cols;
33990             
33991             Roo.each(box, function(b,kk){
33992                 
33993                 b.el.position('absolute');
33994                 
33995                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33996                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33997                 
33998                 if(b.size == 'md-left' || b.size == 'md-right'){
33999                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34000                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34001                 }
34002                 
34003                 b.el.setWidth(width);
34004                 b.el.setHeight(height);
34005                 // iframe?
34006                 b.el.select('iframe',true).setSize(width,height);
34007                 
34008             }, this);
34009             
34010             for (var i = 0; i < this.cols; i++){
34011                 
34012                 if(maxY[i] < maxY[col]){
34013                     col = i;
34014                     continue;
34015                 }
34016                 
34017                 col = Math.min(col, i);
34018                 
34019             }
34020             
34021             x = pos.x + col * (this.colWidth + this.padWidth);
34022             
34023             y = maxY[col];
34024             
34025             var positions = [];
34026             
34027             switch (box.length){
34028                 case 1 :
34029                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34030                     break;
34031                 case 2 :
34032                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34033                     break;
34034                 case 3 :
34035                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34036                     break;
34037                 case 4 :
34038                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34039                     break;
34040                 default :
34041                     break;
34042             }
34043             
34044             Roo.each(box, function(b,kk){
34045                 
34046                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34047                 
34048                 var sz = b.el.getSize();
34049                 
34050                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34051                 
34052             }, this);
34053             
34054         }, this);
34055         
34056         var mY = 0;
34057         
34058         for (var i = 0; i < this.cols; i++){
34059             mY = Math.max(mY, maxY[i]);
34060         }
34061         
34062         this.el.setHeight(mY - pos.y);
34063         
34064     },
34065     
34066 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34067 //    {
34068 //        var pos = this.el.getBox(true);
34069 //        var x = pos.x;
34070 //        var y = pos.y;
34071 //        var maxX = pos.right;
34072 //        
34073 //        var maxHeight = 0;
34074 //        
34075 //        Roo.each(items, function(item, k){
34076 //            
34077 //            var c = k % 2;
34078 //            
34079 //            item.el.position('absolute');
34080 //                
34081 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34082 //
34083 //            item.el.setWidth(width);
34084 //
34085 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34086 //
34087 //            item.el.setHeight(height);
34088 //            
34089 //            if(c == 0){
34090 //                item.el.setXY([x, y], isInstant ? false : true);
34091 //            } else {
34092 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34093 //            }
34094 //            
34095 //            y = y + height + this.alternativePadWidth;
34096 //            
34097 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34098 //            
34099 //        }, this);
34100 //        
34101 //        this.el.setHeight(maxHeight);
34102 //        
34103 //    },
34104     
34105     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34106     {
34107         var pos = this.el.getBox(true);
34108         
34109         var minX = pos.x;
34110         var minY = pos.y;
34111         
34112         var maxX = pos.right;
34113         
34114         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34115         
34116         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34117         
34118         Roo.each(queue, function(box, k){
34119             
34120             Roo.each(box, function(b, kk){
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                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34126                 
34127                 if(b.size == 'md-left' || b.size == 'md-right'){
34128                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34129                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34130                 }
34131                 
34132                 b.el.setWidth(width);
34133                 b.el.setHeight(height);
34134                 
34135             }, this);
34136             
34137             if(!box.length){
34138                 return;
34139             }
34140             
34141             var positions = [];
34142             
34143             switch (box.length){
34144                 case 1 :
34145                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34146                     break;
34147                 case 2 :
34148                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34149                     break;
34150                 case 3 :
34151                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34152                     break;
34153                 case 4 :
34154                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34155                     break;
34156                 default :
34157                     break;
34158             }
34159             
34160             Roo.each(box, function(b,kk){
34161                 
34162                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34163                 
34164                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34165                 
34166             }, this);
34167             
34168         }, this);
34169         
34170     },
34171     
34172     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34173     {
34174         Roo.each(eItems, function(b,k){
34175             
34176             b.size = (k == 0) ? 'sm' : 'xs';
34177             b.x = (k == 0) ? 2 : 1;
34178             b.y = (k == 0) ? 2 : 1;
34179             
34180             b.el.position('absolute');
34181             
34182             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34183                 
34184             b.el.setWidth(width);
34185             
34186             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34187             
34188             b.el.setHeight(height);
34189             
34190         }, this);
34191
34192         var positions = [];
34193         
34194         positions.push({
34195             x : maxX - this.unitWidth * 2 - this.gutter,
34196             y : minY
34197         });
34198         
34199         positions.push({
34200             x : maxX - this.unitWidth,
34201             y : minY + (this.unitWidth + this.gutter) * 2
34202         });
34203         
34204         positions.push({
34205             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34206             y : minY
34207         });
34208         
34209         Roo.each(eItems, function(b,k){
34210             
34211             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34212
34213         }, this);
34214         
34215     },
34216     
34217     getVerticalOneBoxColPositions : function(x, y, box)
34218     {
34219         var pos = [];
34220         
34221         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34222         
34223         if(box[0].size == 'md-left'){
34224             rand = 0;
34225         }
34226         
34227         if(box[0].size == 'md-right'){
34228             rand = 1;
34229         }
34230         
34231         pos.push({
34232             x : x + (this.unitWidth + this.gutter) * rand,
34233             y : y
34234         });
34235         
34236         return pos;
34237     },
34238     
34239     getVerticalTwoBoxColPositions : function(x, y, box)
34240     {
34241         var pos = [];
34242         
34243         if(box[0].size == 'xs'){
34244             
34245             pos.push({
34246                 x : x,
34247                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34248             });
34249
34250             pos.push({
34251                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34252                 y : y
34253             });
34254             
34255             return pos;
34256             
34257         }
34258         
34259         pos.push({
34260             x : x,
34261             y : y
34262         });
34263
34264         pos.push({
34265             x : x + (this.unitWidth + this.gutter) * 2,
34266             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34267         });
34268         
34269         return pos;
34270         
34271     },
34272     
34273     getVerticalThreeBoxColPositions : function(x, y, box)
34274     {
34275         var pos = [];
34276         
34277         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34278             
34279             pos.push({
34280                 x : x,
34281                 y : y
34282             });
34283
34284             pos.push({
34285                 x : x + (this.unitWidth + this.gutter) * 1,
34286                 y : y
34287             });
34288             
34289             pos.push({
34290                 x : x + (this.unitWidth + this.gutter) * 2,
34291                 y : y
34292             });
34293             
34294             return pos;
34295             
34296         }
34297         
34298         if(box[0].size == 'xs' && box[1].size == 'xs'){
34299             
34300             pos.push({
34301                 x : x,
34302                 y : y
34303             });
34304
34305             pos.push({
34306                 x : x,
34307                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34308             });
34309             
34310             pos.push({
34311                 x : x + (this.unitWidth + this.gutter) * 1,
34312                 y : y
34313             });
34314             
34315             return pos;
34316             
34317         }
34318         
34319         pos.push({
34320             x : x,
34321             y : y
34322         });
34323
34324         pos.push({
34325             x : x + (this.unitWidth + this.gutter) * 2,
34326             y : y
34327         });
34328
34329         pos.push({
34330             x : x + (this.unitWidth + this.gutter) * 2,
34331             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34332         });
34333             
34334         return pos;
34335         
34336     },
34337     
34338     getVerticalFourBoxColPositions : function(x, y, box)
34339     {
34340         var pos = [];
34341         
34342         if(box[0].size == 'xs'){
34343             
34344             pos.push({
34345                 x : x,
34346                 y : y
34347             });
34348
34349             pos.push({
34350                 x : x,
34351                 y : y + (this.unitHeight + this.gutter) * 1
34352             });
34353             
34354             pos.push({
34355                 x : x,
34356                 y : y + (this.unitHeight + this.gutter) * 2
34357             });
34358             
34359             pos.push({
34360                 x : x + (this.unitWidth + this.gutter) * 1,
34361                 y : y
34362             });
34363             
34364             return pos;
34365             
34366         }
34367         
34368         pos.push({
34369             x : x,
34370             y : y
34371         });
34372
34373         pos.push({
34374             x : x + (this.unitWidth + this.gutter) * 2,
34375             y : y
34376         });
34377
34378         pos.push({
34379             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34380             y : y + (this.unitHeight + this.gutter) * 1
34381         });
34382
34383         pos.push({
34384             x : x + (this.unitWidth + this.gutter) * 2,
34385             y : y + (this.unitWidth + this.gutter) * 2
34386         });
34387
34388         return pos;
34389         
34390     },
34391     
34392     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34393     {
34394         var pos = [];
34395         
34396         if(box[0].size == 'md-left'){
34397             pos.push({
34398                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34399                 y : minY
34400             });
34401             
34402             return pos;
34403         }
34404         
34405         if(box[0].size == 'md-right'){
34406             pos.push({
34407                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34408                 y : minY + (this.unitWidth + this.gutter) * 1
34409             });
34410             
34411             return pos;
34412         }
34413         
34414         var rand = Math.floor(Math.random() * (4 - box[0].y));
34415         
34416         pos.push({
34417             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34418             y : minY + (this.unitWidth + this.gutter) * rand
34419         });
34420         
34421         return pos;
34422         
34423     },
34424     
34425     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34426     {
34427         var pos = [];
34428         
34429         if(box[0].size == 'xs'){
34430             
34431             pos.push({
34432                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34433                 y : minY
34434             });
34435
34436             pos.push({
34437                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34438                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34439             });
34440             
34441             return pos;
34442             
34443         }
34444         
34445         pos.push({
34446             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34447             y : minY
34448         });
34449
34450         pos.push({
34451             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34452             y : minY + (this.unitWidth + this.gutter) * 2
34453         });
34454         
34455         return pos;
34456         
34457     },
34458     
34459     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34460     {
34461         var pos = [];
34462         
34463         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34464             
34465             pos.push({
34466                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34467                 y : minY
34468             });
34469
34470             pos.push({
34471                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34472                 y : minY + (this.unitWidth + this.gutter) * 1
34473             });
34474             
34475             pos.push({
34476                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34477                 y : minY + (this.unitWidth + this.gutter) * 2
34478             });
34479             
34480             return pos;
34481             
34482         }
34483         
34484         if(box[0].size == 'xs' && box[1].size == 'xs'){
34485             
34486             pos.push({
34487                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34488                 y : minY
34489             });
34490
34491             pos.push({
34492                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34493                 y : minY
34494             });
34495             
34496             pos.push({
34497                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34498                 y : minY + (this.unitWidth + this.gutter) * 1
34499             });
34500             
34501             return pos;
34502             
34503         }
34504         
34505         pos.push({
34506             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34507             y : minY
34508         });
34509
34510         pos.push({
34511             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34512             y : minY + (this.unitWidth + this.gutter) * 2
34513         });
34514
34515         pos.push({
34516             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34517             y : minY + (this.unitWidth + this.gutter) * 2
34518         });
34519             
34520         return pos;
34521         
34522     },
34523     
34524     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34525     {
34526         var pos = [];
34527         
34528         if(box[0].size == 'xs'){
34529             
34530             pos.push({
34531                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34532                 y : minY
34533             });
34534
34535             pos.push({
34536                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34537                 y : minY
34538             });
34539             
34540             pos.push({
34541                 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),
34542                 y : minY
34543             });
34544             
34545             pos.push({
34546                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34547                 y : minY + (this.unitWidth + this.gutter) * 1
34548             });
34549             
34550             return pos;
34551             
34552         }
34553         
34554         pos.push({
34555             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34556             y : minY
34557         });
34558         
34559         pos.push({
34560             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34561             y : minY + (this.unitWidth + this.gutter) * 2
34562         });
34563         
34564         pos.push({
34565             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34566             y : minY + (this.unitWidth + this.gutter) * 2
34567         });
34568         
34569         pos.push({
34570             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),
34571             y : minY + (this.unitWidth + this.gutter) * 2
34572         });
34573
34574         return pos;
34575         
34576     },
34577     
34578     /**
34579     * remove a Masonry Brick
34580     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34581     */
34582     removeBrick : function(brick_id)
34583     {
34584         if (!brick_id) {
34585             return;
34586         }
34587         
34588         for (var i = 0; i<this.bricks.length; i++) {
34589             if (this.bricks[i].id == brick_id) {
34590                 this.bricks.splice(i,1);
34591                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34592                 this.initial();
34593             }
34594         }
34595     },
34596     
34597     /**
34598     * adds a Masonry Brick
34599     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34600     */
34601     addBrick : function(cfg)
34602     {
34603         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34604         //this.register(cn);
34605         cn.parentId = this.id;
34606         cn.render(this.el);
34607         return cn;
34608     },
34609     
34610     /**
34611     * register a Masonry Brick
34612     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34613     */
34614     
34615     register : function(brick)
34616     {
34617         this.bricks.push(brick);
34618         brick.masonryId = this.id;
34619     },
34620     
34621     /**
34622     * clear all the Masonry Brick
34623     */
34624     clearAll : function()
34625     {
34626         this.bricks = [];
34627         //this.getChildContainer().dom.innerHTML = "";
34628         this.el.dom.innerHTML = '';
34629     },
34630     
34631     getSelected : function()
34632     {
34633         if (!this.selectedBrick) {
34634             return false;
34635         }
34636         
34637         return this.selectedBrick;
34638     }
34639 });
34640
34641 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34642     
34643     groups: {},
34644      /**
34645     * register a Masonry Layout
34646     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34647     */
34648     
34649     register : function(layout)
34650     {
34651         this.groups[layout.id] = layout;
34652     },
34653     /**
34654     * fetch a  Masonry Layout based on the masonry layout ID
34655     * @param {string} the masonry layout to add
34656     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34657     */
34658     
34659     get: function(layout_id) {
34660         if (typeof(this.groups[layout_id]) == 'undefined') {
34661             return false;
34662         }
34663         return this.groups[layout_id] ;
34664     }
34665     
34666     
34667     
34668 });
34669
34670  
34671
34672  /**
34673  *
34674  * This is based on 
34675  * http://masonry.desandro.com
34676  *
34677  * The idea is to render all the bricks based on vertical width...
34678  *
34679  * The original code extends 'outlayer' - we might need to use that....
34680  * 
34681  */
34682
34683
34684 /**
34685  * @class Roo.bootstrap.LayoutMasonryAuto
34686  * @extends Roo.bootstrap.Component
34687  * Bootstrap Layout Masonry class
34688  * 
34689  * @constructor
34690  * Create a new Element
34691  * @param {Object} config The config object
34692  */
34693
34694 Roo.bootstrap.LayoutMasonryAuto = function(config){
34695     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34696 };
34697
34698 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34699     
34700       /**
34701      * @cfg {Boolean} isFitWidth  - resize the width..
34702      */   
34703     isFitWidth : false,  // options..
34704     /**
34705      * @cfg {Boolean} isOriginLeft = left align?
34706      */   
34707     isOriginLeft : true,
34708     /**
34709      * @cfg {Boolean} isOriginTop = top align?
34710      */   
34711     isOriginTop : false,
34712     /**
34713      * @cfg {Boolean} isLayoutInstant = no animation?
34714      */   
34715     isLayoutInstant : false, // needed?
34716     /**
34717      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34718      */   
34719     isResizingContainer : true,
34720     /**
34721      * @cfg {Number} columnWidth  width of the columns 
34722      */   
34723     
34724     columnWidth : 0,
34725     
34726     /**
34727      * @cfg {Number} maxCols maximum number of columns
34728      */   
34729     
34730     maxCols: 0,
34731     /**
34732      * @cfg {Number} padHeight padding below box..
34733      */   
34734     
34735     padHeight : 10, 
34736     
34737     /**
34738      * @cfg {Boolean} isAutoInitial defalut true
34739      */   
34740     
34741     isAutoInitial : true, 
34742     
34743     // private?
34744     gutter : 0,
34745     
34746     containerWidth: 0,
34747     initialColumnWidth : 0,
34748     currentSize : null,
34749     
34750     colYs : null, // array.
34751     maxY : 0,
34752     padWidth: 10,
34753     
34754     
34755     tag: 'div',
34756     cls: '',
34757     bricks: null, //CompositeElement
34758     cols : 0, // array?
34759     // element : null, // wrapped now this.el
34760     _isLayoutInited : null, 
34761     
34762     
34763     getAutoCreate : function(){
34764         
34765         var cfg = {
34766             tag: this.tag,
34767             cls: 'blog-masonary-wrapper ' + this.cls,
34768             cn : {
34769                 cls : 'mas-boxes masonary'
34770             }
34771         };
34772         
34773         return cfg;
34774     },
34775     
34776     getChildContainer: function( )
34777     {
34778         if (this.boxesEl) {
34779             return this.boxesEl;
34780         }
34781         
34782         this.boxesEl = this.el.select('.mas-boxes').first();
34783         
34784         return this.boxesEl;
34785     },
34786     
34787     
34788     initEvents : function()
34789     {
34790         var _this = this;
34791         
34792         if(this.isAutoInitial){
34793             Roo.log('hook children rendered');
34794             this.on('childrenrendered', function() {
34795                 Roo.log('children rendered');
34796                 _this.initial();
34797             } ,this);
34798         }
34799         
34800     },
34801     
34802     initial : function()
34803     {
34804         this.reloadItems();
34805
34806         this.currentSize = this.el.getBox(true);
34807
34808         /// was window resize... - let's see if this works..
34809         Roo.EventManager.onWindowResize(this.resize, this); 
34810
34811         if(!this.isAutoInitial){
34812             this.layout();
34813             return;
34814         }
34815         
34816         this.layout.defer(500,this);
34817     },
34818     
34819     reloadItems: function()
34820     {
34821         this.bricks = this.el.select('.masonry-brick', true);
34822         
34823         this.bricks.each(function(b) {
34824             //Roo.log(b.getSize());
34825             if (!b.attr('originalwidth')) {
34826                 b.attr('originalwidth',  b.getSize().width);
34827             }
34828             
34829         });
34830         
34831         Roo.log(this.bricks.elements.length);
34832     },
34833     
34834     resize : function()
34835     {
34836         Roo.log('resize');
34837         var cs = this.el.getBox(true);
34838         
34839         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34840             Roo.log("no change in with or X");
34841             return;
34842         }
34843         this.currentSize = cs;
34844         this.layout();
34845     },
34846     
34847     layout : function()
34848     {
34849          Roo.log('layout');
34850         this._resetLayout();
34851         //this._manageStamps();
34852       
34853         // don't animate first layout
34854         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34855         this.layoutItems( isInstant );
34856       
34857         // flag for initalized
34858         this._isLayoutInited = true;
34859     },
34860     
34861     layoutItems : function( isInstant )
34862     {
34863         //var items = this._getItemsForLayout( this.items );
34864         // original code supports filtering layout items.. we just ignore it..
34865         
34866         this._layoutItems( this.bricks , isInstant );
34867       
34868         this._postLayout();
34869     },
34870     _layoutItems : function ( items , isInstant)
34871     {
34872        //this.fireEvent( 'layout', this, items );
34873     
34874
34875         if ( !items || !items.elements.length ) {
34876           // no items, emit event with empty array
34877             return;
34878         }
34879
34880         var queue = [];
34881         items.each(function(item) {
34882             Roo.log("layout item");
34883             Roo.log(item);
34884             // get x/y object from method
34885             var position = this._getItemLayoutPosition( item );
34886             // enqueue
34887             position.item = item;
34888             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34889             queue.push( position );
34890         }, this);
34891       
34892         this._processLayoutQueue( queue );
34893     },
34894     /** Sets position of item in DOM
34895     * @param {Element} item
34896     * @param {Number} x - horizontal position
34897     * @param {Number} y - vertical position
34898     * @param {Boolean} isInstant - disables transitions
34899     */
34900     _processLayoutQueue : function( queue )
34901     {
34902         for ( var i=0, len = queue.length; i < len; i++ ) {
34903             var obj = queue[i];
34904             obj.item.position('absolute');
34905             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34906         }
34907     },
34908       
34909     
34910     /**
34911     * Any logic you want to do after each layout,
34912     * i.e. size the container
34913     */
34914     _postLayout : function()
34915     {
34916         this.resizeContainer();
34917     },
34918     
34919     resizeContainer : function()
34920     {
34921         if ( !this.isResizingContainer ) {
34922             return;
34923         }
34924         var size = this._getContainerSize();
34925         if ( size ) {
34926             this.el.setSize(size.width,size.height);
34927             this.boxesEl.setSize(size.width,size.height);
34928         }
34929     },
34930     
34931     
34932     
34933     _resetLayout : function()
34934     {
34935         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34936         this.colWidth = this.el.getWidth();
34937         //this.gutter = this.el.getWidth(); 
34938         
34939         this.measureColumns();
34940
34941         // reset column Y
34942         var i = this.cols;
34943         this.colYs = [];
34944         while (i--) {
34945             this.colYs.push( 0 );
34946         }
34947     
34948         this.maxY = 0;
34949     },
34950
34951     measureColumns : function()
34952     {
34953         this.getContainerWidth();
34954       // if columnWidth is 0, default to outerWidth of first item
34955         if ( !this.columnWidth ) {
34956             var firstItem = this.bricks.first();
34957             Roo.log(firstItem);
34958             this.columnWidth  = this.containerWidth;
34959             if (firstItem && firstItem.attr('originalwidth') ) {
34960                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34961             }
34962             // columnWidth fall back to item of first element
34963             Roo.log("set column width?");
34964                         this.initialColumnWidth = this.columnWidth  ;
34965
34966             // if first elem has no width, default to size of container
34967             
34968         }
34969         
34970         
34971         if (this.initialColumnWidth) {
34972             this.columnWidth = this.initialColumnWidth;
34973         }
34974         
34975         
34976             
34977         // column width is fixed at the top - however if container width get's smaller we should
34978         // reduce it...
34979         
34980         // this bit calcs how man columns..
34981             
34982         var columnWidth = this.columnWidth += this.gutter;
34983       
34984         // calculate columns
34985         var containerWidth = this.containerWidth + this.gutter;
34986         
34987         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34988         // fix rounding errors, typically with gutters
34989         var excess = columnWidth - containerWidth % columnWidth;
34990         
34991         
34992         // if overshoot is less than a pixel, round up, otherwise floor it
34993         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34994         cols = Math[ mathMethod ]( cols );
34995         this.cols = Math.max( cols, 1 );
34996         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34997         
34998          // padding positioning..
34999         var totalColWidth = this.cols * this.columnWidth;
35000         var padavail = this.containerWidth - totalColWidth;
35001         // so for 2 columns - we need 3 'pads'
35002         
35003         var padNeeded = (1+this.cols) * this.padWidth;
35004         
35005         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35006         
35007         this.columnWidth += padExtra
35008         //this.padWidth = Math.floor(padavail /  ( this.cols));
35009         
35010         // adjust colum width so that padding is fixed??
35011         
35012         // we have 3 columns ... total = width * 3
35013         // we have X left over... that should be used by 
35014         
35015         //if (this.expandC) {
35016             
35017         //}
35018         
35019         
35020         
35021     },
35022     
35023     getContainerWidth : function()
35024     {
35025        /* // container is parent if fit width
35026         var container = this.isFitWidth ? this.element.parentNode : this.element;
35027         // check that this.size and size are there
35028         // IE8 triggers resize on body size change, so they might not be
35029         
35030         var size = getSize( container );  //FIXME
35031         this.containerWidth = size && size.innerWidth; //FIXME
35032         */
35033          
35034         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35035         
35036     },
35037     
35038     _getItemLayoutPosition : function( item )  // what is item?
35039     {
35040         // we resize the item to our columnWidth..
35041       
35042         item.setWidth(this.columnWidth);
35043         item.autoBoxAdjust  = false;
35044         
35045         var sz = item.getSize();
35046  
35047         // how many columns does this brick span
35048         var remainder = this.containerWidth % this.columnWidth;
35049         
35050         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35051         // round if off by 1 pixel, otherwise use ceil
35052         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35053         colSpan = Math.min( colSpan, this.cols );
35054         
35055         // normally this should be '1' as we dont' currently allow multi width columns..
35056         
35057         var colGroup = this._getColGroup( colSpan );
35058         // get the minimum Y value from the columns
35059         var minimumY = Math.min.apply( Math, colGroup );
35060         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35061         
35062         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35063          
35064         // position the brick
35065         var position = {
35066             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35067             y: this.currentSize.y + minimumY + this.padHeight
35068         };
35069         
35070         Roo.log(position);
35071         // apply setHeight to necessary columns
35072         var setHeight = minimumY + sz.height + this.padHeight;
35073         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35074         
35075         var setSpan = this.cols + 1 - colGroup.length;
35076         for ( var i = 0; i < setSpan; i++ ) {
35077           this.colYs[ shortColIndex + i ] = setHeight ;
35078         }
35079       
35080         return position;
35081     },
35082     
35083     /**
35084      * @param {Number} colSpan - number of columns the element spans
35085      * @returns {Array} colGroup
35086      */
35087     _getColGroup : function( colSpan )
35088     {
35089         if ( colSpan < 2 ) {
35090           // if brick spans only one column, use all the column Ys
35091           return this.colYs;
35092         }
35093       
35094         var colGroup = [];
35095         // how many different places could this brick fit horizontally
35096         var groupCount = this.cols + 1 - colSpan;
35097         // for each group potential horizontal position
35098         for ( var i = 0; i < groupCount; i++ ) {
35099           // make an array of colY values for that one group
35100           var groupColYs = this.colYs.slice( i, i + colSpan );
35101           // and get the max value of the array
35102           colGroup[i] = Math.max.apply( Math, groupColYs );
35103         }
35104         return colGroup;
35105     },
35106     /*
35107     _manageStamp : function( stamp )
35108     {
35109         var stampSize =  stamp.getSize();
35110         var offset = stamp.getBox();
35111         // get the columns that this stamp affects
35112         var firstX = this.isOriginLeft ? offset.x : offset.right;
35113         var lastX = firstX + stampSize.width;
35114         var firstCol = Math.floor( firstX / this.columnWidth );
35115         firstCol = Math.max( 0, firstCol );
35116         
35117         var lastCol = Math.floor( lastX / this.columnWidth );
35118         // lastCol should not go over if multiple of columnWidth #425
35119         lastCol -= lastX % this.columnWidth ? 0 : 1;
35120         lastCol = Math.min( this.cols - 1, lastCol );
35121         
35122         // set colYs to bottom of the stamp
35123         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35124             stampSize.height;
35125             
35126         for ( var i = firstCol; i <= lastCol; i++ ) {
35127           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35128         }
35129     },
35130     */
35131     
35132     _getContainerSize : function()
35133     {
35134         this.maxY = Math.max.apply( Math, this.colYs );
35135         var size = {
35136             height: this.maxY
35137         };
35138       
35139         if ( this.isFitWidth ) {
35140             size.width = this._getContainerFitWidth();
35141         }
35142       
35143         return size;
35144     },
35145     
35146     _getContainerFitWidth : function()
35147     {
35148         var unusedCols = 0;
35149         // count unused columns
35150         var i = this.cols;
35151         while ( --i ) {
35152           if ( this.colYs[i] !== 0 ) {
35153             break;
35154           }
35155           unusedCols++;
35156         }
35157         // fit container to columns that have been used
35158         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35159     },
35160     
35161     needsResizeLayout : function()
35162     {
35163         var previousWidth = this.containerWidth;
35164         this.getContainerWidth();
35165         return previousWidth !== this.containerWidth;
35166     }
35167  
35168 });
35169
35170  
35171
35172  /*
35173  * - LGPL
35174  *
35175  * element
35176  * 
35177  */
35178
35179 /**
35180  * @class Roo.bootstrap.MasonryBrick
35181  * @extends Roo.bootstrap.Component
35182  * Bootstrap MasonryBrick class
35183  * 
35184  * @constructor
35185  * Create a new MasonryBrick
35186  * @param {Object} config The config object
35187  */
35188
35189 Roo.bootstrap.MasonryBrick = function(config){
35190     
35191     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35192     
35193     Roo.bootstrap.MasonryBrick.register(this);
35194     
35195     this.addEvents({
35196         // raw events
35197         /**
35198          * @event click
35199          * When a MasonryBrick is clcik
35200          * @param {Roo.bootstrap.MasonryBrick} this
35201          * @param {Roo.EventObject} e
35202          */
35203         "click" : true
35204     });
35205 };
35206
35207 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35208     
35209     /**
35210      * @cfg {String} title
35211      */   
35212     title : '',
35213     /**
35214      * @cfg {String} html
35215      */   
35216     html : '',
35217     /**
35218      * @cfg {String} bgimage
35219      */   
35220     bgimage : '',
35221     /**
35222      * @cfg {String} videourl
35223      */   
35224     videourl : '',
35225     /**
35226      * @cfg {String} cls
35227      */   
35228     cls : '',
35229     /**
35230      * @cfg {String} href
35231      */   
35232     href : '',
35233     /**
35234      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35235      */   
35236     size : 'xs',
35237     
35238     /**
35239      * @cfg {String} placetitle (center|bottom)
35240      */   
35241     placetitle : '',
35242     
35243     /**
35244      * @cfg {Boolean} isFitContainer defalut true
35245      */   
35246     isFitContainer : true, 
35247     
35248     /**
35249      * @cfg {Boolean} preventDefault defalut false
35250      */   
35251     preventDefault : false, 
35252     
35253     /**
35254      * @cfg {Boolean} inverse defalut false
35255      */   
35256     maskInverse : false, 
35257     
35258     getAutoCreate : function()
35259     {
35260         if(!this.isFitContainer){
35261             return this.getSplitAutoCreate();
35262         }
35263         
35264         var cls = 'masonry-brick masonry-brick-full';
35265         
35266         if(this.href.length){
35267             cls += ' masonry-brick-link';
35268         }
35269         
35270         if(this.bgimage.length){
35271             cls += ' masonry-brick-image';
35272         }
35273         
35274         if(this.maskInverse){
35275             cls += ' mask-inverse';
35276         }
35277         
35278         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35279             cls += ' enable-mask';
35280         }
35281         
35282         if(this.size){
35283             cls += ' masonry-' + this.size + '-brick';
35284         }
35285         
35286         if(this.placetitle.length){
35287             
35288             switch (this.placetitle) {
35289                 case 'center' :
35290                     cls += ' masonry-center-title';
35291                     break;
35292                 case 'bottom' :
35293                     cls += ' masonry-bottom-title';
35294                     break;
35295                 default:
35296                     break;
35297             }
35298             
35299         } else {
35300             if(!this.html.length && !this.bgimage.length){
35301                 cls += ' masonry-center-title';
35302             }
35303
35304             if(!this.html.length && this.bgimage.length){
35305                 cls += ' masonry-bottom-title';
35306             }
35307         }
35308         
35309         if(this.cls){
35310             cls += ' ' + this.cls;
35311         }
35312         
35313         var cfg = {
35314             tag: (this.href.length) ? 'a' : 'div',
35315             cls: cls,
35316             cn: [
35317                 {
35318                     tag: 'div',
35319                     cls: 'masonry-brick-mask'
35320                 },
35321                 {
35322                     tag: 'div',
35323                     cls: 'masonry-brick-paragraph',
35324                     cn: []
35325                 }
35326             ]
35327         };
35328         
35329         if(this.href.length){
35330             cfg.href = this.href;
35331         }
35332         
35333         var cn = cfg.cn[1].cn;
35334         
35335         if(this.title.length){
35336             cn.push({
35337                 tag: 'h4',
35338                 cls: 'masonry-brick-title',
35339                 html: this.title
35340             });
35341         }
35342         
35343         if(this.html.length){
35344             cn.push({
35345                 tag: 'p',
35346                 cls: 'masonry-brick-text',
35347                 html: this.html
35348             });
35349         }
35350         
35351         if (!this.title.length && !this.html.length) {
35352             cfg.cn[1].cls += ' hide';
35353         }
35354         
35355         if(this.bgimage.length){
35356             cfg.cn.push({
35357                 tag: 'img',
35358                 cls: 'masonry-brick-image-view',
35359                 src: this.bgimage
35360             });
35361         }
35362         
35363         if(this.videourl.length){
35364             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35365             // youtube support only?
35366             cfg.cn.push({
35367                 tag: 'iframe',
35368                 cls: 'masonry-brick-image-view',
35369                 src: vurl,
35370                 frameborder : 0,
35371                 allowfullscreen : true
35372             });
35373         }
35374         
35375         return cfg;
35376         
35377     },
35378     
35379     getSplitAutoCreate : function()
35380     {
35381         var cls = 'masonry-brick masonry-brick-split';
35382         
35383         if(this.href.length){
35384             cls += ' masonry-brick-link';
35385         }
35386         
35387         if(this.bgimage.length){
35388             cls += ' masonry-brick-image';
35389         }
35390         
35391         if(this.size){
35392             cls += ' masonry-' + this.size + '-brick';
35393         }
35394         
35395         switch (this.placetitle) {
35396             case 'center' :
35397                 cls += ' masonry-center-title';
35398                 break;
35399             case 'bottom' :
35400                 cls += ' masonry-bottom-title';
35401                 break;
35402             default:
35403                 if(!this.bgimage.length){
35404                     cls += ' masonry-center-title';
35405                 }
35406
35407                 if(this.bgimage.length){
35408                     cls += ' masonry-bottom-title';
35409                 }
35410                 break;
35411         }
35412         
35413         if(this.cls){
35414             cls += ' ' + this.cls;
35415         }
35416         
35417         var cfg = {
35418             tag: (this.href.length) ? 'a' : 'div',
35419             cls: cls,
35420             cn: [
35421                 {
35422                     tag: 'div',
35423                     cls: 'masonry-brick-split-head',
35424                     cn: [
35425                         {
35426                             tag: 'div',
35427                             cls: 'masonry-brick-paragraph',
35428                             cn: []
35429                         }
35430                     ]
35431                 },
35432                 {
35433                     tag: 'div',
35434                     cls: 'masonry-brick-split-body',
35435                     cn: []
35436                 }
35437             ]
35438         };
35439         
35440         if(this.href.length){
35441             cfg.href = this.href;
35442         }
35443         
35444         if(this.title.length){
35445             cfg.cn[0].cn[0].cn.push({
35446                 tag: 'h4',
35447                 cls: 'masonry-brick-title',
35448                 html: this.title
35449             });
35450         }
35451         
35452         if(this.html.length){
35453             cfg.cn[1].cn.push({
35454                 tag: 'p',
35455                 cls: 'masonry-brick-text',
35456                 html: this.html
35457             });
35458         }
35459
35460         if(this.bgimage.length){
35461             cfg.cn[0].cn.push({
35462                 tag: 'img',
35463                 cls: 'masonry-brick-image-view',
35464                 src: this.bgimage
35465             });
35466         }
35467         
35468         if(this.videourl.length){
35469             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35470             // youtube support only?
35471             cfg.cn[0].cn.cn.push({
35472                 tag: 'iframe',
35473                 cls: 'masonry-brick-image-view',
35474                 src: vurl,
35475                 frameborder : 0,
35476                 allowfullscreen : true
35477             });
35478         }
35479         
35480         return cfg;
35481     },
35482     
35483     initEvents: function() 
35484     {
35485         switch (this.size) {
35486             case 'xs' :
35487                 this.x = 1;
35488                 this.y = 1;
35489                 break;
35490             case 'sm' :
35491                 this.x = 2;
35492                 this.y = 2;
35493                 break;
35494             case 'md' :
35495             case 'md-left' :
35496             case 'md-right' :
35497                 this.x = 3;
35498                 this.y = 3;
35499                 break;
35500             case 'tall' :
35501                 this.x = 2;
35502                 this.y = 3;
35503                 break;
35504             case 'wide' :
35505                 this.x = 3;
35506                 this.y = 2;
35507                 break;
35508             case 'wide-thin' :
35509                 this.x = 3;
35510                 this.y = 1;
35511                 break;
35512                         
35513             default :
35514                 break;
35515         }
35516         
35517         if(Roo.isTouch){
35518             this.el.on('touchstart', this.onTouchStart, this);
35519             this.el.on('touchmove', this.onTouchMove, this);
35520             this.el.on('touchend', this.onTouchEnd, this);
35521             this.el.on('contextmenu', this.onContextMenu, this);
35522         } else {
35523             this.el.on('mouseenter'  ,this.enter, this);
35524             this.el.on('mouseleave', this.leave, this);
35525             this.el.on('click', this.onClick, this);
35526         }
35527         
35528         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35529             this.parent().bricks.push(this);   
35530         }
35531         
35532     },
35533     
35534     onClick: function(e, el)
35535     {
35536         var time = this.endTimer - this.startTimer;
35537         // Roo.log(e.preventDefault());
35538         if(Roo.isTouch){
35539             if(time > 1000){
35540                 e.preventDefault();
35541                 return;
35542             }
35543         }
35544         
35545         if(!this.preventDefault){
35546             return;
35547         }
35548         
35549         e.preventDefault();
35550         
35551         if (this.activeClass != '') {
35552             this.selectBrick();
35553         }
35554         
35555         this.fireEvent('click', this, e);
35556     },
35557     
35558     enter: function(e, el)
35559     {
35560         e.preventDefault();
35561         
35562         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35563             return;
35564         }
35565         
35566         if(this.bgimage.length && this.html.length){
35567             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35568         }
35569     },
35570     
35571     leave: function(e, el)
35572     {
35573         e.preventDefault();
35574         
35575         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35576             return;
35577         }
35578         
35579         if(this.bgimage.length && this.html.length){
35580             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35581         }
35582     },
35583     
35584     onTouchStart: function(e, el)
35585     {
35586 //        e.preventDefault();
35587         
35588         this.touchmoved = false;
35589         
35590         if(!this.isFitContainer){
35591             return;
35592         }
35593         
35594         if(!this.bgimage.length || !this.html.length){
35595             return;
35596         }
35597         
35598         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35599         
35600         this.timer = new Date().getTime();
35601         
35602     },
35603     
35604     onTouchMove: function(e, el)
35605     {
35606         this.touchmoved = true;
35607     },
35608     
35609     onContextMenu : function(e,el)
35610     {
35611         e.preventDefault();
35612         e.stopPropagation();
35613         return false;
35614     },
35615     
35616     onTouchEnd: function(e, el)
35617     {
35618 //        e.preventDefault();
35619         
35620         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35621         
35622             this.leave(e,el);
35623             
35624             return;
35625         }
35626         
35627         if(!this.bgimage.length || !this.html.length){
35628             
35629             if(this.href.length){
35630                 window.location.href = this.href;
35631             }
35632             
35633             return;
35634         }
35635         
35636         if(!this.isFitContainer){
35637             return;
35638         }
35639         
35640         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35641         
35642         window.location.href = this.href;
35643     },
35644     
35645     //selection on single brick only
35646     selectBrick : function() {
35647         
35648         if (!this.parentId) {
35649             return;
35650         }
35651         
35652         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35653         var index = m.selectedBrick.indexOf(this.id);
35654         
35655         if ( index > -1) {
35656             m.selectedBrick.splice(index,1);
35657             this.el.removeClass(this.activeClass);
35658             return;
35659         }
35660         
35661         for(var i = 0; i < m.selectedBrick.length; i++) {
35662             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35663             b.el.removeClass(b.activeClass);
35664         }
35665         
35666         m.selectedBrick = [];
35667         
35668         m.selectedBrick.push(this.id);
35669         this.el.addClass(this.activeClass);
35670         return;
35671     },
35672     
35673     isSelected : function(){
35674         return this.el.hasClass(this.activeClass);
35675         
35676     }
35677 });
35678
35679 Roo.apply(Roo.bootstrap.MasonryBrick, {
35680     
35681     //groups: {},
35682     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35683      /**
35684     * register a Masonry Brick
35685     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35686     */
35687     
35688     register : function(brick)
35689     {
35690         //this.groups[brick.id] = brick;
35691         this.groups.add(brick.id, brick);
35692     },
35693     /**
35694     * fetch a  masonry brick based on the masonry brick ID
35695     * @param {string} the masonry brick to add
35696     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35697     */
35698     
35699     get: function(brick_id) 
35700     {
35701         // if (typeof(this.groups[brick_id]) == 'undefined') {
35702         //     return false;
35703         // }
35704         // return this.groups[brick_id] ;
35705         
35706         if(this.groups.key(brick_id)) {
35707             return this.groups.key(brick_id);
35708         }
35709         
35710         return false;
35711     }
35712     
35713     
35714     
35715 });
35716
35717  /*
35718  * - LGPL
35719  *
35720  * element
35721  * 
35722  */
35723
35724 /**
35725  * @class Roo.bootstrap.Brick
35726  * @extends Roo.bootstrap.Component
35727  * Bootstrap Brick class
35728  * 
35729  * @constructor
35730  * Create a new Brick
35731  * @param {Object} config The config object
35732  */
35733
35734 Roo.bootstrap.Brick = function(config){
35735     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35736     
35737     this.addEvents({
35738         // raw events
35739         /**
35740          * @event click
35741          * When a Brick is click
35742          * @param {Roo.bootstrap.Brick} this
35743          * @param {Roo.EventObject} e
35744          */
35745         "click" : true
35746     });
35747 };
35748
35749 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35750     
35751     /**
35752      * @cfg {String} title
35753      */   
35754     title : '',
35755     /**
35756      * @cfg {String} html
35757      */   
35758     html : '',
35759     /**
35760      * @cfg {String} bgimage
35761      */   
35762     bgimage : '',
35763     /**
35764      * @cfg {String} cls
35765      */   
35766     cls : '',
35767     /**
35768      * @cfg {String} href
35769      */   
35770     href : '',
35771     /**
35772      * @cfg {String} video
35773      */   
35774     video : '',
35775     /**
35776      * @cfg {Boolean} square
35777      */   
35778     square : true,
35779     
35780     getAutoCreate : function()
35781     {
35782         var cls = 'roo-brick';
35783         
35784         if(this.href.length){
35785             cls += ' roo-brick-link';
35786         }
35787         
35788         if(this.bgimage.length){
35789             cls += ' roo-brick-image';
35790         }
35791         
35792         if(!this.html.length && !this.bgimage.length){
35793             cls += ' roo-brick-center-title';
35794         }
35795         
35796         if(!this.html.length && this.bgimage.length){
35797             cls += ' roo-brick-bottom-title';
35798         }
35799         
35800         if(this.cls){
35801             cls += ' ' + this.cls;
35802         }
35803         
35804         var cfg = {
35805             tag: (this.href.length) ? 'a' : 'div',
35806             cls: cls,
35807             cn: [
35808                 {
35809                     tag: 'div',
35810                     cls: 'roo-brick-paragraph',
35811                     cn: []
35812                 }
35813             ]
35814         };
35815         
35816         if(this.href.length){
35817             cfg.href = this.href;
35818         }
35819         
35820         var cn = cfg.cn[0].cn;
35821         
35822         if(this.title.length){
35823             cn.push({
35824                 tag: 'h4',
35825                 cls: 'roo-brick-title',
35826                 html: this.title
35827             });
35828         }
35829         
35830         if(this.html.length){
35831             cn.push({
35832                 tag: 'p',
35833                 cls: 'roo-brick-text',
35834                 html: this.html
35835             });
35836         } else {
35837             cn.cls += ' hide';
35838         }
35839         
35840         if(this.bgimage.length){
35841             cfg.cn.push({
35842                 tag: 'img',
35843                 cls: 'roo-brick-image-view',
35844                 src: this.bgimage
35845             });
35846         }
35847         
35848         return cfg;
35849     },
35850     
35851     initEvents: function() 
35852     {
35853         if(this.title.length || this.html.length){
35854             this.el.on('mouseenter'  ,this.enter, this);
35855             this.el.on('mouseleave', this.leave, this);
35856         }
35857         
35858         Roo.EventManager.onWindowResize(this.resize, this); 
35859         
35860         if(this.bgimage.length){
35861             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35862             this.imageEl.on('load', this.onImageLoad, this);
35863             return;
35864         }
35865         
35866         this.resize();
35867     },
35868     
35869     onImageLoad : function()
35870     {
35871         this.resize();
35872     },
35873     
35874     resize : function()
35875     {
35876         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35877         
35878         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35879         
35880         if(this.bgimage.length){
35881             var image = this.el.select('.roo-brick-image-view', true).first();
35882             
35883             image.setWidth(paragraph.getWidth());
35884             
35885             if(this.square){
35886                 image.setHeight(paragraph.getWidth());
35887             }
35888             
35889             this.el.setHeight(image.getHeight());
35890             paragraph.setHeight(image.getHeight());
35891             
35892         }
35893         
35894     },
35895     
35896     enter: function(e, el)
35897     {
35898         e.preventDefault();
35899         
35900         if(this.bgimage.length){
35901             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35902             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35903         }
35904     },
35905     
35906     leave: function(e, el)
35907     {
35908         e.preventDefault();
35909         
35910         if(this.bgimage.length){
35911             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35912             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35913         }
35914     }
35915     
35916 });
35917
35918  
35919
35920  /*
35921  * - LGPL
35922  *
35923  * Number field 
35924  */
35925
35926 /**
35927  * @class Roo.bootstrap.NumberField
35928  * @extends Roo.bootstrap.Input
35929  * Bootstrap NumberField class
35930  * 
35931  * 
35932  * 
35933  * 
35934  * @constructor
35935  * Create a new NumberField
35936  * @param {Object} config The config object
35937  */
35938
35939 Roo.bootstrap.NumberField = function(config){
35940     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35941 };
35942
35943 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35944     
35945     /**
35946      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35947      */
35948     allowDecimals : true,
35949     /**
35950      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35951      */
35952     decimalSeparator : ".",
35953     /**
35954      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35955      */
35956     decimalPrecision : 2,
35957     /**
35958      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35959      */
35960     allowNegative : true,
35961     
35962     /**
35963      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35964      */
35965     allowZero: true,
35966     /**
35967      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35968      */
35969     minValue : Number.NEGATIVE_INFINITY,
35970     /**
35971      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35972      */
35973     maxValue : Number.MAX_VALUE,
35974     /**
35975      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35976      */
35977     minText : "The minimum value for this field is {0}",
35978     /**
35979      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35980      */
35981     maxText : "The maximum value for this field is {0}",
35982     /**
35983      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35984      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35985      */
35986     nanText : "{0} is not a valid number",
35987     /**
35988      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35989      */
35990     thousandsDelimiter : false,
35991     /**
35992      * @cfg {String} valueAlign alignment of value
35993      */
35994     valueAlign : "left",
35995
35996     getAutoCreate : function()
35997     {
35998         var hiddenInput = {
35999             tag: 'input',
36000             type: 'hidden',
36001             id: Roo.id(),
36002             cls: 'hidden-number-input'
36003         };
36004         
36005         if (this.name) {
36006             hiddenInput.name = this.name;
36007         }
36008         
36009         this.name = '';
36010         
36011         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36012         
36013         this.name = hiddenInput.name;
36014         
36015         if(cfg.cn.length > 0) {
36016             cfg.cn.push(hiddenInput);
36017         }
36018         
36019         return cfg;
36020     },
36021
36022     // private
36023     initEvents : function()
36024     {   
36025         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36026         
36027         var allowed = "0123456789";
36028         
36029         if(this.allowDecimals){
36030             allowed += this.decimalSeparator;
36031         }
36032         
36033         if(this.allowNegative){
36034             allowed += "-";
36035         }
36036         
36037         if(this.thousandsDelimiter) {
36038             allowed += ",";
36039         }
36040         
36041         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36042         
36043         var keyPress = function(e){
36044             
36045             var k = e.getKey();
36046             
36047             var c = e.getCharCode();
36048             
36049             if(
36050                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36051                     allowed.indexOf(String.fromCharCode(c)) === -1
36052             ){
36053                 e.stopEvent();
36054                 return;
36055             }
36056             
36057             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36058                 return;
36059             }
36060             
36061             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36062                 e.stopEvent();
36063             }
36064         };
36065         
36066         this.el.on("keypress", keyPress, this);
36067     },
36068     
36069     validateValue : function(value)
36070     {
36071         
36072         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36073             return false;
36074         }
36075         
36076         var num = this.parseValue(value);
36077         
36078         if(isNaN(num)){
36079             this.markInvalid(String.format(this.nanText, value));
36080             return false;
36081         }
36082         
36083         if(num < this.minValue){
36084             this.markInvalid(String.format(this.minText, this.minValue));
36085             return false;
36086         }
36087         
36088         if(num > this.maxValue){
36089             this.markInvalid(String.format(this.maxText, this.maxValue));
36090             return false;
36091         }
36092         
36093         return true;
36094     },
36095
36096     getValue : function()
36097     {
36098         var v = this.hiddenEl().getValue();
36099         
36100         return this.fixPrecision(this.parseValue(v));
36101     },
36102
36103     parseValue : function(value)
36104     {
36105         if(this.thousandsDelimiter) {
36106             value += "";
36107             r = new RegExp(",", "g");
36108             value = value.replace(r, "");
36109         }
36110         
36111         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36112         return isNaN(value) ? '' : value;
36113     },
36114
36115     fixPrecision : function(value)
36116     {
36117         if(this.thousandsDelimiter) {
36118             value += "";
36119             r = new RegExp(",", "g");
36120             value = value.replace(r, "");
36121         }
36122         
36123         var nan = isNaN(value);
36124         
36125         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36126             return nan ? '' : value;
36127         }
36128         return parseFloat(value).toFixed(this.decimalPrecision);
36129     },
36130
36131     setValue : function(v)
36132     {
36133         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36134         
36135         this.value = v;
36136         
36137         if(this.rendered){
36138             
36139             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36140             
36141             this.inputEl().dom.value = (v == '') ? '' :
36142                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36143             
36144             if(!this.allowZero && v === '0') {
36145                 this.hiddenEl().dom.value = '';
36146                 this.inputEl().dom.value = '';
36147             }
36148             
36149             this.validate();
36150         }
36151     },
36152
36153     decimalPrecisionFcn : function(v)
36154     {
36155         return Math.floor(v);
36156     },
36157
36158     beforeBlur : function()
36159     {
36160         var v = this.parseValue(this.getRawValue());
36161         
36162         if(v || v === 0 || v === ''){
36163             this.setValue(v);
36164         }
36165     },
36166     
36167     hiddenEl : function()
36168     {
36169         return this.el.select('input.hidden-number-input',true).first();
36170     }
36171     
36172 });
36173
36174  
36175
36176 /*
36177 * Licence: LGPL
36178 */
36179
36180 /**
36181  * @class Roo.bootstrap.DocumentSlider
36182  * @extends Roo.bootstrap.Component
36183  * Bootstrap DocumentSlider class
36184  * 
36185  * @constructor
36186  * Create a new DocumentViewer
36187  * @param {Object} config The config object
36188  */
36189
36190 Roo.bootstrap.DocumentSlider = function(config){
36191     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36192     
36193     this.files = [];
36194     
36195     this.addEvents({
36196         /**
36197          * @event initial
36198          * Fire after initEvent
36199          * @param {Roo.bootstrap.DocumentSlider} this
36200          */
36201         "initial" : true,
36202         /**
36203          * @event update
36204          * Fire after update
36205          * @param {Roo.bootstrap.DocumentSlider} this
36206          */
36207         "update" : true,
36208         /**
36209          * @event click
36210          * Fire after click
36211          * @param {Roo.bootstrap.DocumentSlider} this
36212          */
36213         "click" : true
36214     });
36215 };
36216
36217 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36218     
36219     files : false,
36220     
36221     indicator : 0,
36222     
36223     getAutoCreate : function()
36224     {
36225         var cfg = {
36226             tag : 'div',
36227             cls : 'roo-document-slider',
36228             cn : [
36229                 {
36230                     tag : 'div',
36231                     cls : 'roo-document-slider-header',
36232                     cn : [
36233                         {
36234                             tag : 'div',
36235                             cls : 'roo-document-slider-header-title'
36236                         }
36237                     ]
36238                 },
36239                 {
36240                     tag : 'div',
36241                     cls : 'roo-document-slider-body',
36242                     cn : [
36243                         {
36244                             tag : 'div',
36245                             cls : 'roo-document-slider-prev',
36246                             cn : [
36247                                 {
36248                                     tag : 'i',
36249                                     cls : 'fa fa-chevron-left'
36250                                 }
36251                             ]
36252                         },
36253                         {
36254                             tag : 'div',
36255                             cls : 'roo-document-slider-thumb',
36256                             cn : [
36257                                 {
36258                                     tag : 'img',
36259                                     cls : 'roo-document-slider-image'
36260                                 }
36261                             ]
36262                         },
36263                         {
36264                             tag : 'div',
36265                             cls : 'roo-document-slider-next',
36266                             cn : [
36267                                 {
36268                                     tag : 'i',
36269                                     cls : 'fa fa-chevron-right'
36270                                 }
36271                             ]
36272                         }
36273                     ]
36274                 }
36275             ]
36276         };
36277         
36278         return cfg;
36279     },
36280     
36281     initEvents : function()
36282     {
36283         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36284         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36285         
36286         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36287         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36288         
36289         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36290         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36291         
36292         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36293         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36294         
36295         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36296         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36297         
36298         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36299         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36300         
36301         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36302         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36303         
36304         this.thumbEl.on('click', this.onClick, this);
36305         
36306         this.prevIndicator.on('click', this.prev, this);
36307         
36308         this.nextIndicator.on('click', this.next, this);
36309         
36310     },
36311     
36312     initial : function()
36313     {
36314         if(this.files.length){
36315             this.indicator = 1;
36316             this.update()
36317         }
36318         
36319         this.fireEvent('initial', this);
36320     },
36321     
36322     update : function()
36323     {
36324         this.imageEl.attr('src', this.files[this.indicator - 1]);
36325         
36326         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36327         
36328         this.prevIndicator.show();
36329         
36330         if(this.indicator == 1){
36331             this.prevIndicator.hide();
36332         }
36333         
36334         this.nextIndicator.show();
36335         
36336         if(this.indicator == this.files.length){
36337             this.nextIndicator.hide();
36338         }
36339         
36340         this.thumbEl.scrollTo('top');
36341         
36342         this.fireEvent('update', this);
36343     },
36344     
36345     onClick : function(e)
36346     {
36347         e.preventDefault();
36348         
36349         this.fireEvent('click', this);
36350     },
36351     
36352     prev : function(e)
36353     {
36354         e.preventDefault();
36355         
36356         this.indicator = Math.max(1, this.indicator - 1);
36357         
36358         this.update();
36359     },
36360     
36361     next : function(e)
36362     {
36363         e.preventDefault();
36364         
36365         this.indicator = Math.min(this.files.length, this.indicator + 1);
36366         
36367         this.update();
36368     }
36369 });
36370 /*
36371  * - LGPL
36372  *
36373  * RadioSet
36374  *
36375  *
36376  */
36377
36378 /**
36379  * @class Roo.bootstrap.RadioSet
36380  * @extends Roo.bootstrap.Input
36381  * Bootstrap RadioSet class
36382  * @cfg {String} indicatorpos (left|right) default left
36383  * @cfg {Boolean} inline (true|false) inline the element (default true)
36384  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36385  * @constructor
36386  * Create a new RadioSet
36387  * @param {Object} config The config object
36388  */
36389
36390 Roo.bootstrap.RadioSet = function(config){
36391     
36392     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36393     
36394     this.radioes = [];
36395     
36396     Roo.bootstrap.RadioSet.register(this);
36397     
36398     this.addEvents({
36399         /**
36400         * @event check
36401         * Fires when the element is checked or unchecked.
36402         * @param {Roo.bootstrap.RadioSet} this This radio
36403         * @param {Roo.bootstrap.Radio} item The checked item
36404         */
36405        check : true,
36406        /**
36407         * @event click
36408         * Fires when the element is click.
36409         * @param {Roo.bootstrap.RadioSet} this This radio set
36410         * @param {Roo.bootstrap.Radio} item The checked item
36411         * @param {Roo.EventObject} e The event object
36412         */
36413        click : true
36414     });
36415     
36416 };
36417
36418 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36419
36420     radioes : false,
36421     
36422     inline : true,
36423     
36424     weight : '',
36425     
36426     indicatorpos : 'left',
36427     
36428     getAutoCreate : function()
36429     {
36430         var label = {
36431             tag : 'label',
36432             cls : 'roo-radio-set-label',
36433             cn : [
36434                 {
36435                     tag : 'span',
36436                     html : this.fieldLabel
36437                 }
36438             ]
36439         };
36440         if (Roo.bootstrap.version == 3) {
36441             
36442             
36443             if(this.indicatorpos == 'left'){
36444                 label.cn.unshift({
36445                     tag : 'i',
36446                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36447                     tooltip : 'This field is required'
36448                 });
36449             } else {
36450                 label.cn.push({
36451                     tag : 'i',
36452                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36453                     tooltip : 'This field is required'
36454                 });
36455             }
36456         }
36457         var items = {
36458             tag : 'div',
36459             cls : 'roo-radio-set-items'
36460         };
36461         
36462         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36463         
36464         if (align === 'left' && this.fieldLabel.length) {
36465             
36466             items = {
36467                 cls : "roo-radio-set-right", 
36468                 cn: [
36469                     items
36470                 ]
36471             };
36472             
36473             if(this.labelWidth > 12){
36474                 label.style = "width: " + this.labelWidth + 'px';
36475             }
36476             
36477             if(this.labelWidth < 13 && this.labelmd == 0){
36478                 this.labelmd = this.labelWidth;
36479             }
36480             
36481             if(this.labellg > 0){
36482                 label.cls += ' col-lg-' + this.labellg;
36483                 items.cls += ' col-lg-' + (12 - this.labellg);
36484             }
36485             
36486             if(this.labelmd > 0){
36487                 label.cls += ' col-md-' + this.labelmd;
36488                 items.cls += ' col-md-' + (12 - this.labelmd);
36489             }
36490             
36491             if(this.labelsm > 0){
36492                 label.cls += ' col-sm-' + this.labelsm;
36493                 items.cls += ' col-sm-' + (12 - this.labelsm);
36494             }
36495             
36496             if(this.labelxs > 0){
36497                 label.cls += ' col-xs-' + this.labelxs;
36498                 items.cls += ' col-xs-' + (12 - this.labelxs);
36499             }
36500         }
36501         
36502         var cfg = {
36503             tag : 'div',
36504             cls : 'roo-radio-set',
36505             cn : [
36506                 {
36507                     tag : 'input',
36508                     cls : 'roo-radio-set-input',
36509                     type : 'hidden',
36510                     name : this.name,
36511                     value : this.value ? this.value :  ''
36512                 },
36513                 label,
36514                 items
36515             ]
36516         };
36517         
36518         if(this.weight.length){
36519             cfg.cls += ' roo-radio-' + this.weight;
36520         }
36521         
36522         if(this.inline) {
36523             cfg.cls += ' roo-radio-set-inline';
36524         }
36525         
36526         var settings=this;
36527         ['xs','sm','md','lg'].map(function(size){
36528             if (settings[size]) {
36529                 cfg.cls += ' col-' + size + '-' + settings[size];
36530             }
36531         });
36532         
36533         return cfg;
36534         
36535     },
36536
36537     initEvents : function()
36538     {
36539         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36540         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36541         
36542         if(!this.fieldLabel.length){
36543             this.labelEl.hide();
36544         }
36545         
36546         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36547         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36548         
36549         this.indicator = this.indicatorEl();
36550         
36551         if(this.indicator){
36552             this.indicator.addClass('invisible');
36553         }
36554         
36555         this.originalValue = this.getValue();
36556         
36557     },
36558     
36559     inputEl: function ()
36560     {
36561         return this.el.select('.roo-radio-set-input', true).first();
36562     },
36563     
36564     getChildContainer : function()
36565     {
36566         return this.itemsEl;
36567     },
36568     
36569     register : function(item)
36570     {
36571         this.radioes.push(item);
36572         
36573     },
36574     
36575     validate : function()
36576     {   
36577         if(this.getVisibilityEl().hasClass('hidden')){
36578             return true;
36579         }
36580         
36581         var valid = false;
36582         
36583         Roo.each(this.radioes, function(i){
36584             if(!i.checked){
36585                 return;
36586             }
36587             
36588             valid = true;
36589             return false;
36590         });
36591         
36592         if(this.allowBlank) {
36593             return true;
36594         }
36595         
36596         if(this.disabled || valid){
36597             this.markValid();
36598             return true;
36599         }
36600         
36601         this.markInvalid();
36602         return false;
36603         
36604     },
36605     
36606     markValid : function()
36607     {
36608         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36609             this.indicatorEl().removeClass('visible');
36610             this.indicatorEl().addClass('invisible');
36611         }
36612         
36613         
36614         if (Roo.bootstrap.version == 3) {
36615             this.el.removeClass([this.invalidClass, this.validClass]);
36616             this.el.addClass(this.validClass);
36617         } else {
36618             this.el.removeClass(['is-invalid','is-valid']);
36619             this.el.addClass(['is-valid']);
36620         }
36621         this.fireEvent('valid', this);
36622     },
36623     
36624     markInvalid : function(msg)
36625     {
36626         if(this.allowBlank || this.disabled){
36627             return;
36628         }
36629         
36630         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36631             this.indicatorEl().removeClass('invisible');
36632             this.indicatorEl().addClass('visible');
36633         }
36634         if (Roo.bootstrap.version == 3) {
36635             this.el.removeClass([this.invalidClass, this.validClass]);
36636             this.el.addClass(this.invalidClass);
36637         } else {
36638             this.el.removeClass(['is-invalid','is-valid']);
36639             this.el.addClass(['is-invalid']);
36640         }
36641         
36642         this.fireEvent('invalid', this, msg);
36643         
36644     },
36645     
36646     setValue : function(v, suppressEvent)
36647     {   
36648         if(this.value === v){
36649             return;
36650         }
36651         
36652         this.value = v;
36653         
36654         if(this.rendered){
36655             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36656         }
36657         
36658         Roo.each(this.radioes, function(i){
36659             i.checked = false;
36660             i.el.removeClass('checked');
36661         });
36662         
36663         Roo.each(this.radioes, function(i){
36664             
36665             if(i.value === v || i.value.toString() === v.toString()){
36666                 i.checked = true;
36667                 i.el.addClass('checked');
36668                 
36669                 if(suppressEvent !== true){
36670                     this.fireEvent('check', this, i);
36671                 }
36672                 
36673                 return false;
36674             }
36675             
36676         }, this);
36677         
36678         this.validate();
36679     },
36680     
36681     clearInvalid : function(){
36682         
36683         if(!this.el || this.preventMark){
36684             return;
36685         }
36686         
36687         this.el.removeClass([this.invalidClass]);
36688         
36689         this.fireEvent('valid', this);
36690     }
36691     
36692 });
36693
36694 Roo.apply(Roo.bootstrap.RadioSet, {
36695     
36696     groups: {},
36697     
36698     register : function(set)
36699     {
36700         this.groups[set.name] = set;
36701     },
36702     
36703     get: function(name) 
36704     {
36705         if (typeof(this.groups[name]) == 'undefined') {
36706             return false;
36707         }
36708         
36709         return this.groups[name] ;
36710     }
36711     
36712 });
36713 /*
36714  * Based on:
36715  * Ext JS Library 1.1.1
36716  * Copyright(c) 2006-2007, Ext JS, LLC.
36717  *
36718  * Originally Released Under LGPL - original licence link has changed is not relivant.
36719  *
36720  * Fork - LGPL
36721  * <script type="text/javascript">
36722  */
36723
36724
36725 /**
36726  * @class Roo.bootstrap.SplitBar
36727  * @extends Roo.util.Observable
36728  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36729  * <br><br>
36730  * Usage:
36731  * <pre><code>
36732 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36733                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36734 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36735 split.minSize = 100;
36736 split.maxSize = 600;
36737 split.animate = true;
36738 split.on('moved', splitterMoved);
36739 </code></pre>
36740  * @constructor
36741  * Create a new SplitBar
36742  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36743  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36744  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36745  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36746                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36747                         position of the SplitBar).
36748  */
36749 Roo.bootstrap.SplitBar = function(cfg){
36750     
36751     /** @private */
36752     
36753     //{
36754     //  dragElement : elm
36755     //  resizingElement: el,
36756         // optional..
36757     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36758     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36759         // existingProxy ???
36760     //}
36761     
36762     this.el = Roo.get(cfg.dragElement, true);
36763     this.el.dom.unselectable = "on";
36764     /** @private */
36765     this.resizingEl = Roo.get(cfg.resizingElement, true);
36766
36767     /**
36768      * @private
36769      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36770      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36771      * @type Number
36772      */
36773     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36774     
36775     /**
36776      * The minimum size of the resizing element. (Defaults to 0)
36777      * @type Number
36778      */
36779     this.minSize = 0;
36780     
36781     /**
36782      * The maximum size of the resizing element. (Defaults to 2000)
36783      * @type Number
36784      */
36785     this.maxSize = 2000;
36786     
36787     /**
36788      * Whether to animate the transition to the new size
36789      * @type Boolean
36790      */
36791     this.animate = false;
36792     
36793     /**
36794      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36795      * @type Boolean
36796      */
36797     this.useShim = false;
36798     
36799     /** @private */
36800     this.shim = null;
36801     
36802     if(!cfg.existingProxy){
36803         /** @private */
36804         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36805     }else{
36806         this.proxy = Roo.get(cfg.existingProxy).dom;
36807     }
36808     /** @private */
36809     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36810     
36811     /** @private */
36812     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36813     
36814     /** @private */
36815     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36816     
36817     /** @private */
36818     this.dragSpecs = {};
36819     
36820     /**
36821      * @private The adapter to use to positon and resize elements
36822      */
36823     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36824     this.adapter.init(this);
36825     
36826     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36827         /** @private */
36828         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36829         this.el.addClass("roo-splitbar-h");
36830     }else{
36831         /** @private */
36832         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36833         this.el.addClass("roo-splitbar-v");
36834     }
36835     
36836     this.addEvents({
36837         /**
36838          * @event resize
36839          * Fires when the splitter is moved (alias for {@link #event-moved})
36840          * @param {Roo.bootstrap.SplitBar} this
36841          * @param {Number} newSize the new width or height
36842          */
36843         "resize" : true,
36844         /**
36845          * @event moved
36846          * Fires when the splitter is moved
36847          * @param {Roo.bootstrap.SplitBar} this
36848          * @param {Number} newSize the new width or height
36849          */
36850         "moved" : true,
36851         /**
36852          * @event beforeresize
36853          * Fires before the splitter is dragged
36854          * @param {Roo.bootstrap.SplitBar} this
36855          */
36856         "beforeresize" : true,
36857
36858         "beforeapply" : true
36859     });
36860
36861     Roo.util.Observable.call(this);
36862 };
36863
36864 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36865     onStartProxyDrag : function(x, y){
36866         this.fireEvent("beforeresize", this);
36867         if(!this.overlay){
36868             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36869             o.unselectable();
36870             o.enableDisplayMode("block");
36871             // all splitbars share the same overlay
36872             Roo.bootstrap.SplitBar.prototype.overlay = o;
36873         }
36874         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36875         this.overlay.show();
36876         Roo.get(this.proxy).setDisplayed("block");
36877         var size = this.adapter.getElementSize(this);
36878         this.activeMinSize = this.getMinimumSize();;
36879         this.activeMaxSize = this.getMaximumSize();;
36880         var c1 = size - this.activeMinSize;
36881         var c2 = Math.max(this.activeMaxSize - size, 0);
36882         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36883             this.dd.resetConstraints();
36884             this.dd.setXConstraint(
36885                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36886                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36887             );
36888             this.dd.setYConstraint(0, 0);
36889         }else{
36890             this.dd.resetConstraints();
36891             this.dd.setXConstraint(0, 0);
36892             this.dd.setYConstraint(
36893                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36894                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36895             );
36896          }
36897         this.dragSpecs.startSize = size;
36898         this.dragSpecs.startPoint = [x, y];
36899         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36900     },
36901     
36902     /** 
36903      * @private Called after the drag operation by the DDProxy
36904      */
36905     onEndProxyDrag : function(e){
36906         Roo.get(this.proxy).setDisplayed(false);
36907         var endPoint = Roo.lib.Event.getXY(e);
36908         if(this.overlay){
36909             this.overlay.hide();
36910         }
36911         var newSize;
36912         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36913             newSize = this.dragSpecs.startSize + 
36914                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36915                     endPoint[0] - this.dragSpecs.startPoint[0] :
36916                     this.dragSpecs.startPoint[0] - endPoint[0]
36917                 );
36918         }else{
36919             newSize = this.dragSpecs.startSize + 
36920                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36921                     endPoint[1] - this.dragSpecs.startPoint[1] :
36922                     this.dragSpecs.startPoint[1] - endPoint[1]
36923                 );
36924         }
36925         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36926         if(newSize != this.dragSpecs.startSize){
36927             if(this.fireEvent('beforeapply', this, newSize) !== false){
36928                 this.adapter.setElementSize(this, newSize);
36929                 this.fireEvent("moved", this, newSize);
36930                 this.fireEvent("resize", this, newSize);
36931             }
36932         }
36933     },
36934     
36935     /**
36936      * Get the adapter this SplitBar uses
36937      * @return The adapter object
36938      */
36939     getAdapter : function(){
36940         return this.adapter;
36941     },
36942     
36943     /**
36944      * Set the adapter this SplitBar uses
36945      * @param {Object} adapter A SplitBar adapter object
36946      */
36947     setAdapter : function(adapter){
36948         this.adapter = adapter;
36949         this.adapter.init(this);
36950     },
36951     
36952     /**
36953      * Gets the minimum size for the resizing element
36954      * @return {Number} The minimum size
36955      */
36956     getMinimumSize : function(){
36957         return this.minSize;
36958     },
36959     
36960     /**
36961      * Sets the minimum size for the resizing element
36962      * @param {Number} minSize The minimum size
36963      */
36964     setMinimumSize : function(minSize){
36965         this.minSize = minSize;
36966     },
36967     
36968     /**
36969      * Gets the maximum size for the resizing element
36970      * @return {Number} The maximum size
36971      */
36972     getMaximumSize : function(){
36973         return this.maxSize;
36974     },
36975     
36976     /**
36977      * Sets the maximum size for the resizing element
36978      * @param {Number} maxSize The maximum size
36979      */
36980     setMaximumSize : function(maxSize){
36981         this.maxSize = maxSize;
36982     },
36983     
36984     /**
36985      * Sets the initialize size for the resizing element
36986      * @param {Number} size The initial size
36987      */
36988     setCurrentSize : function(size){
36989         var oldAnimate = this.animate;
36990         this.animate = false;
36991         this.adapter.setElementSize(this, size);
36992         this.animate = oldAnimate;
36993     },
36994     
36995     /**
36996      * Destroy this splitbar. 
36997      * @param {Boolean} removeEl True to remove the element
36998      */
36999     destroy : function(removeEl){
37000         if(this.shim){
37001             this.shim.remove();
37002         }
37003         this.dd.unreg();
37004         this.proxy.parentNode.removeChild(this.proxy);
37005         if(removeEl){
37006             this.el.remove();
37007         }
37008     }
37009 });
37010
37011 /**
37012  * @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.
37013  */
37014 Roo.bootstrap.SplitBar.createProxy = function(dir){
37015     var proxy = new Roo.Element(document.createElement("div"));
37016     proxy.unselectable();
37017     var cls = 'roo-splitbar-proxy';
37018     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37019     document.body.appendChild(proxy.dom);
37020     return proxy.dom;
37021 };
37022
37023 /** 
37024  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37025  * Default Adapter. It assumes the splitter and resizing element are not positioned
37026  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37027  */
37028 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37029 };
37030
37031 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37032     // do nothing for now
37033     init : function(s){
37034     
37035     },
37036     /**
37037      * Called before drag operations to get the current size of the resizing element. 
37038      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37039      */
37040      getElementSize : function(s){
37041         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37042             return s.resizingEl.getWidth();
37043         }else{
37044             return s.resizingEl.getHeight();
37045         }
37046     },
37047     
37048     /**
37049      * Called after drag operations to set the size of the resizing element.
37050      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37051      * @param {Number} newSize The new size to set
37052      * @param {Function} onComplete A function to be invoked when resizing is complete
37053      */
37054     setElementSize : function(s, newSize, onComplete){
37055         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37056             if(!s.animate){
37057                 s.resizingEl.setWidth(newSize);
37058                 if(onComplete){
37059                     onComplete(s, newSize);
37060                 }
37061             }else{
37062                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37063             }
37064         }else{
37065             
37066             if(!s.animate){
37067                 s.resizingEl.setHeight(newSize);
37068                 if(onComplete){
37069                     onComplete(s, newSize);
37070                 }
37071             }else{
37072                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37073             }
37074         }
37075     }
37076 };
37077
37078 /** 
37079  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37080  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37081  * Adapter that  moves the splitter element to align with the resized sizing element. 
37082  * Used with an absolute positioned SplitBar.
37083  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37084  * document.body, make sure you assign an id to the body element.
37085  */
37086 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37087     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37088     this.container = Roo.get(container);
37089 };
37090
37091 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37092     init : function(s){
37093         this.basic.init(s);
37094     },
37095     
37096     getElementSize : function(s){
37097         return this.basic.getElementSize(s);
37098     },
37099     
37100     setElementSize : function(s, newSize, onComplete){
37101         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37102     },
37103     
37104     moveSplitter : function(s){
37105         var yes = Roo.bootstrap.SplitBar;
37106         switch(s.placement){
37107             case yes.LEFT:
37108                 s.el.setX(s.resizingEl.getRight());
37109                 break;
37110             case yes.RIGHT:
37111                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37112                 break;
37113             case yes.TOP:
37114                 s.el.setY(s.resizingEl.getBottom());
37115                 break;
37116             case yes.BOTTOM:
37117                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37118                 break;
37119         }
37120     }
37121 };
37122
37123 /**
37124  * Orientation constant - Create a vertical SplitBar
37125  * @static
37126  * @type Number
37127  */
37128 Roo.bootstrap.SplitBar.VERTICAL = 1;
37129
37130 /**
37131  * Orientation constant - Create a horizontal SplitBar
37132  * @static
37133  * @type Number
37134  */
37135 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37136
37137 /**
37138  * Placement constant - The resizing element is to the left of the splitter element
37139  * @static
37140  * @type Number
37141  */
37142 Roo.bootstrap.SplitBar.LEFT = 1;
37143
37144 /**
37145  * Placement constant - The resizing element is to the right of the splitter element
37146  * @static
37147  * @type Number
37148  */
37149 Roo.bootstrap.SplitBar.RIGHT = 2;
37150
37151 /**
37152  * Placement constant - The resizing element is positioned above the splitter element
37153  * @static
37154  * @type Number
37155  */
37156 Roo.bootstrap.SplitBar.TOP = 3;
37157
37158 /**
37159  * Placement constant - The resizing element is positioned under splitter element
37160  * @static
37161  * @type Number
37162  */
37163 Roo.bootstrap.SplitBar.BOTTOM = 4;
37164 Roo.namespace("Roo.bootstrap.layout");/*
37165  * Based on:
37166  * Ext JS Library 1.1.1
37167  * Copyright(c) 2006-2007, Ext JS, LLC.
37168  *
37169  * Originally Released Under LGPL - original licence link has changed is not relivant.
37170  *
37171  * Fork - LGPL
37172  * <script type="text/javascript">
37173  */
37174
37175 /**
37176  * @class Roo.bootstrap.layout.Manager
37177  * @extends Roo.bootstrap.Component
37178  * Base class for layout managers.
37179  */
37180 Roo.bootstrap.layout.Manager = function(config)
37181 {
37182     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37183
37184
37185
37186
37187
37188     /** false to disable window resize monitoring @type Boolean */
37189     this.monitorWindowResize = true;
37190     this.regions = {};
37191     this.addEvents({
37192         /**
37193          * @event layout
37194          * Fires when a layout is performed.
37195          * @param {Roo.LayoutManager} this
37196          */
37197         "layout" : true,
37198         /**
37199          * @event regionresized
37200          * Fires when the user resizes a region.
37201          * @param {Roo.LayoutRegion} region The resized region
37202          * @param {Number} newSize The new size (width for east/west, height for north/south)
37203          */
37204         "regionresized" : true,
37205         /**
37206          * @event regioncollapsed
37207          * Fires when a region is collapsed.
37208          * @param {Roo.LayoutRegion} region The collapsed region
37209          */
37210         "regioncollapsed" : true,
37211         /**
37212          * @event regionexpanded
37213          * Fires when a region is expanded.
37214          * @param {Roo.LayoutRegion} region The expanded region
37215          */
37216         "regionexpanded" : true
37217     });
37218     this.updating = false;
37219
37220     if (config.el) {
37221         this.el = Roo.get(config.el);
37222         this.initEvents();
37223     }
37224
37225 };
37226
37227 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37228
37229
37230     regions : null,
37231
37232     monitorWindowResize : true,
37233
37234
37235     updating : false,
37236
37237
37238     onRender : function(ct, position)
37239     {
37240         if(!this.el){
37241             this.el = Roo.get(ct);
37242             this.initEvents();
37243         }
37244         //this.fireEvent('render',this);
37245     },
37246
37247
37248     initEvents: function()
37249     {
37250
37251
37252         // ie scrollbar fix
37253         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37254             document.body.scroll = "no";
37255         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37256             this.el.position('relative');
37257         }
37258         this.id = this.el.id;
37259         this.el.addClass("roo-layout-container");
37260         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37261         if(this.el.dom != document.body ) {
37262             this.el.on('resize', this.layout,this);
37263             this.el.on('show', this.layout,this);
37264         }
37265
37266     },
37267
37268     /**
37269      * Returns true if this layout is currently being updated
37270      * @return {Boolean}
37271      */
37272     isUpdating : function(){
37273         return this.updating;
37274     },
37275
37276     /**
37277      * Suspend the LayoutManager from doing auto-layouts while
37278      * making multiple add or remove calls
37279      */
37280     beginUpdate : function(){
37281         this.updating = true;
37282     },
37283
37284     /**
37285      * Restore auto-layouts and optionally disable the manager from performing a layout
37286      * @param {Boolean} noLayout true to disable a layout update
37287      */
37288     endUpdate : function(noLayout){
37289         this.updating = false;
37290         if(!noLayout){
37291             this.layout();
37292         }
37293     },
37294
37295     layout: function(){
37296         // abstract...
37297     },
37298
37299     onRegionResized : function(region, newSize){
37300         this.fireEvent("regionresized", region, newSize);
37301         this.layout();
37302     },
37303
37304     onRegionCollapsed : function(region){
37305         this.fireEvent("regioncollapsed", region);
37306     },
37307
37308     onRegionExpanded : function(region){
37309         this.fireEvent("regionexpanded", region);
37310     },
37311
37312     /**
37313      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37314      * performs box-model adjustments.
37315      * @return {Object} The size as an object {width: (the width), height: (the height)}
37316      */
37317     getViewSize : function()
37318     {
37319         var size;
37320         if(this.el.dom != document.body){
37321             size = this.el.getSize();
37322         }else{
37323             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37324         }
37325         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37326         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37327         return size;
37328     },
37329
37330     /**
37331      * Returns the Element this layout is bound to.
37332      * @return {Roo.Element}
37333      */
37334     getEl : function(){
37335         return this.el;
37336     },
37337
37338     /**
37339      * Returns the specified region.
37340      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37341      * @return {Roo.LayoutRegion}
37342      */
37343     getRegion : function(target){
37344         return this.regions[target.toLowerCase()];
37345     },
37346
37347     onWindowResize : function(){
37348         if(this.monitorWindowResize){
37349             this.layout();
37350         }
37351     }
37352 });
37353 /*
37354  * Based on:
37355  * Ext JS Library 1.1.1
37356  * Copyright(c) 2006-2007, Ext JS, LLC.
37357  *
37358  * Originally Released Under LGPL - original licence link has changed is not relivant.
37359  *
37360  * Fork - LGPL
37361  * <script type="text/javascript">
37362  */
37363 /**
37364  * @class Roo.bootstrap.layout.Border
37365  * @extends Roo.bootstrap.layout.Manager
37366  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37367  * please see: examples/bootstrap/nested.html<br><br>
37368  
37369 <b>The container the layout is rendered into can be either the body element or any other element.
37370 If it is not the body element, the container needs to either be an absolute positioned element,
37371 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37372 the container size if it is not the body element.</b>
37373
37374 * @constructor
37375 * Create a new Border
37376 * @param {Object} config Configuration options
37377  */
37378 Roo.bootstrap.layout.Border = function(config){
37379     config = config || {};
37380     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37381     
37382     
37383     
37384     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37385         if(config[region]){
37386             config[region].region = region;
37387             this.addRegion(config[region]);
37388         }
37389     },this);
37390     
37391 };
37392
37393 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37394
37395 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37396     
37397     parent : false, // this might point to a 'nest' or a ???
37398     
37399     /**
37400      * Creates and adds a new region if it doesn't already exist.
37401      * @param {String} target The target region key (north, south, east, west or center).
37402      * @param {Object} config The regions config object
37403      * @return {BorderLayoutRegion} The new region
37404      */
37405     addRegion : function(config)
37406     {
37407         if(!this.regions[config.region]){
37408             var r = this.factory(config);
37409             this.bindRegion(r);
37410         }
37411         return this.regions[config.region];
37412     },
37413
37414     // private (kinda)
37415     bindRegion : function(r){
37416         this.regions[r.config.region] = r;
37417         
37418         r.on("visibilitychange",    this.layout, this);
37419         r.on("paneladded",          this.layout, this);
37420         r.on("panelremoved",        this.layout, this);
37421         r.on("invalidated",         this.layout, this);
37422         r.on("resized",             this.onRegionResized, this);
37423         r.on("collapsed",           this.onRegionCollapsed, this);
37424         r.on("expanded",            this.onRegionExpanded, this);
37425     },
37426
37427     /**
37428      * Performs a layout update.
37429      */
37430     layout : function()
37431     {
37432         if(this.updating) {
37433             return;
37434         }
37435         
37436         // render all the rebions if they have not been done alreayd?
37437         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37438             if(this.regions[region] && !this.regions[region].bodyEl){
37439                 this.regions[region].onRender(this.el)
37440             }
37441         },this);
37442         
37443         var size = this.getViewSize();
37444         var w = size.width;
37445         var h = size.height;
37446         var centerW = w;
37447         var centerH = h;
37448         var centerY = 0;
37449         var centerX = 0;
37450         //var x = 0, y = 0;
37451
37452         var rs = this.regions;
37453         var north = rs["north"];
37454         var south = rs["south"]; 
37455         var west = rs["west"];
37456         var east = rs["east"];
37457         var center = rs["center"];
37458         //if(this.hideOnLayout){ // not supported anymore
37459             //c.el.setStyle("display", "none");
37460         //}
37461         if(north && north.isVisible()){
37462             var b = north.getBox();
37463             var m = north.getMargins();
37464             b.width = w - (m.left+m.right);
37465             b.x = m.left;
37466             b.y = m.top;
37467             centerY = b.height + b.y + m.bottom;
37468             centerH -= centerY;
37469             north.updateBox(this.safeBox(b));
37470         }
37471         if(south && south.isVisible()){
37472             var b = south.getBox();
37473             var m = south.getMargins();
37474             b.width = w - (m.left+m.right);
37475             b.x = m.left;
37476             var totalHeight = (b.height + m.top + m.bottom);
37477             b.y = h - totalHeight + m.top;
37478             centerH -= totalHeight;
37479             south.updateBox(this.safeBox(b));
37480         }
37481         if(west && west.isVisible()){
37482             var b = west.getBox();
37483             var m = west.getMargins();
37484             b.height = centerH - (m.top+m.bottom);
37485             b.x = m.left;
37486             b.y = centerY + m.top;
37487             var totalWidth = (b.width + m.left + m.right);
37488             centerX += totalWidth;
37489             centerW -= totalWidth;
37490             west.updateBox(this.safeBox(b));
37491         }
37492         if(east && east.isVisible()){
37493             var b = east.getBox();
37494             var m = east.getMargins();
37495             b.height = centerH - (m.top+m.bottom);
37496             var totalWidth = (b.width + m.left + m.right);
37497             b.x = w - totalWidth + m.left;
37498             b.y = centerY + m.top;
37499             centerW -= totalWidth;
37500             east.updateBox(this.safeBox(b));
37501         }
37502         if(center){
37503             var m = center.getMargins();
37504             var centerBox = {
37505                 x: centerX + m.left,
37506                 y: centerY + m.top,
37507                 width: centerW - (m.left+m.right),
37508                 height: centerH - (m.top+m.bottom)
37509             };
37510             //if(this.hideOnLayout){
37511                 //center.el.setStyle("display", "block");
37512             //}
37513             center.updateBox(this.safeBox(centerBox));
37514         }
37515         this.el.repaint();
37516         this.fireEvent("layout", this);
37517     },
37518
37519     // private
37520     safeBox : function(box){
37521         box.width = Math.max(0, box.width);
37522         box.height = Math.max(0, box.height);
37523         return box;
37524     },
37525
37526     /**
37527      * Adds a ContentPanel (or subclass) to this layout.
37528      * @param {String} target The target region key (north, south, east, west or center).
37529      * @param {Roo.ContentPanel} panel The panel to add
37530      * @return {Roo.ContentPanel} The added panel
37531      */
37532     add : function(target, panel){
37533          
37534         target = target.toLowerCase();
37535         return this.regions[target].add(panel);
37536     },
37537
37538     /**
37539      * Remove a ContentPanel (or subclass) to this layout.
37540      * @param {String} target The target region key (north, south, east, west or center).
37541      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37542      * @return {Roo.ContentPanel} The removed panel
37543      */
37544     remove : function(target, panel){
37545         target = target.toLowerCase();
37546         return this.regions[target].remove(panel);
37547     },
37548
37549     /**
37550      * Searches all regions for a panel with the specified id
37551      * @param {String} panelId
37552      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37553      */
37554     findPanel : function(panelId){
37555         var rs = this.regions;
37556         for(var target in rs){
37557             if(typeof rs[target] != "function"){
37558                 var p = rs[target].getPanel(panelId);
37559                 if(p){
37560                     return p;
37561                 }
37562             }
37563         }
37564         return null;
37565     },
37566
37567     /**
37568      * Searches all regions for a panel with the specified id and activates (shows) it.
37569      * @param {String/ContentPanel} panelId The panels id or the panel itself
37570      * @return {Roo.ContentPanel} The shown panel or null
37571      */
37572     showPanel : function(panelId) {
37573       var rs = this.regions;
37574       for(var target in rs){
37575          var r = rs[target];
37576          if(typeof r != "function"){
37577             if(r.hasPanel(panelId)){
37578                return r.showPanel(panelId);
37579             }
37580          }
37581       }
37582       return null;
37583    },
37584
37585    /**
37586      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37587      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37588      */
37589    /*
37590     restoreState : function(provider){
37591         if(!provider){
37592             provider = Roo.state.Manager;
37593         }
37594         var sm = new Roo.LayoutStateManager();
37595         sm.init(this, provider);
37596     },
37597 */
37598  
37599  
37600     /**
37601      * Adds a xtype elements to the layout.
37602      * <pre><code>
37603
37604 layout.addxtype({
37605        xtype : 'ContentPanel',
37606        region: 'west',
37607        items: [ .... ]
37608    }
37609 );
37610
37611 layout.addxtype({
37612         xtype : 'NestedLayoutPanel',
37613         region: 'west',
37614         layout: {
37615            center: { },
37616            west: { }   
37617         },
37618         items : [ ... list of content panels or nested layout panels.. ]
37619    }
37620 );
37621 </code></pre>
37622      * @param {Object} cfg Xtype definition of item to add.
37623      */
37624     addxtype : function(cfg)
37625     {
37626         // basically accepts a pannel...
37627         // can accept a layout region..!?!?
37628         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37629         
37630         
37631         // theory?  children can only be panels??
37632         
37633         //if (!cfg.xtype.match(/Panel$/)) {
37634         //    return false;
37635         //}
37636         var ret = false;
37637         
37638         if (typeof(cfg.region) == 'undefined') {
37639             Roo.log("Failed to add Panel, region was not set");
37640             Roo.log(cfg);
37641             return false;
37642         }
37643         var region = cfg.region;
37644         delete cfg.region;
37645         
37646           
37647         var xitems = [];
37648         if (cfg.items) {
37649             xitems = cfg.items;
37650             delete cfg.items;
37651         }
37652         var nb = false;
37653         
37654         if ( region == 'center') {
37655             Roo.log("Center: " + cfg.title);
37656         }
37657         
37658         
37659         switch(cfg.xtype) 
37660         {
37661             case 'Content':  // ContentPanel (el, cfg)
37662             case 'Scroll':  // ContentPanel (el, cfg)
37663             case 'View': 
37664                 cfg.autoCreate = cfg.autoCreate || true;
37665                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37666                 //} else {
37667                 //    var el = this.el.createChild();
37668                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37669                 //}
37670                 
37671                 this.add(region, ret);
37672                 break;
37673             
37674             /*
37675             case 'TreePanel': // our new panel!
37676                 cfg.el = this.el.createChild();
37677                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37678                 this.add(region, ret);
37679                 break;
37680             */
37681             
37682             case 'Nest': 
37683                 // create a new Layout (which is  a Border Layout...
37684                 
37685                 var clayout = cfg.layout;
37686                 clayout.el  = this.el.createChild();
37687                 clayout.items   = clayout.items  || [];
37688                 
37689                 delete cfg.layout;
37690                 
37691                 // replace this exitems with the clayout ones..
37692                 xitems = clayout.items;
37693                  
37694                 // force background off if it's in center...
37695                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37696                     cfg.background = false;
37697                 }
37698                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37699                 
37700                 
37701                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37702                 //console.log('adding nested layout panel '  + cfg.toSource());
37703                 this.add(region, ret);
37704                 nb = {}; /// find first...
37705                 break;
37706             
37707             case 'Grid':
37708                 
37709                 // needs grid and region
37710                 
37711                 //var el = this.getRegion(region).el.createChild();
37712                 /*
37713                  *var el = this.el.createChild();
37714                 // create the grid first...
37715                 cfg.grid.container = el;
37716                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37717                 */
37718                 
37719                 if (region == 'center' && this.active ) {
37720                     cfg.background = false;
37721                 }
37722                 
37723                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37724                 
37725                 this.add(region, ret);
37726                 /*
37727                 if (cfg.background) {
37728                     // render grid on panel activation (if panel background)
37729                     ret.on('activate', function(gp) {
37730                         if (!gp.grid.rendered) {
37731                     //        gp.grid.render(el);
37732                         }
37733                     });
37734                 } else {
37735                   //  cfg.grid.render(el);
37736                 }
37737                 */
37738                 break;
37739            
37740            
37741             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37742                 // it was the old xcomponent building that caused this before.
37743                 // espeically if border is the top element in the tree.
37744                 ret = this;
37745                 break; 
37746                 
37747                     
37748                 
37749                 
37750                 
37751             default:
37752                 /*
37753                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37754                     
37755                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37756                     this.add(region, ret);
37757                 } else {
37758                 */
37759                     Roo.log(cfg);
37760                     throw "Can not add '" + cfg.xtype + "' to Border";
37761                     return null;
37762              
37763                                 
37764              
37765         }
37766         this.beginUpdate();
37767         // add children..
37768         var region = '';
37769         var abn = {};
37770         Roo.each(xitems, function(i)  {
37771             region = nb && i.region ? i.region : false;
37772             
37773             var add = ret.addxtype(i);
37774            
37775             if (region) {
37776                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37777                 if (!i.background) {
37778                     abn[region] = nb[region] ;
37779                 }
37780             }
37781             
37782         });
37783         this.endUpdate();
37784
37785         // make the last non-background panel active..
37786         //if (nb) { Roo.log(abn); }
37787         if (nb) {
37788             
37789             for(var r in abn) {
37790                 region = this.getRegion(r);
37791                 if (region) {
37792                     // tried using nb[r], but it does not work..
37793                      
37794                     region.showPanel(abn[r]);
37795                    
37796                 }
37797             }
37798         }
37799         return ret;
37800         
37801     },
37802     
37803     
37804 // private
37805     factory : function(cfg)
37806     {
37807         
37808         var validRegions = Roo.bootstrap.layout.Border.regions;
37809
37810         var target = cfg.region;
37811         cfg.mgr = this;
37812         
37813         var r = Roo.bootstrap.layout;
37814         Roo.log(target);
37815         switch(target){
37816             case "north":
37817                 return new r.North(cfg);
37818             case "south":
37819                 return new r.South(cfg);
37820             case "east":
37821                 return new r.East(cfg);
37822             case "west":
37823                 return new r.West(cfg);
37824             case "center":
37825                 return new r.Center(cfg);
37826         }
37827         throw 'Layout region "'+target+'" not supported.';
37828     }
37829     
37830     
37831 });
37832  /*
37833  * Based on:
37834  * Ext JS Library 1.1.1
37835  * Copyright(c) 2006-2007, Ext JS, LLC.
37836  *
37837  * Originally Released Under LGPL - original licence link has changed is not relivant.
37838  *
37839  * Fork - LGPL
37840  * <script type="text/javascript">
37841  */
37842  
37843 /**
37844  * @class Roo.bootstrap.layout.Basic
37845  * @extends Roo.util.Observable
37846  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37847  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37848  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37849  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37850  * @cfg {string}   region  the region that it inhabits..
37851  * @cfg {bool}   skipConfig skip config?
37852  * 
37853
37854  */
37855 Roo.bootstrap.layout.Basic = function(config){
37856     
37857     this.mgr = config.mgr;
37858     
37859     this.position = config.region;
37860     
37861     var skipConfig = config.skipConfig;
37862     
37863     this.events = {
37864         /**
37865          * @scope Roo.BasicLayoutRegion
37866          */
37867         
37868         /**
37869          * @event beforeremove
37870          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37871          * @param {Roo.LayoutRegion} this
37872          * @param {Roo.ContentPanel} panel The panel
37873          * @param {Object} e The cancel event object
37874          */
37875         "beforeremove" : true,
37876         /**
37877          * @event invalidated
37878          * Fires when the layout for this region is changed.
37879          * @param {Roo.LayoutRegion} this
37880          */
37881         "invalidated" : true,
37882         /**
37883          * @event visibilitychange
37884          * Fires when this region is shown or hidden 
37885          * @param {Roo.LayoutRegion} this
37886          * @param {Boolean} visibility true or false
37887          */
37888         "visibilitychange" : true,
37889         /**
37890          * @event paneladded
37891          * Fires when a panel is added. 
37892          * @param {Roo.LayoutRegion} this
37893          * @param {Roo.ContentPanel} panel The panel
37894          */
37895         "paneladded" : true,
37896         /**
37897          * @event panelremoved
37898          * Fires when a panel is removed. 
37899          * @param {Roo.LayoutRegion} this
37900          * @param {Roo.ContentPanel} panel The panel
37901          */
37902         "panelremoved" : true,
37903         /**
37904          * @event beforecollapse
37905          * Fires when this region before collapse.
37906          * @param {Roo.LayoutRegion} this
37907          */
37908         "beforecollapse" : true,
37909         /**
37910          * @event collapsed
37911          * Fires when this region is collapsed.
37912          * @param {Roo.LayoutRegion} this
37913          */
37914         "collapsed" : true,
37915         /**
37916          * @event expanded
37917          * Fires when this region is expanded.
37918          * @param {Roo.LayoutRegion} this
37919          */
37920         "expanded" : true,
37921         /**
37922          * @event slideshow
37923          * Fires when this region is slid into view.
37924          * @param {Roo.LayoutRegion} this
37925          */
37926         "slideshow" : true,
37927         /**
37928          * @event slidehide
37929          * Fires when this region slides out of view. 
37930          * @param {Roo.LayoutRegion} this
37931          */
37932         "slidehide" : true,
37933         /**
37934          * @event panelactivated
37935          * Fires when a panel is activated. 
37936          * @param {Roo.LayoutRegion} this
37937          * @param {Roo.ContentPanel} panel The activated panel
37938          */
37939         "panelactivated" : true,
37940         /**
37941          * @event resized
37942          * Fires when the user resizes this region. 
37943          * @param {Roo.LayoutRegion} this
37944          * @param {Number} newSize The new size (width for east/west, height for north/south)
37945          */
37946         "resized" : true
37947     };
37948     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37949     this.panels = new Roo.util.MixedCollection();
37950     this.panels.getKey = this.getPanelId.createDelegate(this);
37951     this.box = null;
37952     this.activePanel = null;
37953     // ensure listeners are added...
37954     
37955     if (config.listeners || config.events) {
37956         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37957             listeners : config.listeners || {},
37958             events : config.events || {}
37959         });
37960     }
37961     
37962     if(skipConfig !== true){
37963         this.applyConfig(config);
37964     }
37965 };
37966
37967 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37968 {
37969     getPanelId : function(p){
37970         return p.getId();
37971     },
37972     
37973     applyConfig : function(config){
37974         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37975         this.config = config;
37976         
37977     },
37978     
37979     /**
37980      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37981      * the width, for horizontal (north, south) the height.
37982      * @param {Number} newSize The new width or height
37983      */
37984     resizeTo : function(newSize){
37985         var el = this.el ? this.el :
37986                  (this.activePanel ? this.activePanel.getEl() : null);
37987         if(el){
37988             switch(this.position){
37989                 case "east":
37990                 case "west":
37991                     el.setWidth(newSize);
37992                     this.fireEvent("resized", this, newSize);
37993                 break;
37994                 case "north":
37995                 case "south":
37996                     el.setHeight(newSize);
37997                     this.fireEvent("resized", this, newSize);
37998                 break;                
37999             }
38000         }
38001     },
38002     
38003     getBox : function(){
38004         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38005     },
38006     
38007     getMargins : function(){
38008         return this.margins;
38009     },
38010     
38011     updateBox : function(box){
38012         this.box = box;
38013         var el = this.activePanel.getEl();
38014         el.dom.style.left = box.x + "px";
38015         el.dom.style.top = box.y + "px";
38016         this.activePanel.setSize(box.width, box.height);
38017     },
38018     
38019     /**
38020      * Returns the container element for this region.
38021      * @return {Roo.Element}
38022      */
38023     getEl : function(){
38024         return this.activePanel;
38025     },
38026     
38027     /**
38028      * Returns true if this region is currently visible.
38029      * @return {Boolean}
38030      */
38031     isVisible : function(){
38032         return this.activePanel ? true : false;
38033     },
38034     
38035     setActivePanel : function(panel){
38036         panel = this.getPanel(panel);
38037         if(this.activePanel && this.activePanel != panel){
38038             this.activePanel.setActiveState(false);
38039             this.activePanel.getEl().setLeftTop(-10000,-10000);
38040         }
38041         this.activePanel = panel;
38042         panel.setActiveState(true);
38043         if(this.box){
38044             panel.setSize(this.box.width, this.box.height);
38045         }
38046         this.fireEvent("panelactivated", this, panel);
38047         this.fireEvent("invalidated");
38048     },
38049     
38050     /**
38051      * Show the specified panel.
38052      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38053      * @return {Roo.ContentPanel} The shown panel or null
38054      */
38055     showPanel : function(panel){
38056         panel = this.getPanel(panel);
38057         if(panel){
38058             this.setActivePanel(panel);
38059         }
38060         return panel;
38061     },
38062     
38063     /**
38064      * Get the active panel for this region.
38065      * @return {Roo.ContentPanel} The active panel or null
38066      */
38067     getActivePanel : function(){
38068         return this.activePanel;
38069     },
38070     
38071     /**
38072      * Add the passed ContentPanel(s)
38073      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38074      * @return {Roo.ContentPanel} The panel added (if only one was added)
38075      */
38076     add : function(panel){
38077         if(arguments.length > 1){
38078             for(var i = 0, len = arguments.length; i < len; i++) {
38079                 this.add(arguments[i]);
38080             }
38081             return null;
38082         }
38083         if(this.hasPanel(panel)){
38084             this.showPanel(panel);
38085             return panel;
38086         }
38087         var el = panel.getEl();
38088         if(el.dom.parentNode != this.mgr.el.dom){
38089             this.mgr.el.dom.appendChild(el.dom);
38090         }
38091         if(panel.setRegion){
38092             panel.setRegion(this);
38093         }
38094         this.panels.add(panel);
38095         el.setStyle("position", "absolute");
38096         if(!panel.background){
38097             this.setActivePanel(panel);
38098             if(this.config.initialSize && this.panels.getCount()==1){
38099                 this.resizeTo(this.config.initialSize);
38100             }
38101         }
38102         this.fireEvent("paneladded", this, panel);
38103         return panel;
38104     },
38105     
38106     /**
38107      * Returns true if the panel is in this region.
38108      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38109      * @return {Boolean}
38110      */
38111     hasPanel : function(panel){
38112         if(typeof panel == "object"){ // must be panel obj
38113             panel = panel.getId();
38114         }
38115         return this.getPanel(panel) ? true : false;
38116     },
38117     
38118     /**
38119      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38120      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38121      * @param {Boolean} preservePanel Overrides the config preservePanel option
38122      * @return {Roo.ContentPanel} The panel that was removed
38123      */
38124     remove : function(panel, preservePanel){
38125         panel = this.getPanel(panel);
38126         if(!panel){
38127             return null;
38128         }
38129         var e = {};
38130         this.fireEvent("beforeremove", this, panel, e);
38131         if(e.cancel === true){
38132             return null;
38133         }
38134         var panelId = panel.getId();
38135         this.panels.removeKey(panelId);
38136         return panel;
38137     },
38138     
38139     /**
38140      * Returns the panel specified or null if it's not in this region.
38141      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38142      * @return {Roo.ContentPanel}
38143      */
38144     getPanel : function(id){
38145         if(typeof id == "object"){ // must be panel obj
38146             return id;
38147         }
38148         return this.panels.get(id);
38149     },
38150     
38151     /**
38152      * Returns this regions position (north/south/east/west/center).
38153      * @return {String} 
38154      */
38155     getPosition: function(){
38156         return this.position;    
38157     }
38158 });/*
38159  * Based on:
38160  * Ext JS Library 1.1.1
38161  * Copyright(c) 2006-2007, Ext JS, LLC.
38162  *
38163  * Originally Released Under LGPL - original licence link has changed is not relivant.
38164  *
38165  * Fork - LGPL
38166  * <script type="text/javascript">
38167  */
38168  
38169 /**
38170  * @class Roo.bootstrap.layout.Region
38171  * @extends Roo.bootstrap.layout.Basic
38172  * This class represents a region in a layout manager.
38173  
38174  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38175  * @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})
38176  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38177  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38178  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38179  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38180  * @cfg {String}    title           The title for the region (overrides panel titles)
38181  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38182  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38183  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38184  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38185  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38186  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38187  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38188  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38189  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38190  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38191
38192  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38193  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38194  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38195  * @cfg {Number}    width           For East/West panels
38196  * @cfg {Number}    height          For North/South panels
38197  * @cfg {Boolean}   split           To show the splitter
38198  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38199  * 
38200  * @cfg {string}   cls             Extra CSS classes to add to region
38201  * 
38202  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38203  * @cfg {string}   region  the region that it inhabits..
38204  *
38205
38206  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38207  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38208
38209  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38210  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38211  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38212  */
38213 Roo.bootstrap.layout.Region = function(config)
38214 {
38215     this.applyConfig(config);
38216
38217     var mgr = config.mgr;
38218     var pos = config.region;
38219     config.skipConfig = true;
38220     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38221     
38222     if (mgr.el) {
38223         this.onRender(mgr.el);   
38224     }
38225      
38226     this.visible = true;
38227     this.collapsed = false;
38228     this.unrendered_panels = [];
38229 };
38230
38231 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38232
38233     position: '', // set by wrapper (eg. north/south etc..)
38234     unrendered_panels : null,  // unrendered panels.
38235     
38236     tabPosition : false,
38237     
38238     mgr: false, // points to 'Border'
38239     
38240     
38241     createBody : function(){
38242         /** This region's body element 
38243         * @type Roo.Element */
38244         this.bodyEl = this.el.createChild({
38245                 tag: "div",
38246                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38247         });
38248     },
38249
38250     onRender: function(ctr, pos)
38251     {
38252         var dh = Roo.DomHelper;
38253         /** This region's container element 
38254         * @type Roo.Element */
38255         this.el = dh.append(ctr.dom, {
38256                 tag: "div",
38257                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38258             }, true);
38259         /** This region's title element 
38260         * @type Roo.Element */
38261     
38262         this.titleEl = dh.append(this.el.dom,  {
38263                 tag: "div",
38264                 unselectable: "on",
38265                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38266                 children:[
38267                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38268                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38269                 ]
38270             }, true);
38271         
38272         this.titleEl.enableDisplayMode();
38273         /** This region's title text element 
38274         * @type HTMLElement */
38275         this.titleTextEl = this.titleEl.dom.firstChild;
38276         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38277         /*
38278         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38279         this.closeBtn.enableDisplayMode();
38280         this.closeBtn.on("click", this.closeClicked, this);
38281         this.closeBtn.hide();
38282     */
38283         this.createBody(this.config);
38284         if(this.config.hideWhenEmpty){
38285             this.hide();
38286             this.on("paneladded", this.validateVisibility, this);
38287             this.on("panelremoved", this.validateVisibility, this);
38288         }
38289         if(this.autoScroll){
38290             this.bodyEl.setStyle("overflow", "auto");
38291         }else{
38292             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38293         }
38294         //if(c.titlebar !== false){
38295             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38296                 this.titleEl.hide();
38297             }else{
38298                 this.titleEl.show();
38299                 if(this.config.title){
38300                     this.titleTextEl.innerHTML = this.config.title;
38301                 }
38302             }
38303         //}
38304         if(this.config.collapsed){
38305             this.collapse(true);
38306         }
38307         if(this.config.hidden){
38308             this.hide();
38309         }
38310         
38311         if (this.unrendered_panels && this.unrendered_panels.length) {
38312             for (var i =0;i< this.unrendered_panels.length; i++) {
38313                 this.add(this.unrendered_panels[i]);
38314             }
38315             this.unrendered_panels = null;
38316             
38317         }
38318         
38319     },
38320     
38321     applyConfig : function(c)
38322     {
38323         /*
38324          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38325             var dh = Roo.DomHelper;
38326             if(c.titlebar !== false){
38327                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38328                 this.collapseBtn.on("click", this.collapse, this);
38329                 this.collapseBtn.enableDisplayMode();
38330                 /*
38331                 if(c.showPin === true || this.showPin){
38332                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38333                     this.stickBtn.enableDisplayMode();
38334                     this.stickBtn.on("click", this.expand, this);
38335                     this.stickBtn.hide();
38336                 }
38337                 
38338             }
38339             */
38340             /** This region's collapsed element
38341             * @type Roo.Element */
38342             /*
38343              *
38344             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38345                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38346             ]}, true);
38347             
38348             if(c.floatable !== false){
38349                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38350                this.collapsedEl.on("click", this.collapseClick, this);
38351             }
38352
38353             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38354                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38355                    id: "message", unselectable: "on", style:{"float":"left"}});
38356                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38357              }
38358             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38359             this.expandBtn.on("click", this.expand, this);
38360             
38361         }
38362         
38363         if(this.collapseBtn){
38364             this.collapseBtn.setVisible(c.collapsible == true);
38365         }
38366         
38367         this.cmargins = c.cmargins || this.cmargins ||
38368                          (this.position == "west" || this.position == "east" ?
38369                              {top: 0, left: 2, right:2, bottom: 0} :
38370                              {top: 2, left: 0, right:0, bottom: 2});
38371         */
38372         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38373         
38374         
38375         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38376         
38377         this.autoScroll = c.autoScroll || false;
38378         
38379         
38380        
38381         
38382         this.duration = c.duration || .30;
38383         this.slideDuration = c.slideDuration || .45;
38384         this.config = c;
38385        
38386     },
38387     /**
38388      * Returns true if this region is currently visible.
38389      * @return {Boolean}
38390      */
38391     isVisible : function(){
38392         return this.visible;
38393     },
38394
38395     /**
38396      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38397      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38398      */
38399     //setCollapsedTitle : function(title){
38400     //    title = title || "&#160;";
38401      //   if(this.collapsedTitleTextEl){
38402       //      this.collapsedTitleTextEl.innerHTML = title;
38403        // }
38404     //},
38405
38406     getBox : function(){
38407         var b;
38408       //  if(!this.collapsed){
38409             b = this.el.getBox(false, true);
38410        // }else{
38411           //  b = this.collapsedEl.getBox(false, true);
38412         //}
38413         return b;
38414     },
38415
38416     getMargins : function(){
38417         return this.margins;
38418         //return this.collapsed ? this.cmargins : this.margins;
38419     },
38420 /*
38421     highlight : function(){
38422         this.el.addClass("x-layout-panel-dragover");
38423     },
38424
38425     unhighlight : function(){
38426         this.el.removeClass("x-layout-panel-dragover");
38427     },
38428 */
38429     updateBox : function(box)
38430     {
38431         if (!this.bodyEl) {
38432             return; // not rendered yet..
38433         }
38434         
38435         this.box = box;
38436         if(!this.collapsed){
38437             this.el.dom.style.left = box.x + "px";
38438             this.el.dom.style.top = box.y + "px";
38439             this.updateBody(box.width, box.height);
38440         }else{
38441             this.collapsedEl.dom.style.left = box.x + "px";
38442             this.collapsedEl.dom.style.top = box.y + "px";
38443             this.collapsedEl.setSize(box.width, box.height);
38444         }
38445         if(this.tabs){
38446             this.tabs.autoSizeTabs();
38447         }
38448     },
38449
38450     updateBody : function(w, h)
38451     {
38452         if(w !== null){
38453             this.el.setWidth(w);
38454             w -= this.el.getBorderWidth("rl");
38455             if(this.config.adjustments){
38456                 w += this.config.adjustments[0];
38457             }
38458         }
38459         if(h !== null && h > 0){
38460             this.el.setHeight(h);
38461             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38462             h -= this.el.getBorderWidth("tb");
38463             if(this.config.adjustments){
38464                 h += this.config.adjustments[1];
38465             }
38466             this.bodyEl.setHeight(h);
38467             if(this.tabs){
38468                 h = this.tabs.syncHeight(h);
38469             }
38470         }
38471         if(this.panelSize){
38472             w = w !== null ? w : this.panelSize.width;
38473             h = h !== null ? h : this.panelSize.height;
38474         }
38475         if(this.activePanel){
38476             var el = this.activePanel.getEl();
38477             w = w !== null ? w : el.getWidth();
38478             h = h !== null ? h : el.getHeight();
38479             this.panelSize = {width: w, height: h};
38480             this.activePanel.setSize(w, h);
38481         }
38482         if(Roo.isIE && this.tabs){
38483             this.tabs.el.repaint();
38484         }
38485     },
38486
38487     /**
38488      * Returns the container element for this region.
38489      * @return {Roo.Element}
38490      */
38491     getEl : function(){
38492         return this.el;
38493     },
38494
38495     /**
38496      * Hides this region.
38497      */
38498     hide : function(){
38499         //if(!this.collapsed){
38500             this.el.dom.style.left = "-2000px";
38501             this.el.hide();
38502         //}else{
38503          //   this.collapsedEl.dom.style.left = "-2000px";
38504          //   this.collapsedEl.hide();
38505        // }
38506         this.visible = false;
38507         this.fireEvent("visibilitychange", this, false);
38508     },
38509
38510     /**
38511      * Shows this region if it was previously hidden.
38512      */
38513     show : function(){
38514         //if(!this.collapsed){
38515             this.el.show();
38516         //}else{
38517         //    this.collapsedEl.show();
38518        // }
38519         this.visible = true;
38520         this.fireEvent("visibilitychange", this, true);
38521     },
38522 /*
38523     closeClicked : function(){
38524         if(this.activePanel){
38525             this.remove(this.activePanel);
38526         }
38527     },
38528
38529     collapseClick : function(e){
38530         if(this.isSlid){
38531            e.stopPropagation();
38532            this.slideIn();
38533         }else{
38534            e.stopPropagation();
38535            this.slideOut();
38536         }
38537     },
38538 */
38539     /**
38540      * Collapses this region.
38541      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38542      */
38543     /*
38544     collapse : function(skipAnim, skipCheck = false){
38545         if(this.collapsed) {
38546             return;
38547         }
38548         
38549         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38550             
38551             this.collapsed = true;
38552             if(this.split){
38553                 this.split.el.hide();
38554             }
38555             if(this.config.animate && skipAnim !== true){
38556                 this.fireEvent("invalidated", this);
38557                 this.animateCollapse();
38558             }else{
38559                 this.el.setLocation(-20000,-20000);
38560                 this.el.hide();
38561                 this.collapsedEl.show();
38562                 this.fireEvent("collapsed", this);
38563                 this.fireEvent("invalidated", this);
38564             }
38565         }
38566         
38567     },
38568 */
38569     animateCollapse : function(){
38570         // overridden
38571     },
38572
38573     /**
38574      * Expands this region if it was previously collapsed.
38575      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38576      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38577      */
38578     /*
38579     expand : function(e, skipAnim){
38580         if(e) {
38581             e.stopPropagation();
38582         }
38583         if(!this.collapsed || this.el.hasActiveFx()) {
38584             return;
38585         }
38586         if(this.isSlid){
38587             this.afterSlideIn();
38588             skipAnim = true;
38589         }
38590         this.collapsed = false;
38591         if(this.config.animate && skipAnim !== true){
38592             this.animateExpand();
38593         }else{
38594             this.el.show();
38595             if(this.split){
38596                 this.split.el.show();
38597             }
38598             this.collapsedEl.setLocation(-2000,-2000);
38599             this.collapsedEl.hide();
38600             this.fireEvent("invalidated", this);
38601             this.fireEvent("expanded", this);
38602         }
38603     },
38604 */
38605     animateExpand : function(){
38606         // overridden
38607     },
38608
38609     initTabs : function()
38610     {
38611         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38612         
38613         var ts = new Roo.bootstrap.panel.Tabs({
38614             el: this.bodyEl.dom,
38615             region : this,
38616             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38617             disableTooltips: this.config.disableTabTips,
38618             toolbar : this.config.toolbar
38619         });
38620         
38621         if(this.config.hideTabs){
38622             ts.stripWrap.setDisplayed(false);
38623         }
38624         this.tabs = ts;
38625         ts.resizeTabs = this.config.resizeTabs === true;
38626         ts.minTabWidth = this.config.minTabWidth || 40;
38627         ts.maxTabWidth = this.config.maxTabWidth || 250;
38628         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38629         ts.monitorResize = false;
38630         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38631         ts.bodyEl.addClass('roo-layout-tabs-body');
38632         this.panels.each(this.initPanelAsTab, this);
38633     },
38634
38635     initPanelAsTab : function(panel){
38636         var ti = this.tabs.addTab(
38637             panel.getEl().id,
38638             panel.getTitle(),
38639             null,
38640             this.config.closeOnTab && panel.isClosable(),
38641             panel.tpl
38642         );
38643         if(panel.tabTip !== undefined){
38644             ti.setTooltip(panel.tabTip);
38645         }
38646         ti.on("activate", function(){
38647               this.setActivePanel(panel);
38648         }, this);
38649         
38650         if(this.config.closeOnTab){
38651             ti.on("beforeclose", function(t, e){
38652                 e.cancel = true;
38653                 this.remove(panel);
38654             }, this);
38655         }
38656         
38657         panel.tabItem = ti;
38658         
38659         return ti;
38660     },
38661
38662     updatePanelTitle : function(panel, title)
38663     {
38664         if(this.activePanel == panel){
38665             this.updateTitle(title);
38666         }
38667         if(this.tabs){
38668             var ti = this.tabs.getTab(panel.getEl().id);
38669             ti.setText(title);
38670             if(panel.tabTip !== undefined){
38671                 ti.setTooltip(panel.tabTip);
38672             }
38673         }
38674     },
38675
38676     updateTitle : function(title){
38677         if(this.titleTextEl && !this.config.title){
38678             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38679         }
38680     },
38681
38682     setActivePanel : function(panel)
38683     {
38684         panel = this.getPanel(panel);
38685         if(this.activePanel && this.activePanel != panel){
38686             if(this.activePanel.setActiveState(false) === false){
38687                 return;
38688             }
38689         }
38690         this.activePanel = panel;
38691         panel.setActiveState(true);
38692         if(this.panelSize){
38693             panel.setSize(this.panelSize.width, this.panelSize.height);
38694         }
38695         if(this.closeBtn){
38696             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38697         }
38698         this.updateTitle(panel.getTitle());
38699         if(this.tabs){
38700             this.fireEvent("invalidated", this);
38701         }
38702         this.fireEvent("panelactivated", this, panel);
38703     },
38704
38705     /**
38706      * Shows the specified panel.
38707      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38708      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38709      */
38710     showPanel : function(panel)
38711     {
38712         panel = this.getPanel(panel);
38713         if(panel){
38714             if(this.tabs){
38715                 var tab = this.tabs.getTab(panel.getEl().id);
38716                 if(tab.isHidden()){
38717                     this.tabs.unhideTab(tab.id);
38718                 }
38719                 tab.activate();
38720             }else{
38721                 this.setActivePanel(panel);
38722             }
38723         }
38724         return panel;
38725     },
38726
38727     /**
38728      * Get the active panel for this region.
38729      * @return {Roo.ContentPanel} The active panel or null
38730      */
38731     getActivePanel : function(){
38732         return this.activePanel;
38733     },
38734
38735     validateVisibility : function(){
38736         if(this.panels.getCount() < 1){
38737             this.updateTitle("&#160;");
38738             this.closeBtn.hide();
38739             this.hide();
38740         }else{
38741             if(!this.isVisible()){
38742                 this.show();
38743             }
38744         }
38745     },
38746
38747     /**
38748      * Adds the passed ContentPanel(s) to this region.
38749      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38750      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38751      */
38752     add : function(panel)
38753     {
38754         if(arguments.length > 1){
38755             for(var i = 0, len = arguments.length; i < len; i++) {
38756                 this.add(arguments[i]);
38757             }
38758             return null;
38759         }
38760         
38761         // if we have not been rendered yet, then we can not really do much of this..
38762         if (!this.bodyEl) {
38763             this.unrendered_panels.push(panel);
38764             return panel;
38765         }
38766         
38767         
38768         
38769         
38770         if(this.hasPanel(panel)){
38771             this.showPanel(panel);
38772             return panel;
38773         }
38774         panel.setRegion(this);
38775         this.panels.add(panel);
38776        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38777             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38778             // and hide them... ???
38779             this.bodyEl.dom.appendChild(panel.getEl().dom);
38780             if(panel.background !== true){
38781                 this.setActivePanel(panel);
38782             }
38783             this.fireEvent("paneladded", this, panel);
38784             return panel;
38785         }
38786         */
38787         if(!this.tabs){
38788             this.initTabs();
38789         }else{
38790             this.initPanelAsTab(panel);
38791         }
38792         
38793         
38794         if(panel.background !== true){
38795             this.tabs.activate(panel.getEl().id);
38796         }
38797         this.fireEvent("paneladded", this, panel);
38798         return panel;
38799     },
38800
38801     /**
38802      * Hides the tab for the specified panel.
38803      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38804      */
38805     hidePanel : function(panel){
38806         if(this.tabs && (panel = this.getPanel(panel))){
38807             this.tabs.hideTab(panel.getEl().id);
38808         }
38809     },
38810
38811     /**
38812      * Unhides the tab for a previously hidden panel.
38813      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38814      */
38815     unhidePanel : function(panel){
38816         if(this.tabs && (panel = this.getPanel(panel))){
38817             this.tabs.unhideTab(panel.getEl().id);
38818         }
38819     },
38820
38821     clearPanels : function(){
38822         while(this.panels.getCount() > 0){
38823              this.remove(this.panels.first());
38824         }
38825     },
38826
38827     /**
38828      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38829      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38830      * @param {Boolean} preservePanel Overrides the config preservePanel option
38831      * @return {Roo.ContentPanel} The panel that was removed
38832      */
38833     remove : function(panel, preservePanel)
38834     {
38835         panel = this.getPanel(panel);
38836         if(!panel){
38837             return null;
38838         }
38839         var e = {};
38840         this.fireEvent("beforeremove", this, panel, e);
38841         if(e.cancel === true){
38842             return null;
38843         }
38844         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38845         var panelId = panel.getId();
38846         this.panels.removeKey(panelId);
38847         if(preservePanel){
38848             document.body.appendChild(panel.getEl().dom);
38849         }
38850         if(this.tabs){
38851             this.tabs.removeTab(panel.getEl().id);
38852         }else if (!preservePanel){
38853             this.bodyEl.dom.removeChild(panel.getEl().dom);
38854         }
38855         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38856             var p = this.panels.first();
38857             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38858             tempEl.appendChild(p.getEl().dom);
38859             this.bodyEl.update("");
38860             this.bodyEl.dom.appendChild(p.getEl().dom);
38861             tempEl = null;
38862             this.updateTitle(p.getTitle());
38863             this.tabs = null;
38864             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38865             this.setActivePanel(p);
38866         }
38867         panel.setRegion(null);
38868         if(this.activePanel == panel){
38869             this.activePanel = null;
38870         }
38871         if(this.config.autoDestroy !== false && preservePanel !== true){
38872             try{panel.destroy();}catch(e){}
38873         }
38874         this.fireEvent("panelremoved", this, panel);
38875         return panel;
38876     },
38877
38878     /**
38879      * Returns the TabPanel component used by this region
38880      * @return {Roo.TabPanel}
38881      */
38882     getTabs : function(){
38883         return this.tabs;
38884     },
38885
38886     createTool : function(parentEl, className){
38887         var btn = Roo.DomHelper.append(parentEl, {
38888             tag: "div",
38889             cls: "x-layout-tools-button",
38890             children: [ {
38891                 tag: "div",
38892                 cls: "roo-layout-tools-button-inner " + className,
38893                 html: "&#160;"
38894             }]
38895         }, true);
38896         btn.addClassOnOver("roo-layout-tools-button-over");
38897         return btn;
38898     }
38899 });/*
38900  * Based on:
38901  * Ext JS Library 1.1.1
38902  * Copyright(c) 2006-2007, Ext JS, LLC.
38903  *
38904  * Originally Released Under LGPL - original licence link has changed is not relivant.
38905  *
38906  * Fork - LGPL
38907  * <script type="text/javascript">
38908  */
38909  
38910
38911
38912 /**
38913  * @class Roo.SplitLayoutRegion
38914  * @extends Roo.LayoutRegion
38915  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38916  */
38917 Roo.bootstrap.layout.Split = function(config){
38918     this.cursor = config.cursor;
38919     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38920 };
38921
38922 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38923 {
38924     splitTip : "Drag to resize.",
38925     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38926     useSplitTips : false,
38927
38928     applyConfig : function(config){
38929         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38930     },
38931     
38932     onRender : function(ctr,pos) {
38933         
38934         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38935         if(!this.config.split){
38936             return;
38937         }
38938         if(!this.split){
38939             
38940             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38941                             tag: "div",
38942                             id: this.el.id + "-split",
38943                             cls: "roo-layout-split roo-layout-split-"+this.position,
38944                             html: "&#160;"
38945             });
38946             /** The SplitBar for this region 
38947             * @type Roo.SplitBar */
38948             // does not exist yet...
38949             Roo.log([this.position, this.orientation]);
38950             
38951             this.split = new Roo.bootstrap.SplitBar({
38952                 dragElement : splitEl,
38953                 resizingElement: this.el,
38954                 orientation : this.orientation
38955             });
38956             
38957             this.split.on("moved", this.onSplitMove, this);
38958             this.split.useShim = this.config.useShim === true;
38959             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38960             if(this.useSplitTips){
38961                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38962             }
38963             //if(config.collapsible){
38964             //    this.split.el.on("dblclick", this.collapse,  this);
38965             //}
38966         }
38967         if(typeof this.config.minSize != "undefined"){
38968             this.split.minSize = this.config.minSize;
38969         }
38970         if(typeof this.config.maxSize != "undefined"){
38971             this.split.maxSize = this.config.maxSize;
38972         }
38973         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38974             this.hideSplitter();
38975         }
38976         
38977     },
38978
38979     getHMaxSize : function(){
38980          var cmax = this.config.maxSize || 10000;
38981          var center = this.mgr.getRegion("center");
38982          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38983     },
38984
38985     getVMaxSize : function(){
38986          var cmax = this.config.maxSize || 10000;
38987          var center = this.mgr.getRegion("center");
38988          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38989     },
38990
38991     onSplitMove : function(split, newSize){
38992         this.fireEvent("resized", this, newSize);
38993     },
38994     
38995     /** 
38996      * Returns the {@link Roo.SplitBar} for this region.
38997      * @return {Roo.SplitBar}
38998      */
38999     getSplitBar : function(){
39000         return this.split;
39001     },
39002     
39003     hide : function(){
39004         this.hideSplitter();
39005         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39006     },
39007
39008     hideSplitter : function(){
39009         if(this.split){
39010             this.split.el.setLocation(-2000,-2000);
39011             this.split.el.hide();
39012         }
39013     },
39014
39015     show : function(){
39016         if(this.split){
39017             this.split.el.show();
39018         }
39019         Roo.bootstrap.layout.Split.superclass.show.call(this);
39020     },
39021     
39022     beforeSlide: function(){
39023         if(Roo.isGecko){// firefox overflow auto bug workaround
39024             this.bodyEl.clip();
39025             if(this.tabs) {
39026                 this.tabs.bodyEl.clip();
39027             }
39028             if(this.activePanel){
39029                 this.activePanel.getEl().clip();
39030                 
39031                 if(this.activePanel.beforeSlide){
39032                     this.activePanel.beforeSlide();
39033                 }
39034             }
39035         }
39036     },
39037     
39038     afterSlide : function(){
39039         if(Roo.isGecko){// firefox overflow auto bug workaround
39040             this.bodyEl.unclip();
39041             if(this.tabs) {
39042                 this.tabs.bodyEl.unclip();
39043             }
39044             if(this.activePanel){
39045                 this.activePanel.getEl().unclip();
39046                 if(this.activePanel.afterSlide){
39047                     this.activePanel.afterSlide();
39048                 }
39049             }
39050         }
39051     },
39052
39053     initAutoHide : function(){
39054         if(this.autoHide !== false){
39055             if(!this.autoHideHd){
39056                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39057                 this.autoHideHd = {
39058                     "mouseout": function(e){
39059                         if(!e.within(this.el, true)){
39060                             st.delay(500);
39061                         }
39062                     },
39063                     "mouseover" : function(e){
39064                         st.cancel();
39065                     },
39066                     scope : this
39067                 };
39068             }
39069             this.el.on(this.autoHideHd);
39070         }
39071     },
39072
39073     clearAutoHide : function(){
39074         if(this.autoHide !== false){
39075             this.el.un("mouseout", this.autoHideHd.mouseout);
39076             this.el.un("mouseover", this.autoHideHd.mouseover);
39077         }
39078     },
39079
39080     clearMonitor : function(){
39081         Roo.get(document).un("click", this.slideInIf, this);
39082     },
39083
39084     // these names are backwards but not changed for compat
39085     slideOut : function(){
39086         if(this.isSlid || this.el.hasActiveFx()){
39087             return;
39088         }
39089         this.isSlid = true;
39090         if(this.collapseBtn){
39091             this.collapseBtn.hide();
39092         }
39093         this.closeBtnState = this.closeBtn.getStyle('display');
39094         this.closeBtn.hide();
39095         if(this.stickBtn){
39096             this.stickBtn.show();
39097         }
39098         this.el.show();
39099         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39100         this.beforeSlide();
39101         this.el.setStyle("z-index", 10001);
39102         this.el.slideIn(this.getSlideAnchor(), {
39103             callback: function(){
39104                 this.afterSlide();
39105                 this.initAutoHide();
39106                 Roo.get(document).on("click", this.slideInIf, this);
39107                 this.fireEvent("slideshow", this);
39108             },
39109             scope: this,
39110             block: true
39111         });
39112     },
39113
39114     afterSlideIn : function(){
39115         this.clearAutoHide();
39116         this.isSlid = false;
39117         this.clearMonitor();
39118         this.el.setStyle("z-index", "");
39119         if(this.collapseBtn){
39120             this.collapseBtn.show();
39121         }
39122         this.closeBtn.setStyle('display', this.closeBtnState);
39123         if(this.stickBtn){
39124             this.stickBtn.hide();
39125         }
39126         this.fireEvent("slidehide", this);
39127     },
39128
39129     slideIn : function(cb){
39130         if(!this.isSlid || this.el.hasActiveFx()){
39131             Roo.callback(cb);
39132             return;
39133         }
39134         this.isSlid = false;
39135         this.beforeSlide();
39136         this.el.slideOut(this.getSlideAnchor(), {
39137             callback: function(){
39138                 this.el.setLeftTop(-10000, -10000);
39139                 this.afterSlide();
39140                 this.afterSlideIn();
39141                 Roo.callback(cb);
39142             },
39143             scope: this,
39144             block: true
39145         });
39146     },
39147     
39148     slideInIf : function(e){
39149         if(!e.within(this.el)){
39150             this.slideIn();
39151         }
39152     },
39153
39154     animateCollapse : function(){
39155         this.beforeSlide();
39156         this.el.setStyle("z-index", 20000);
39157         var anchor = this.getSlideAnchor();
39158         this.el.slideOut(anchor, {
39159             callback : function(){
39160                 this.el.setStyle("z-index", "");
39161                 this.collapsedEl.slideIn(anchor, {duration:.3});
39162                 this.afterSlide();
39163                 this.el.setLocation(-10000,-10000);
39164                 this.el.hide();
39165                 this.fireEvent("collapsed", this);
39166             },
39167             scope: this,
39168             block: true
39169         });
39170     },
39171
39172     animateExpand : function(){
39173         this.beforeSlide();
39174         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39175         this.el.setStyle("z-index", 20000);
39176         this.collapsedEl.hide({
39177             duration:.1
39178         });
39179         this.el.slideIn(this.getSlideAnchor(), {
39180             callback : function(){
39181                 this.el.setStyle("z-index", "");
39182                 this.afterSlide();
39183                 if(this.split){
39184                     this.split.el.show();
39185                 }
39186                 this.fireEvent("invalidated", this);
39187                 this.fireEvent("expanded", this);
39188             },
39189             scope: this,
39190             block: true
39191         });
39192     },
39193
39194     anchors : {
39195         "west" : "left",
39196         "east" : "right",
39197         "north" : "top",
39198         "south" : "bottom"
39199     },
39200
39201     sanchors : {
39202         "west" : "l",
39203         "east" : "r",
39204         "north" : "t",
39205         "south" : "b"
39206     },
39207
39208     canchors : {
39209         "west" : "tl-tr",
39210         "east" : "tr-tl",
39211         "north" : "tl-bl",
39212         "south" : "bl-tl"
39213     },
39214
39215     getAnchor : function(){
39216         return this.anchors[this.position];
39217     },
39218
39219     getCollapseAnchor : function(){
39220         return this.canchors[this.position];
39221     },
39222
39223     getSlideAnchor : function(){
39224         return this.sanchors[this.position];
39225     },
39226
39227     getAlignAdj : function(){
39228         var cm = this.cmargins;
39229         switch(this.position){
39230             case "west":
39231                 return [0, 0];
39232             break;
39233             case "east":
39234                 return [0, 0];
39235             break;
39236             case "north":
39237                 return [0, 0];
39238             break;
39239             case "south":
39240                 return [0, 0];
39241             break;
39242         }
39243     },
39244
39245     getExpandAdj : function(){
39246         var c = this.collapsedEl, cm = this.cmargins;
39247         switch(this.position){
39248             case "west":
39249                 return [-(cm.right+c.getWidth()+cm.left), 0];
39250             break;
39251             case "east":
39252                 return [cm.right+c.getWidth()+cm.left, 0];
39253             break;
39254             case "north":
39255                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39256             break;
39257             case "south":
39258                 return [0, cm.top+cm.bottom+c.getHeight()];
39259             break;
39260         }
39261     }
39262 });/*
39263  * Based on:
39264  * Ext JS Library 1.1.1
39265  * Copyright(c) 2006-2007, Ext JS, LLC.
39266  *
39267  * Originally Released Under LGPL - original licence link has changed is not relivant.
39268  *
39269  * Fork - LGPL
39270  * <script type="text/javascript">
39271  */
39272 /*
39273  * These classes are private internal classes
39274  */
39275 Roo.bootstrap.layout.Center = function(config){
39276     config.region = "center";
39277     Roo.bootstrap.layout.Region.call(this, config);
39278     this.visible = true;
39279     this.minWidth = config.minWidth || 20;
39280     this.minHeight = config.minHeight || 20;
39281 };
39282
39283 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39284     hide : function(){
39285         // center panel can't be hidden
39286     },
39287     
39288     show : function(){
39289         // center panel can't be hidden
39290     },
39291     
39292     getMinWidth: function(){
39293         return this.minWidth;
39294     },
39295     
39296     getMinHeight: function(){
39297         return this.minHeight;
39298     }
39299 });
39300
39301
39302
39303
39304  
39305
39306
39307
39308
39309
39310
39311 Roo.bootstrap.layout.North = function(config)
39312 {
39313     config.region = 'north';
39314     config.cursor = 'n-resize';
39315     
39316     Roo.bootstrap.layout.Split.call(this, config);
39317     
39318     
39319     if(this.split){
39320         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39321         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39322         this.split.el.addClass("roo-layout-split-v");
39323     }
39324     //var size = config.initialSize || config.height;
39325     //if(this.el && typeof size != "undefined"){
39326     //    this.el.setHeight(size);
39327     //}
39328 };
39329 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39330 {
39331     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39332      
39333      
39334     onRender : function(ctr, pos)
39335     {
39336         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39337         var size = this.config.initialSize || this.config.height;
39338         if(this.el && typeof size != "undefined"){
39339             this.el.setHeight(size);
39340         }
39341     
39342     },
39343     
39344     getBox : function(){
39345         if(this.collapsed){
39346             return this.collapsedEl.getBox();
39347         }
39348         var box = this.el.getBox();
39349         if(this.split){
39350             box.height += this.split.el.getHeight();
39351         }
39352         return box;
39353     },
39354     
39355     updateBox : function(box){
39356         if(this.split && !this.collapsed){
39357             box.height -= this.split.el.getHeight();
39358             this.split.el.setLeft(box.x);
39359             this.split.el.setTop(box.y+box.height);
39360             this.split.el.setWidth(box.width);
39361         }
39362         if(this.collapsed){
39363             this.updateBody(box.width, null);
39364         }
39365         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39366     }
39367 });
39368
39369
39370
39371
39372
39373 Roo.bootstrap.layout.South = function(config){
39374     config.region = 'south';
39375     config.cursor = 's-resize';
39376     Roo.bootstrap.layout.Split.call(this, config);
39377     if(this.split){
39378         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39379         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39380         this.split.el.addClass("roo-layout-split-v");
39381     }
39382     
39383 };
39384
39385 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39386     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39387     
39388     onRender : function(ctr, pos)
39389     {
39390         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39391         var size = this.config.initialSize || this.config.height;
39392         if(this.el && typeof size != "undefined"){
39393             this.el.setHeight(size);
39394         }
39395     
39396     },
39397     
39398     getBox : function(){
39399         if(this.collapsed){
39400             return this.collapsedEl.getBox();
39401         }
39402         var box = this.el.getBox();
39403         if(this.split){
39404             var sh = this.split.el.getHeight();
39405             box.height += sh;
39406             box.y -= sh;
39407         }
39408         return box;
39409     },
39410     
39411     updateBox : function(box){
39412         if(this.split && !this.collapsed){
39413             var sh = this.split.el.getHeight();
39414             box.height -= sh;
39415             box.y += sh;
39416             this.split.el.setLeft(box.x);
39417             this.split.el.setTop(box.y-sh);
39418             this.split.el.setWidth(box.width);
39419         }
39420         if(this.collapsed){
39421             this.updateBody(box.width, null);
39422         }
39423         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39424     }
39425 });
39426
39427 Roo.bootstrap.layout.East = function(config){
39428     config.region = "east";
39429     config.cursor = "e-resize";
39430     Roo.bootstrap.layout.Split.call(this, config);
39431     if(this.split){
39432         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39433         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39434         this.split.el.addClass("roo-layout-split-h");
39435     }
39436     
39437 };
39438 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39439     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39440     
39441     onRender : function(ctr, pos)
39442     {
39443         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39444         var size = this.config.initialSize || this.config.width;
39445         if(this.el && typeof size != "undefined"){
39446             this.el.setWidth(size);
39447         }
39448     
39449     },
39450     
39451     getBox : function(){
39452         if(this.collapsed){
39453             return this.collapsedEl.getBox();
39454         }
39455         var box = this.el.getBox();
39456         if(this.split){
39457             var sw = this.split.el.getWidth();
39458             box.width += sw;
39459             box.x -= sw;
39460         }
39461         return box;
39462     },
39463
39464     updateBox : function(box){
39465         if(this.split && !this.collapsed){
39466             var sw = this.split.el.getWidth();
39467             box.width -= sw;
39468             this.split.el.setLeft(box.x);
39469             this.split.el.setTop(box.y);
39470             this.split.el.setHeight(box.height);
39471             box.x += sw;
39472         }
39473         if(this.collapsed){
39474             this.updateBody(null, box.height);
39475         }
39476         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39477     }
39478 });
39479
39480 Roo.bootstrap.layout.West = function(config){
39481     config.region = "west";
39482     config.cursor = "w-resize";
39483     
39484     Roo.bootstrap.layout.Split.call(this, config);
39485     if(this.split){
39486         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39487         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39488         this.split.el.addClass("roo-layout-split-h");
39489     }
39490     
39491 };
39492 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39493     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39494     
39495     onRender: function(ctr, pos)
39496     {
39497         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39498         var size = this.config.initialSize || this.config.width;
39499         if(typeof size != "undefined"){
39500             this.el.setWidth(size);
39501         }
39502     },
39503     
39504     getBox : function(){
39505         if(this.collapsed){
39506             return this.collapsedEl.getBox();
39507         }
39508         var box = this.el.getBox();
39509         if (box.width == 0) {
39510             box.width = this.config.width; // kludge?
39511         }
39512         if(this.split){
39513             box.width += this.split.el.getWidth();
39514         }
39515         return box;
39516     },
39517     
39518     updateBox : function(box){
39519         if(this.split && !this.collapsed){
39520             var sw = this.split.el.getWidth();
39521             box.width -= sw;
39522             this.split.el.setLeft(box.x+box.width);
39523             this.split.el.setTop(box.y);
39524             this.split.el.setHeight(box.height);
39525         }
39526         if(this.collapsed){
39527             this.updateBody(null, box.height);
39528         }
39529         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39530     }
39531 });Roo.namespace("Roo.bootstrap.panel");/*
39532  * Based on:
39533  * Ext JS Library 1.1.1
39534  * Copyright(c) 2006-2007, Ext JS, LLC.
39535  *
39536  * Originally Released Under LGPL - original licence link has changed is not relivant.
39537  *
39538  * Fork - LGPL
39539  * <script type="text/javascript">
39540  */
39541 /**
39542  * @class Roo.ContentPanel
39543  * @extends Roo.util.Observable
39544  * A basic ContentPanel element.
39545  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39546  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39547  * @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
39548  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39549  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39550  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39551  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39552  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39553  * @cfg {String} title          The title for this panel
39554  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39555  * @cfg {String} url            Calls {@link #setUrl} with this value
39556  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39557  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39558  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39559  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39560  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39561  * @cfg {Boolean} badges render the badges
39562  * @cfg {String} cls  extra classes to use  
39563  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39564
39565  * @constructor
39566  * Create a new ContentPanel.
39567  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39568  * @param {String/Object} config A string to set only the title or a config object
39569  * @param {String} content (optional) Set the HTML content for this panel
39570  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39571  */
39572 Roo.bootstrap.panel.Content = function( config){
39573     
39574     this.tpl = config.tpl || false;
39575     
39576     var el = config.el;
39577     var content = config.content;
39578
39579     if(config.autoCreate){ // xtype is available if this is called from factory
39580         el = Roo.id();
39581     }
39582     this.el = Roo.get(el);
39583     if(!this.el && config && config.autoCreate){
39584         if(typeof config.autoCreate == "object"){
39585             if(!config.autoCreate.id){
39586                 config.autoCreate.id = config.id||el;
39587             }
39588             this.el = Roo.DomHelper.append(document.body,
39589                         config.autoCreate, true);
39590         }else{
39591             var elcfg =  {
39592                 tag: "div",
39593                 cls: (config.cls || '') +
39594                     (config.background ? ' bg-' + config.background : '') +
39595                     " roo-layout-inactive-content",
39596                 id: config.id||el
39597             };
39598             if (config.iframe) {
39599                 elcfg.cn = [
39600                     {
39601                         tag : 'iframe',
39602                         style : 'border: 0px',
39603                         src : 'about:blank'
39604                     }
39605                 ];
39606             }
39607               
39608             if (config.html) {
39609                 elcfg.html = config.html;
39610                 
39611             }
39612                         
39613             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39614             if (config.iframe) {
39615                 this.iframeEl = this.el.select('iframe',true).first();
39616             }
39617             
39618         }
39619     } 
39620     this.closable = false;
39621     this.loaded = false;
39622     this.active = false;
39623    
39624       
39625     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39626         
39627         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39628         
39629         this.wrapEl = this.el; //this.el.wrap();
39630         var ti = [];
39631         if (config.toolbar.items) {
39632             ti = config.toolbar.items ;
39633             delete config.toolbar.items ;
39634         }
39635         
39636         var nitems = [];
39637         this.toolbar.render(this.wrapEl, 'before');
39638         for(var i =0;i < ti.length;i++) {
39639           //  Roo.log(['add child', items[i]]);
39640             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39641         }
39642         this.toolbar.items = nitems;
39643         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39644         delete config.toolbar;
39645         
39646     }
39647     /*
39648     // xtype created footer. - not sure if will work as we normally have to render first..
39649     if (this.footer && !this.footer.el && this.footer.xtype) {
39650         if (!this.wrapEl) {
39651             this.wrapEl = this.el.wrap();
39652         }
39653     
39654         this.footer.container = this.wrapEl.createChild();
39655          
39656         this.footer = Roo.factory(this.footer, Roo);
39657         
39658     }
39659     */
39660     
39661      if(typeof config == "string"){
39662         this.title = config;
39663     }else{
39664         Roo.apply(this, config);
39665     }
39666     
39667     if(this.resizeEl){
39668         this.resizeEl = Roo.get(this.resizeEl, true);
39669     }else{
39670         this.resizeEl = this.el;
39671     }
39672     // handle view.xtype
39673     
39674  
39675     
39676     
39677     this.addEvents({
39678         /**
39679          * @event activate
39680          * Fires when this panel is activated. 
39681          * @param {Roo.ContentPanel} this
39682          */
39683         "activate" : true,
39684         /**
39685          * @event deactivate
39686          * Fires when this panel is activated. 
39687          * @param {Roo.ContentPanel} this
39688          */
39689         "deactivate" : true,
39690
39691         /**
39692          * @event resize
39693          * Fires when this panel is resized if fitToFrame is true.
39694          * @param {Roo.ContentPanel} this
39695          * @param {Number} width The width after any component adjustments
39696          * @param {Number} height The height after any component adjustments
39697          */
39698         "resize" : true,
39699         
39700          /**
39701          * @event render
39702          * Fires when this tab is created
39703          * @param {Roo.ContentPanel} this
39704          */
39705         "render" : true
39706         
39707         
39708         
39709     });
39710     
39711
39712     
39713     
39714     if(this.autoScroll && !this.iframe){
39715         this.resizeEl.setStyle("overflow", "auto");
39716     } else {
39717         // fix randome scrolling
39718         //this.el.on('scroll', function() {
39719         //    Roo.log('fix random scolling');
39720         //    this.scrollTo('top',0); 
39721         //});
39722     }
39723     content = content || this.content;
39724     if(content){
39725         this.setContent(content);
39726     }
39727     if(config && config.url){
39728         this.setUrl(this.url, this.params, this.loadOnce);
39729     }
39730     
39731     
39732     
39733     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39734     
39735     if (this.view && typeof(this.view.xtype) != 'undefined') {
39736         this.view.el = this.el.appendChild(document.createElement("div"));
39737         this.view = Roo.factory(this.view); 
39738         this.view.render  &&  this.view.render(false, '');  
39739     }
39740     
39741     
39742     this.fireEvent('render', this);
39743 };
39744
39745 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39746     
39747     cls : '',
39748     background : '',
39749     
39750     tabTip : '',
39751     
39752     iframe : false,
39753     iframeEl : false,
39754     
39755     setRegion : function(region){
39756         this.region = region;
39757         this.setActiveClass(region && !this.background);
39758     },
39759     
39760     
39761     setActiveClass: function(state)
39762     {
39763         if(state){
39764            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39765            this.el.setStyle('position','relative');
39766         }else{
39767            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39768            this.el.setStyle('position', 'absolute');
39769         } 
39770     },
39771     
39772     /**
39773      * Returns the toolbar for this Panel if one was configured. 
39774      * @return {Roo.Toolbar} 
39775      */
39776     getToolbar : function(){
39777         return this.toolbar;
39778     },
39779     
39780     setActiveState : function(active)
39781     {
39782         this.active = active;
39783         this.setActiveClass(active);
39784         if(!active){
39785             if(this.fireEvent("deactivate", this) === false){
39786                 return false;
39787             }
39788             return true;
39789         }
39790         this.fireEvent("activate", this);
39791         return true;
39792     },
39793     /**
39794      * Updates this panel's element (not for iframe)
39795      * @param {String} content The new content
39796      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39797     */
39798     setContent : function(content, loadScripts){
39799         if (this.iframe) {
39800             return;
39801         }
39802         
39803         this.el.update(content, loadScripts);
39804     },
39805
39806     ignoreResize : function(w, h){
39807         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39808             return true;
39809         }else{
39810             this.lastSize = {width: w, height: h};
39811             return false;
39812         }
39813     },
39814     /**
39815      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39816      * @return {Roo.UpdateManager} The UpdateManager
39817      */
39818     getUpdateManager : function(){
39819         if (this.iframe) {
39820             return false;
39821         }
39822         return this.el.getUpdateManager();
39823     },
39824      /**
39825      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39826      * Does not work with IFRAME contents
39827      * @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:
39828 <pre><code>
39829 panel.load({
39830     url: "your-url.php",
39831     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39832     callback: yourFunction,
39833     scope: yourObject, //(optional scope)
39834     discardUrl: false,
39835     nocache: false,
39836     text: "Loading...",
39837     timeout: 30,
39838     scripts: false
39839 });
39840 </code></pre>
39841      
39842      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39843      * 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.
39844      * @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}
39845      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39846      * @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.
39847      * @return {Roo.ContentPanel} this
39848      */
39849     load : function(){
39850         
39851         if (this.iframe) {
39852             return this;
39853         }
39854         
39855         var um = this.el.getUpdateManager();
39856         um.update.apply(um, arguments);
39857         return this;
39858     },
39859
39860
39861     /**
39862      * 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.
39863      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39864      * @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)
39865      * @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)
39866      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39867      */
39868     setUrl : function(url, params, loadOnce){
39869         if (this.iframe) {
39870             this.iframeEl.dom.src = url;
39871             return false;
39872         }
39873         
39874         if(this.refreshDelegate){
39875             this.removeListener("activate", this.refreshDelegate);
39876         }
39877         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39878         this.on("activate", this.refreshDelegate);
39879         return this.el.getUpdateManager();
39880     },
39881     
39882     _handleRefresh : function(url, params, loadOnce){
39883         if(!loadOnce || !this.loaded){
39884             var updater = this.el.getUpdateManager();
39885             updater.update(url, params, this._setLoaded.createDelegate(this));
39886         }
39887     },
39888     
39889     _setLoaded : function(){
39890         this.loaded = true;
39891     }, 
39892     
39893     /**
39894      * Returns this panel's id
39895      * @return {String} 
39896      */
39897     getId : function(){
39898         return this.el.id;
39899     },
39900     
39901     /** 
39902      * Returns this panel's element - used by regiosn to add.
39903      * @return {Roo.Element} 
39904      */
39905     getEl : function(){
39906         return this.wrapEl || this.el;
39907     },
39908     
39909    
39910     
39911     adjustForComponents : function(width, height)
39912     {
39913         //Roo.log('adjustForComponents ');
39914         if(this.resizeEl != this.el){
39915             width -= this.el.getFrameWidth('lr');
39916             height -= this.el.getFrameWidth('tb');
39917         }
39918         if(this.toolbar){
39919             var te = this.toolbar.getEl();
39920             te.setWidth(width);
39921             height -= te.getHeight();
39922         }
39923         if(this.footer){
39924             var te = this.footer.getEl();
39925             te.setWidth(width);
39926             height -= te.getHeight();
39927         }
39928         
39929         
39930         if(this.adjustments){
39931             width += this.adjustments[0];
39932             height += this.adjustments[1];
39933         }
39934         return {"width": width, "height": height};
39935     },
39936     
39937     setSize : function(width, height){
39938         if(this.fitToFrame && !this.ignoreResize(width, height)){
39939             if(this.fitContainer && this.resizeEl != this.el){
39940                 this.el.setSize(width, height);
39941             }
39942             var size = this.adjustForComponents(width, height);
39943             if (this.iframe) {
39944                 this.iframeEl.setSize(width,height);
39945             }
39946             
39947             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39948             this.fireEvent('resize', this, size.width, size.height);
39949             
39950             
39951         }
39952     },
39953     
39954     /**
39955      * Returns this panel's title
39956      * @return {String} 
39957      */
39958     getTitle : function(){
39959         
39960         if (typeof(this.title) != 'object') {
39961             return this.title;
39962         }
39963         
39964         var t = '';
39965         for (var k in this.title) {
39966             if (!this.title.hasOwnProperty(k)) {
39967                 continue;
39968             }
39969             
39970             if (k.indexOf('-') >= 0) {
39971                 var s = k.split('-');
39972                 for (var i = 0; i<s.length; i++) {
39973                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39974                 }
39975             } else {
39976                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39977             }
39978         }
39979         return t;
39980     },
39981     
39982     /**
39983      * Set this panel's title
39984      * @param {String} title
39985      */
39986     setTitle : function(title){
39987         this.title = title;
39988         if(this.region){
39989             this.region.updatePanelTitle(this, title);
39990         }
39991     },
39992     
39993     /**
39994      * Returns true is this panel was configured to be closable
39995      * @return {Boolean} 
39996      */
39997     isClosable : function(){
39998         return this.closable;
39999     },
40000     
40001     beforeSlide : function(){
40002         this.el.clip();
40003         this.resizeEl.clip();
40004     },
40005     
40006     afterSlide : function(){
40007         this.el.unclip();
40008         this.resizeEl.unclip();
40009     },
40010     
40011     /**
40012      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40013      *   Will fail silently if the {@link #setUrl} method has not been called.
40014      *   This does not activate the panel, just updates its content.
40015      */
40016     refresh : function(){
40017         if(this.refreshDelegate){
40018            this.loaded = false;
40019            this.refreshDelegate();
40020         }
40021     },
40022     
40023     /**
40024      * Destroys this panel
40025      */
40026     destroy : function(){
40027         this.el.removeAllListeners();
40028         var tempEl = document.createElement("span");
40029         tempEl.appendChild(this.el.dom);
40030         tempEl.innerHTML = "";
40031         this.el.remove();
40032         this.el = null;
40033     },
40034     
40035     /**
40036      * form - if the content panel contains a form - this is a reference to it.
40037      * @type {Roo.form.Form}
40038      */
40039     form : false,
40040     /**
40041      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40042      *    This contains a reference to it.
40043      * @type {Roo.View}
40044      */
40045     view : false,
40046     
40047       /**
40048      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40049      * <pre><code>
40050
40051 layout.addxtype({
40052        xtype : 'Form',
40053        items: [ .... ]
40054    }
40055 );
40056
40057 </code></pre>
40058      * @param {Object} cfg Xtype definition of item to add.
40059      */
40060     
40061     
40062     getChildContainer: function () {
40063         return this.getEl();
40064     }
40065     
40066     
40067     /*
40068         var  ret = new Roo.factory(cfg);
40069         return ret;
40070         
40071         
40072         // add form..
40073         if (cfg.xtype.match(/^Form$/)) {
40074             
40075             var el;
40076             //if (this.footer) {
40077             //    el = this.footer.container.insertSibling(false, 'before');
40078             //} else {
40079                 el = this.el.createChild();
40080             //}
40081
40082             this.form = new  Roo.form.Form(cfg);
40083             
40084             
40085             if ( this.form.allItems.length) {
40086                 this.form.render(el.dom);
40087             }
40088             return this.form;
40089         }
40090         // should only have one of theses..
40091         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40092             // views.. should not be just added - used named prop 'view''
40093             
40094             cfg.el = this.el.appendChild(document.createElement("div"));
40095             // factory?
40096             
40097             var ret = new Roo.factory(cfg);
40098              
40099              ret.render && ret.render(false, ''); // render blank..
40100             this.view = ret;
40101             return ret;
40102         }
40103         return false;
40104     }
40105     \*/
40106 });
40107  
40108 /**
40109  * @class Roo.bootstrap.panel.Grid
40110  * @extends Roo.bootstrap.panel.Content
40111  * @constructor
40112  * Create a new GridPanel.
40113  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40114  * @param {Object} config A the config object
40115   
40116  */
40117
40118
40119
40120 Roo.bootstrap.panel.Grid = function(config)
40121 {
40122     
40123       
40124     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40125         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40126
40127     config.el = this.wrapper;
40128     //this.el = this.wrapper;
40129     
40130       if (config.container) {
40131         // ctor'ed from a Border/panel.grid
40132         
40133         
40134         this.wrapper.setStyle("overflow", "hidden");
40135         this.wrapper.addClass('roo-grid-container');
40136
40137     }
40138     
40139     
40140     if(config.toolbar){
40141         var tool_el = this.wrapper.createChild();    
40142         this.toolbar = Roo.factory(config.toolbar);
40143         var ti = [];
40144         if (config.toolbar.items) {
40145             ti = config.toolbar.items ;
40146             delete config.toolbar.items ;
40147         }
40148         
40149         var nitems = [];
40150         this.toolbar.render(tool_el);
40151         for(var i =0;i < ti.length;i++) {
40152           //  Roo.log(['add child', items[i]]);
40153             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40154         }
40155         this.toolbar.items = nitems;
40156         
40157         delete config.toolbar;
40158     }
40159     
40160     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40161     config.grid.scrollBody = true;;
40162     config.grid.monitorWindowResize = false; // turn off autosizing
40163     config.grid.autoHeight = false;
40164     config.grid.autoWidth = false;
40165     
40166     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40167     
40168     if (config.background) {
40169         // render grid on panel activation (if panel background)
40170         this.on('activate', function(gp) {
40171             if (!gp.grid.rendered) {
40172                 gp.grid.render(this.wrapper);
40173                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40174             }
40175         });
40176             
40177     } else {
40178         this.grid.render(this.wrapper);
40179         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40180
40181     }
40182     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40183     // ??? needed ??? config.el = this.wrapper;
40184     
40185     
40186     
40187   
40188     // xtype created footer. - not sure if will work as we normally have to render first..
40189     if (this.footer && !this.footer.el && this.footer.xtype) {
40190         
40191         var ctr = this.grid.getView().getFooterPanel(true);
40192         this.footer.dataSource = this.grid.dataSource;
40193         this.footer = Roo.factory(this.footer, Roo);
40194         this.footer.render(ctr);
40195         
40196     }
40197     
40198     
40199     
40200     
40201      
40202 };
40203
40204 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40205     getId : function(){
40206         return this.grid.id;
40207     },
40208     
40209     /**
40210      * Returns the grid for this panel
40211      * @return {Roo.bootstrap.Table} 
40212      */
40213     getGrid : function(){
40214         return this.grid;    
40215     },
40216     
40217     setSize : function(width, height){
40218         if(!this.ignoreResize(width, height)){
40219             var grid = this.grid;
40220             var size = this.adjustForComponents(width, height);
40221             // tfoot is not a footer?
40222           
40223             
40224             var gridel = grid.getGridEl();
40225             gridel.setSize(size.width, size.height);
40226             
40227             var tbd = grid.getGridEl().select('tbody', true).first();
40228             var thd = grid.getGridEl().select('thead',true).first();
40229             var tbf= grid.getGridEl().select('tfoot', true).first();
40230
40231             if (tbf) {
40232                 size.height -= tbf.getHeight();
40233             }
40234             if (thd) {
40235                 size.height -= thd.getHeight();
40236             }
40237             
40238             tbd.setSize(size.width, size.height );
40239             // this is for the account management tab -seems to work there.
40240             var thd = grid.getGridEl().select('thead',true).first();
40241             //if (tbd) {
40242             //    tbd.setSize(size.width, size.height - thd.getHeight());
40243             //}
40244              
40245             grid.autoSize();
40246         }
40247     },
40248      
40249     
40250     
40251     beforeSlide : function(){
40252         this.grid.getView().scroller.clip();
40253     },
40254     
40255     afterSlide : function(){
40256         this.grid.getView().scroller.unclip();
40257     },
40258     
40259     destroy : function(){
40260         this.grid.destroy();
40261         delete this.grid;
40262         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40263     }
40264 });
40265
40266 /**
40267  * @class Roo.bootstrap.panel.Nest
40268  * @extends Roo.bootstrap.panel.Content
40269  * @constructor
40270  * Create a new Panel, that can contain a layout.Border.
40271  * 
40272  * 
40273  * @param {Roo.BorderLayout} layout The layout for this panel
40274  * @param {String/Object} config A string to set only the title or a config object
40275  */
40276 Roo.bootstrap.panel.Nest = function(config)
40277 {
40278     // construct with only one argument..
40279     /* FIXME - implement nicer consturctors
40280     if (layout.layout) {
40281         config = layout;
40282         layout = config.layout;
40283         delete config.layout;
40284     }
40285     if (layout.xtype && !layout.getEl) {
40286         // then layout needs constructing..
40287         layout = Roo.factory(layout, Roo);
40288     }
40289     */
40290     
40291     config.el =  config.layout.getEl();
40292     
40293     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40294     
40295     config.layout.monitorWindowResize = false; // turn off autosizing
40296     this.layout = config.layout;
40297     this.layout.getEl().addClass("roo-layout-nested-layout");
40298     this.layout.parent = this;
40299     
40300     
40301     
40302     
40303 };
40304
40305 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40306
40307     setSize : function(width, height){
40308         if(!this.ignoreResize(width, height)){
40309             var size = this.adjustForComponents(width, height);
40310             var el = this.layout.getEl();
40311             if (size.height < 1) {
40312                 el.setWidth(size.width);   
40313             } else {
40314                 el.setSize(size.width, size.height);
40315             }
40316             var touch = el.dom.offsetWidth;
40317             this.layout.layout();
40318             // ie requires a double layout on the first pass
40319             if(Roo.isIE && !this.initialized){
40320                 this.initialized = true;
40321                 this.layout.layout();
40322             }
40323         }
40324     },
40325     
40326     // activate all subpanels if not currently active..
40327     
40328     setActiveState : function(active){
40329         this.active = active;
40330         this.setActiveClass(active);
40331         
40332         if(!active){
40333             this.fireEvent("deactivate", this);
40334             return;
40335         }
40336         
40337         this.fireEvent("activate", this);
40338         // not sure if this should happen before or after..
40339         if (!this.layout) {
40340             return; // should not happen..
40341         }
40342         var reg = false;
40343         for (var r in this.layout.regions) {
40344             reg = this.layout.getRegion(r);
40345             if (reg.getActivePanel()) {
40346                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40347                 reg.setActivePanel(reg.getActivePanel());
40348                 continue;
40349             }
40350             if (!reg.panels.length) {
40351                 continue;
40352             }
40353             reg.showPanel(reg.getPanel(0));
40354         }
40355         
40356         
40357         
40358         
40359     },
40360     
40361     /**
40362      * Returns the nested BorderLayout for this panel
40363      * @return {Roo.BorderLayout} 
40364      */
40365     getLayout : function(){
40366         return this.layout;
40367     },
40368     
40369      /**
40370      * Adds a xtype elements to the layout of the nested panel
40371      * <pre><code>
40372
40373 panel.addxtype({
40374        xtype : 'ContentPanel',
40375        region: 'west',
40376        items: [ .... ]
40377    }
40378 );
40379
40380 panel.addxtype({
40381         xtype : 'NestedLayoutPanel',
40382         region: 'west',
40383         layout: {
40384            center: { },
40385            west: { }   
40386         },
40387         items : [ ... list of content panels or nested layout panels.. ]
40388    }
40389 );
40390 </code></pre>
40391      * @param {Object} cfg Xtype definition of item to add.
40392      */
40393     addxtype : function(cfg) {
40394         return this.layout.addxtype(cfg);
40395     
40396     }
40397 });/*
40398  * Based on:
40399  * Ext JS Library 1.1.1
40400  * Copyright(c) 2006-2007, Ext JS, LLC.
40401  *
40402  * Originally Released Under LGPL - original licence link has changed is not relivant.
40403  *
40404  * Fork - LGPL
40405  * <script type="text/javascript">
40406  */
40407 /**
40408  * @class Roo.TabPanel
40409  * @extends Roo.util.Observable
40410  * A lightweight tab container.
40411  * <br><br>
40412  * Usage:
40413  * <pre><code>
40414 // basic tabs 1, built from existing content
40415 var tabs = new Roo.TabPanel("tabs1");
40416 tabs.addTab("script", "View Script");
40417 tabs.addTab("markup", "View Markup");
40418 tabs.activate("script");
40419
40420 // more advanced tabs, built from javascript
40421 var jtabs = new Roo.TabPanel("jtabs");
40422 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40423
40424 // set up the UpdateManager
40425 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40426 var updater = tab2.getUpdateManager();
40427 updater.setDefaultUrl("ajax1.htm");
40428 tab2.on('activate', updater.refresh, updater, true);
40429
40430 // Use setUrl for Ajax loading
40431 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40432 tab3.setUrl("ajax2.htm", null, true);
40433
40434 // Disabled tab
40435 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40436 tab4.disable();
40437
40438 jtabs.activate("jtabs-1");
40439  * </code></pre>
40440  * @constructor
40441  * Create a new TabPanel.
40442  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40443  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40444  */
40445 Roo.bootstrap.panel.Tabs = function(config){
40446     /**
40447     * The container element for this TabPanel.
40448     * @type Roo.Element
40449     */
40450     this.el = Roo.get(config.el);
40451     delete config.el;
40452     if(config){
40453         if(typeof config == "boolean"){
40454             this.tabPosition = config ? "bottom" : "top";
40455         }else{
40456             Roo.apply(this, config);
40457         }
40458     }
40459     
40460     if(this.tabPosition == "bottom"){
40461         // if tabs are at the bottom = create the body first.
40462         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40463         this.el.addClass("roo-tabs-bottom");
40464     }
40465     // next create the tabs holders
40466     
40467     if (this.tabPosition == "west"){
40468         
40469         var reg = this.region; // fake it..
40470         while (reg) {
40471             if (!reg.mgr.parent) {
40472                 break;
40473             }
40474             reg = reg.mgr.parent.region;
40475         }
40476         Roo.log("got nest?");
40477         Roo.log(reg);
40478         if (reg.mgr.getRegion('west')) {
40479             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40480             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40481             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40482             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40483             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40484         
40485             
40486         }
40487         
40488         
40489     } else {
40490      
40491         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40492         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40493         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40494         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40495     }
40496     
40497     
40498     if(Roo.isIE){
40499         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40500     }
40501     
40502     // finally - if tabs are at the top, then create the body last..
40503     if(this.tabPosition != "bottom"){
40504         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40505          * @type Roo.Element
40506          */
40507         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40508         this.el.addClass("roo-tabs-top");
40509     }
40510     this.items = [];
40511
40512     this.bodyEl.setStyle("position", "relative");
40513
40514     this.active = null;
40515     this.activateDelegate = this.activate.createDelegate(this);
40516
40517     this.addEvents({
40518         /**
40519          * @event tabchange
40520          * Fires when the active tab changes
40521          * @param {Roo.TabPanel} this
40522          * @param {Roo.TabPanelItem} activePanel The new active tab
40523          */
40524         "tabchange": true,
40525         /**
40526          * @event beforetabchange
40527          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40528          * @param {Roo.TabPanel} this
40529          * @param {Object} e Set cancel to true on this object to cancel the tab change
40530          * @param {Roo.TabPanelItem} tab The tab being changed to
40531          */
40532         "beforetabchange" : true
40533     });
40534
40535     Roo.EventManager.onWindowResize(this.onResize, this);
40536     this.cpad = this.el.getPadding("lr");
40537     this.hiddenCount = 0;
40538
40539
40540     // toolbar on the tabbar support...
40541     if (this.toolbar) {
40542         alert("no toolbar support yet");
40543         this.toolbar  = false;
40544         /*
40545         var tcfg = this.toolbar;
40546         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40547         this.toolbar = new Roo.Toolbar(tcfg);
40548         if (Roo.isSafari) {
40549             var tbl = tcfg.container.child('table', true);
40550             tbl.setAttribute('width', '100%');
40551         }
40552         */
40553         
40554     }
40555    
40556
40557
40558     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40559 };
40560
40561 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40562     /*
40563      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40564      */
40565     tabPosition : "top",
40566     /*
40567      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40568      */
40569     currentTabWidth : 0,
40570     /*
40571      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40572      */
40573     minTabWidth : 40,
40574     /*
40575      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40576      */
40577     maxTabWidth : 250,
40578     /*
40579      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40580      */
40581     preferredTabWidth : 175,
40582     /*
40583      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40584      */
40585     resizeTabs : false,
40586     /*
40587      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40588      */
40589     monitorResize : true,
40590     /*
40591      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40592      */
40593     toolbar : false,  // set by caller..
40594     
40595     region : false, /// set by caller
40596     
40597     disableTooltips : true, // not used yet...
40598
40599     /**
40600      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40601      * @param {String} id The id of the div to use <b>or create</b>
40602      * @param {String} text The text for the tab
40603      * @param {String} content (optional) Content to put in the TabPanelItem body
40604      * @param {Boolean} closable (optional) True to create a close icon on the tab
40605      * @return {Roo.TabPanelItem} The created TabPanelItem
40606      */
40607     addTab : function(id, text, content, closable, tpl)
40608     {
40609         var item = new Roo.bootstrap.panel.TabItem({
40610             panel: this,
40611             id : id,
40612             text : text,
40613             closable : closable,
40614             tpl : tpl
40615         });
40616         this.addTabItem(item);
40617         if(content){
40618             item.setContent(content);
40619         }
40620         return item;
40621     },
40622
40623     /**
40624      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40625      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40626      * @return {Roo.TabPanelItem}
40627      */
40628     getTab : function(id){
40629         return this.items[id];
40630     },
40631
40632     /**
40633      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40634      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40635      */
40636     hideTab : function(id){
40637         var t = this.items[id];
40638         if(!t.isHidden()){
40639            t.setHidden(true);
40640            this.hiddenCount++;
40641            this.autoSizeTabs();
40642         }
40643     },
40644
40645     /**
40646      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40647      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40648      */
40649     unhideTab : function(id){
40650         var t = this.items[id];
40651         if(t.isHidden()){
40652            t.setHidden(false);
40653            this.hiddenCount--;
40654            this.autoSizeTabs();
40655         }
40656     },
40657
40658     /**
40659      * Adds an existing {@link Roo.TabPanelItem}.
40660      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40661      */
40662     addTabItem : function(item)
40663     {
40664         this.items[item.id] = item;
40665         this.items.push(item);
40666         this.autoSizeTabs();
40667       //  if(this.resizeTabs){
40668     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40669   //         this.autoSizeTabs();
40670 //        }else{
40671 //            item.autoSize();
40672        // }
40673     },
40674
40675     /**
40676      * Removes a {@link Roo.TabPanelItem}.
40677      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40678      */
40679     removeTab : function(id){
40680         var items = this.items;
40681         var tab = items[id];
40682         if(!tab) { return; }
40683         var index = items.indexOf(tab);
40684         if(this.active == tab && items.length > 1){
40685             var newTab = this.getNextAvailable(index);
40686             if(newTab) {
40687                 newTab.activate();
40688             }
40689         }
40690         this.stripEl.dom.removeChild(tab.pnode.dom);
40691         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40692             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40693         }
40694         items.splice(index, 1);
40695         delete this.items[tab.id];
40696         tab.fireEvent("close", tab);
40697         tab.purgeListeners();
40698         this.autoSizeTabs();
40699     },
40700
40701     getNextAvailable : function(start){
40702         var items = this.items;
40703         var index = start;
40704         // look for a next tab that will slide over to
40705         // replace the one being removed
40706         while(index < items.length){
40707             var item = items[++index];
40708             if(item && !item.isHidden()){
40709                 return item;
40710             }
40711         }
40712         // if one isn't found select the previous tab (on the left)
40713         index = start;
40714         while(index >= 0){
40715             var item = items[--index];
40716             if(item && !item.isHidden()){
40717                 return item;
40718             }
40719         }
40720         return null;
40721     },
40722
40723     /**
40724      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40725      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40726      */
40727     disableTab : function(id){
40728         var tab = this.items[id];
40729         if(tab && this.active != tab){
40730             tab.disable();
40731         }
40732     },
40733
40734     /**
40735      * Enables a {@link Roo.TabPanelItem} that is disabled.
40736      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40737      */
40738     enableTab : function(id){
40739         var tab = this.items[id];
40740         tab.enable();
40741     },
40742
40743     /**
40744      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40745      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40746      * @return {Roo.TabPanelItem} The TabPanelItem.
40747      */
40748     activate : function(id)
40749     {
40750         //Roo.log('activite:'  + id);
40751         
40752         var tab = this.items[id];
40753         if(!tab){
40754             return null;
40755         }
40756         if(tab == this.active || tab.disabled){
40757             return tab;
40758         }
40759         var e = {};
40760         this.fireEvent("beforetabchange", this, e, tab);
40761         if(e.cancel !== true && !tab.disabled){
40762             if(this.active){
40763                 this.active.hide();
40764             }
40765             this.active = this.items[id];
40766             this.active.show();
40767             this.fireEvent("tabchange", this, this.active);
40768         }
40769         return tab;
40770     },
40771
40772     /**
40773      * Gets the active {@link Roo.TabPanelItem}.
40774      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40775      */
40776     getActiveTab : function(){
40777         return this.active;
40778     },
40779
40780     /**
40781      * Updates the tab body element to fit the height of the container element
40782      * for overflow scrolling
40783      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40784      */
40785     syncHeight : function(targetHeight){
40786         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40787         var bm = this.bodyEl.getMargins();
40788         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40789         this.bodyEl.setHeight(newHeight);
40790         return newHeight;
40791     },
40792
40793     onResize : function(){
40794         if(this.monitorResize){
40795             this.autoSizeTabs();
40796         }
40797     },
40798
40799     /**
40800      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40801      */
40802     beginUpdate : function(){
40803         this.updating = true;
40804     },
40805
40806     /**
40807      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40808      */
40809     endUpdate : function(){
40810         this.updating = false;
40811         this.autoSizeTabs();
40812     },
40813
40814     /**
40815      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40816      */
40817     autoSizeTabs : function()
40818     {
40819         var count = this.items.length;
40820         var vcount = count - this.hiddenCount;
40821         
40822         if (vcount < 2) {
40823             this.stripEl.hide();
40824         } else {
40825             this.stripEl.show();
40826         }
40827         
40828         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40829             return;
40830         }
40831         
40832         
40833         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40834         var availWidth = Math.floor(w / vcount);
40835         var b = this.stripBody;
40836         if(b.getWidth() > w){
40837             var tabs = this.items;
40838             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40839             if(availWidth < this.minTabWidth){
40840                 /*if(!this.sleft){    // incomplete scrolling code
40841                     this.createScrollButtons();
40842                 }
40843                 this.showScroll();
40844                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40845             }
40846         }else{
40847             if(this.currentTabWidth < this.preferredTabWidth){
40848                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40849             }
40850         }
40851     },
40852
40853     /**
40854      * Returns the number of tabs in this TabPanel.
40855      * @return {Number}
40856      */
40857      getCount : function(){
40858          return this.items.length;
40859      },
40860
40861     /**
40862      * Resizes all the tabs to the passed width
40863      * @param {Number} The new width
40864      */
40865     setTabWidth : function(width){
40866         this.currentTabWidth = width;
40867         for(var i = 0, len = this.items.length; i < len; i++) {
40868                 if(!this.items[i].isHidden()) {
40869                 this.items[i].setWidth(width);
40870             }
40871         }
40872     },
40873
40874     /**
40875      * Destroys this TabPanel
40876      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40877      */
40878     destroy : function(removeEl){
40879         Roo.EventManager.removeResizeListener(this.onResize, this);
40880         for(var i = 0, len = this.items.length; i < len; i++){
40881             this.items[i].purgeListeners();
40882         }
40883         if(removeEl === true){
40884             this.el.update("");
40885             this.el.remove();
40886         }
40887     },
40888     
40889     createStrip : function(container)
40890     {
40891         var strip = document.createElement("nav");
40892         strip.className = Roo.bootstrap.version == 4 ?
40893             "navbar-light bg-light" : 
40894             "navbar navbar-default"; //"x-tabs-wrap";
40895         container.appendChild(strip);
40896         return strip;
40897     },
40898     
40899     createStripList : function(strip)
40900     {
40901         // div wrapper for retard IE
40902         // returns the "tr" element.
40903         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40904         //'<div class="x-tabs-strip-wrap">'+
40905           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40906           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40907         return strip.firstChild; //.firstChild.firstChild.firstChild;
40908     },
40909     createBody : function(container)
40910     {
40911         var body = document.createElement("div");
40912         Roo.id(body, "tab-body");
40913         //Roo.fly(body).addClass("x-tabs-body");
40914         Roo.fly(body).addClass("tab-content");
40915         container.appendChild(body);
40916         return body;
40917     },
40918     createItemBody :function(bodyEl, id){
40919         var body = Roo.getDom(id);
40920         if(!body){
40921             body = document.createElement("div");
40922             body.id = id;
40923         }
40924         //Roo.fly(body).addClass("x-tabs-item-body");
40925         Roo.fly(body).addClass("tab-pane");
40926          bodyEl.insertBefore(body, bodyEl.firstChild);
40927         return body;
40928     },
40929     /** @private */
40930     createStripElements :  function(stripEl, text, closable, tpl)
40931     {
40932         var td = document.createElement("li"); // was td..
40933         td.className = 'nav-item';
40934         
40935         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40936         
40937         
40938         stripEl.appendChild(td);
40939         /*if(closable){
40940             td.className = "x-tabs-closable";
40941             if(!this.closeTpl){
40942                 this.closeTpl = new Roo.Template(
40943                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40944                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40945                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40946                 );
40947             }
40948             var el = this.closeTpl.overwrite(td, {"text": text});
40949             var close = el.getElementsByTagName("div")[0];
40950             var inner = el.getElementsByTagName("em")[0];
40951             return {"el": el, "close": close, "inner": inner};
40952         } else {
40953         */
40954         // not sure what this is..
40955 //            if(!this.tabTpl){
40956                 //this.tabTpl = new Roo.Template(
40957                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40958                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40959                 //);
40960 //                this.tabTpl = new Roo.Template(
40961 //                   '<a href="#">' +
40962 //                   '<span unselectable="on"' +
40963 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40964 //                            ' >{text}</span></a>'
40965 //                );
40966 //                
40967 //            }
40968
40969
40970             var template = tpl || this.tabTpl || false;
40971             
40972             if(!template){
40973                 template =  new Roo.Template(
40974                         Roo.bootstrap.version == 4 ? 
40975                             (
40976                                 '<a class="nav-link" href="#" unselectable="on"' +
40977                                      (this.disableTooltips ? '' : ' title="{text}"') +
40978                                      ' >{text}</a>'
40979                             ) : (
40980                                 '<a class="nav-link" href="#">' +
40981                                 '<span unselectable="on"' +
40982                                          (this.disableTooltips ? '' : ' title="{text}"') +
40983                                     ' >{text}</span></a>'
40984                             )
40985                 );
40986             }
40987             
40988             switch (typeof(template)) {
40989                 case 'object' :
40990                     break;
40991                 case 'string' :
40992                     template = new Roo.Template(template);
40993                     break;
40994                 default :
40995                     break;
40996             }
40997             
40998             var el = template.overwrite(td, {"text": text});
40999             
41000             var inner = el.getElementsByTagName("span")[0];
41001             
41002             return {"el": el, "inner": inner};
41003             
41004     }
41005         
41006     
41007 });
41008
41009 /**
41010  * @class Roo.TabPanelItem
41011  * @extends Roo.util.Observable
41012  * Represents an individual item (tab plus body) in a TabPanel.
41013  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41014  * @param {String} id The id of this TabPanelItem
41015  * @param {String} text The text for the tab of this TabPanelItem
41016  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41017  */
41018 Roo.bootstrap.panel.TabItem = function(config){
41019     /**
41020      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41021      * @type Roo.TabPanel
41022      */
41023     this.tabPanel = config.panel;
41024     /**
41025      * The id for this TabPanelItem
41026      * @type String
41027      */
41028     this.id = config.id;
41029     /** @private */
41030     this.disabled = false;
41031     /** @private */
41032     this.text = config.text;
41033     /** @private */
41034     this.loaded = false;
41035     this.closable = config.closable;
41036
41037     /**
41038      * The body element for this TabPanelItem.
41039      * @type Roo.Element
41040      */
41041     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41042     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41043     this.bodyEl.setStyle("display", "block");
41044     this.bodyEl.setStyle("zoom", "1");
41045     //this.hideAction();
41046
41047     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41048     /** @private */
41049     this.el = Roo.get(els.el);
41050     this.inner = Roo.get(els.inner, true);
41051      this.textEl = Roo.bootstrap.version == 4 ?
41052         this.el : Roo.get(this.el.dom.firstChild, true);
41053
41054     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41055     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41056
41057     
41058 //    this.el.on("mousedown", this.onTabMouseDown, this);
41059     this.el.on("click", this.onTabClick, this);
41060     /** @private */
41061     if(config.closable){
41062         var c = Roo.get(els.close, true);
41063         c.dom.title = this.closeText;
41064         c.addClassOnOver("close-over");
41065         c.on("click", this.closeClick, this);
41066      }
41067
41068     this.addEvents({
41069          /**
41070          * @event activate
41071          * Fires when this tab becomes the active tab.
41072          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41073          * @param {Roo.TabPanelItem} this
41074          */
41075         "activate": true,
41076         /**
41077          * @event beforeclose
41078          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41079          * @param {Roo.TabPanelItem} this
41080          * @param {Object} e Set cancel to true on this object to cancel the close.
41081          */
41082         "beforeclose": true,
41083         /**
41084          * @event close
41085          * Fires when this tab is closed.
41086          * @param {Roo.TabPanelItem} this
41087          */
41088          "close": true,
41089         /**
41090          * @event deactivate
41091          * Fires when this tab is no longer the active tab.
41092          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41093          * @param {Roo.TabPanelItem} this
41094          */
41095          "deactivate" : true
41096     });
41097     this.hidden = false;
41098
41099     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41100 };
41101
41102 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41103            {
41104     purgeListeners : function(){
41105        Roo.util.Observable.prototype.purgeListeners.call(this);
41106        this.el.removeAllListeners();
41107     },
41108     /**
41109      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41110      */
41111     show : function(){
41112         this.status_node.addClass("active");
41113         this.showAction();
41114         if(Roo.isOpera){
41115             this.tabPanel.stripWrap.repaint();
41116         }
41117         this.fireEvent("activate", this.tabPanel, this);
41118     },
41119
41120     /**
41121      * Returns true if this tab is the active tab.
41122      * @return {Boolean}
41123      */
41124     isActive : function(){
41125         return this.tabPanel.getActiveTab() == this;
41126     },
41127
41128     /**
41129      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41130      */
41131     hide : function(){
41132         this.status_node.removeClass("active");
41133         this.hideAction();
41134         this.fireEvent("deactivate", this.tabPanel, this);
41135     },
41136
41137     hideAction : function(){
41138         this.bodyEl.hide();
41139         this.bodyEl.setStyle("position", "absolute");
41140         this.bodyEl.setLeft("-20000px");
41141         this.bodyEl.setTop("-20000px");
41142     },
41143
41144     showAction : function(){
41145         this.bodyEl.setStyle("position", "relative");
41146         this.bodyEl.setTop("");
41147         this.bodyEl.setLeft("");
41148         this.bodyEl.show();
41149     },
41150
41151     /**
41152      * Set the tooltip for the tab.
41153      * @param {String} tooltip The tab's tooltip
41154      */
41155     setTooltip : function(text){
41156         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41157             this.textEl.dom.qtip = text;
41158             this.textEl.dom.removeAttribute('title');
41159         }else{
41160             this.textEl.dom.title = text;
41161         }
41162     },
41163
41164     onTabClick : function(e){
41165         e.preventDefault();
41166         this.tabPanel.activate(this.id);
41167     },
41168
41169     onTabMouseDown : function(e){
41170         e.preventDefault();
41171         this.tabPanel.activate(this.id);
41172     },
41173 /*
41174     getWidth : function(){
41175         return this.inner.getWidth();
41176     },
41177
41178     setWidth : function(width){
41179         var iwidth = width - this.linode.getPadding("lr");
41180         this.inner.setWidth(iwidth);
41181         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41182         this.linode.setWidth(width);
41183     },
41184 */
41185     /**
41186      * Show or hide the tab
41187      * @param {Boolean} hidden True to hide or false to show.
41188      */
41189     setHidden : function(hidden){
41190         this.hidden = hidden;
41191         this.linode.setStyle("display", hidden ? "none" : "");
41192     },
41193
41194     /**
41195      * Returns true if this tab is "hidden"
41196      * @return {Boolean}
41197      */
41198     isHidden : function(){
41199         return this.hidden;
41200     },
41201
41202     /**
41203      * Returns the text for this tab
41204      * @return {String}
41205      */
41206     getText : function(){
41207         return this.text;
41208     },
41209     /*
41210     autoSize : function(){
41211         //this.el.beginMeasure();
41212         this.textEl.setWidth(1);
41213         /*
41214          *  #2804 [new] Tabs in Roojs
41215          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41216          */
41217         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41218         //this.el.endMeasure();
41219     //},
41220
41221     /**
41222      * Sets the text for the tab (Note: this also sets the tooltip text)
41223      * @param {String} text The tab's text and tooltip
41224      */
41225     setText : function(text){
41226         this.text = text;
41227         this.textEl.update(text);
41228         this.setTooltip(text);
41229         //if(!this.tabPanel.resizeTabs){
41230         //    this.autoSize();
41231         //}
41232     },
41233     /**
41234      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41235      */
41236     activate : function(){
41237         this.tabPanel.activate(this.id);
41238     },
41239
41240     /**
41241      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41242      */
41243     disable : function(){
41244         if(this.tabPanel.active != this){
41245             this.disabled = true;
41246             this.status_node.addClass("disabled");
41247         }
41248     },
41249
41250     /**
41251      * Enables this TabPanelItem if it was previously disabled.
41252      */
41253     enable : function(){
41254         this.disabled = false;
41255         this.status_node.removeClass("disabled");
41256     },
41257
41258     /**
41259      * Sets the content for this TabPanelItem.
41260      * @param {String} content The content
41261      * @param {Boolean} loadScripts true to look for and load scripts
41262      */
41263     setContent : function(content, loadScripts){
41264         this.bodyEl.update(content, loadScripts);
41265     },
41266
41267     /**
41268      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41269      * @return {Roo.UpdateManager} The UpdateManager
41270      */
41271     getUpdateManager : function(){
41272         return this.bodyEl.getUpdateManager();
41273     },
41274
41275     /**
41276      * Set a URL to be used to load the content for this TabPanelItem.
41277      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41278      * @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)
41279      * @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)
41280      * @return {Roo.UpdateManager} The UpdateManager
41281      */
41282     setUrl : function(url, params, loadOnce){
41283         if(this.refreshDelegate){
41284             this.un('activate', this.refreshDelegate);
41285         }
41286         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41287         this.on("activate", this.refreshDelegate);
41288         return this.bodyEl.getUpdateManager();
41289     },
41290
41291     /** @private */
41292     _handleRefresh : function(url, params, loadOnce){
41293         if(!loadOnce || !this.loaded){
41294             var updater = this.bodyEl.getUpdateManager();
41295             updater.update(url, params, this._setLoaded.createDelegate(this));
41296         }
41297     },
41298
41299     /**
41300      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41301      *   Will fail silently if the setUrl method has not been called.
41302      *   This does not activate the panel, just updates its content.
41303      */
41304     refresh : function(){
41305         if(this.refreshDelegate){
41306            this.loaded = false;
41307            this.refreshDelegate();
41308         }
41309     },
41310
41311     /** @private */
41312     _setLoaded : function(){
41313         this.loaded = true;
41314     },
41315
41316     /** @private */
41317     closeClick : function(e){
41318         var o = {};
41319         e.stopEvent();
41320         this.fireEvent("beforeclose", this, o);
41321         if(o.cancel !== true){
41322             this.tabPanel.removeTab(this.id);
41323         }
41324     },
41325     /**
41326      * The text displayed in the tooltip for the close icon.
41327      * @type String
41328      */
41329     closeText : "Close this tab"
41330 });
41331 /**
41332 *    This script refer to:
41333 *    Title: International Telephone Input
41334 *    Author: Jack O'Connor
41335 *    Code version:  v12.1.12
41336 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41337 **/
41338
41339 Roo.bootstrap.PhoneInputData = function() {
41340     var d = [
41341       [
41342         "Afghanistan (‫افغانستان‬‎)",
41343         "af",
41344         "93"
41345       ],
41346       [
41347         "Albania (Shqipëri)",
41348         "al",
41349         "355"
41350       ],
41351       [
41352         "Algeria (‫الجزائر‬‎)",
41353         "dz",
41354         "213"
41355       ],
41356       [
41357         "American Samoa",
41358         "as",
41359         "1684"
41360       ],
41361       [
41362         "Andorra",
41363         "ad",
41364         "376"
41365       ],
41366       [
41367         "Angola",
41368         "ao",
41369         "244"
41370       ],
41371       [
41372         "Anguilla",
41373         "ai",
41374         "1264"
41375       ],
41376       [
41377         "Antigua and Barbuda",
41378         "ag",
41379         "1268"
41380       ],
41381       [
41382         "Argentina",
41383         "ar",
41384         "54"
41385       ],
41386       [
41387         "Armenia (Հայաստան)",
41388         "am",
41389         "374"
41390       ],
41391       [
41392         "Aruba",
41393         "aw",
41394         "297"
41395       ],
41396       [
41397         "Australia",
41398         "au",
41399         "61",
41400         0
41401       ],
41402       [
41403         "Austria (Österreich)",
41404         "at",
41405         "43"
41406       ],
41407       [
41408         "Azerbaijan (Azərbaycan)",
41409         "az",
41410         "994"
41411       ],
41412       [
41413         "Bahamas",
41414         "bs",
41415         "1242"
41416       ],
41417       [
41418         "Bahrain (‫البحرين‬‎)",
41419         "bh",
41420         "973"
41421       ],
41422       [
41423         "Bangladesh (বাংলাদেশ)",
41424         "bd",
41425         "880"
41426       ],
41427       [
41428         "Barbados",
41429         "bb",
41430         "1246"
41431       ],
41432       [
41433         "Belarus (Беларусь)",
41434         "by",
41435         "375"
41436       ],
41437       [
41438         "Belgium (België)",
41439         "be",
41440         "32"
41441       ],
41442       [
41443         "Belize",
41444         "bz",
41445         "501"
41446       ],
41447       [
41448         "Benin (Bénin)",
41449         "bj",
41450         "229"
41451       ],
41452       [
41453         "Bermuda",
41454         "bm",
41455         "1441"
41456       ],
41457       [
41458         "Bhutan (འབྲུག)",
41459         "bt",
41460         "975"
41461       ],
41462       [
41463         "Bolivia",
41464         "bo",
41465         "591"
41466       ],
41467       [
41468         "Bosnia and Herzegovina (Босна и Херцеговина)",
41469         "ba",
41470         "387"
41471       ],
41472       [
41473         "Botswana",
41474         "bw",
41475         "267"
41476       ],
41477       [
41478         "Brazil (Brasil)",
41479         "br",
41480         "55"
41481       ],
41482       [
41483         "British Indian Ocean Territory",
41484         "io",
41485         "246"
41486       ],
41487       [
41488         "British Virgin Islands",
41489         "vg",
41490         "1284"
41491       ],
41492       [
41493         "Brunei",
41494         "bn",
41495         "673"
41496       ],
41497       [
41498         "Bulgaria (България)",
41499         "bg",
41500         "359"
41501       ],
41502       [
41503         "Burkina Faso",
41504         "bf",
41505         "226"
41506       ],
41507       [
41508         "Burundi (Uburundi)",
41509         "bi",
41510         "257"
41511       ],
41512       [
41513         "Cambodia (កម្ពុជា)",
41514         "kh",
41515         "855"
41516       ],
41517       [
41518         "Cameroon (Cameroun)",
41519         "cm",
41520         "237"
41521       ],
41522       [
41523         "Canada",
41524         "ca",
41525         "1",
41526         1,
41527         ["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"]
41528       ],
41529       [
41530         "Cape Verde (Kabu Verdi)",
41531         "cv",
41532         "238"
41533       ],
41534       [
41535         "Caribbean Netherlands",
41536         "bq",
41537         "599",
41538         1
41539       ],
41540       [
41541         "Cayman Islands",
41542         "ky",
41543         "1345"
41544       ],
41545       [
41546         "Central African Republic (République centrafricaine)",
41547         "cf",
41548         "236"
41549       ],
41550       [
41551         "Chad (Tchad)",
41552         "td",
41553         "235"
41554       ],
41555       [
41556         "Chile",
41557         "cl",
41558         "56"
41559       ],
41560       [
41561         "China (中国)",
41562         "cn",
41563         "86"
41564       ],
41565       [
41566         "Christmas Island",
41567         "cx",
41568         "61",
41569         2
41570       ],
41571       [
41572         "Cocos (Keeling) Islands",
41573         "cc",
41574         "61",
41575         1
41576       ],
41577       [
41578         "Colombia",
41579         "co",
41580         "57"
41581       ],
41582       [
41583         "Comoros (‫جزر القمر‬‎)",
41584         "km",
41585         "269"
41586       ],
41587       [
41588         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41589         "cd",
41590         "243"
41591       ],
41592       [
41593         "Congo (Republic) (Congo-Brazzaville)",
41594         "cg",
41595         "242"
41596       ],
41597       [
41598         "Cook Islands",
41599         "ck",
41600         "682"
41601       ],
41602       [
41603         "Costa Rica",
41604         "cr",
41605         "506"
41606       ],
41607       [
41608         "Côte d’Ivoire",
41609         "ci",
41610         "225"
41611       ],
41612       [
41613         "Croatia (Hrvatska)",
41614         "hr",
41615         "385"
41616       ],
41617       [
41618         "Cuba",
41619         "cu",
41620         "53"
41621       ],
41622       [
41623         "Curaçao",
41624         "cw",
41625         "599",
41626         0
41627       ],
41628       [
41629         "Cyprus (Κύπρος)",
41630         "cy",
41631         "357"
41632       ],
41633       [
41634         "Czech Republic (Česká republika)",
41635         "cz",
41636         "420"
41637       ],
41638       [
41639         "Denmark (Danmark)",
41640         "dk",
41641         "45"
41642       ],
41643       [
41644         "Djibouti",
41645         "dj",
41646         "253"
41647       ],
41648       [
41649         "Dominica",
41650         "dm",
41651         "1767"
41652       ],
41653       [
41654         "Dominican Republic (República Dominicana)",
41655         "do",
41656         "1",
41657         2,
41658         ["809", "829", "849"]
41659       ],
41660       [
41661         "Ecuador",
41662         "ec",
41663         "593"
41664       ],
41665       [
41666         "Egypt (‫مصر‬‎)",
41667         "eg",
41668         "20"
41669       ],
41670       [
41671         "El Salvador",
41672         "sv",
41673         "503"
41674       ],
41675       [
41676         "Equatorial Guinea (Guinea Ecuatorial)",
41677         "gq",
41678         "240"
41679       ],
41680       [
41681         "Eritrea",
41682         "er",
41683         "291"
41684       ],
41685       [
41686         "Estonia (Eesti)",
41687         "ee",
41688         "372"
41689       ],
41690       [
41691         "Ethiopia",
41692         "et",
41693         "251"
41694       ],
41695       [
41696         "Falkland Islands (Islas Malvinas)",
41697         "fk",
41698         "500"
41699       ],
41700       [
41701         "Faroe Islands (Føroyar)",
41702         "fo",
41703         "298"
41704       ],
41705       [
41706         "Fiji",
41707         "fj",
41708         "679"
41709       ],
41710       [
41711         "Finland (Suomi)",
41712         "fi",
41713         "358",
41714         0
41715       ],
41716       [
41717         "France",
41718         "fr",
41719         "33"
41720       ],
41721       [
41722         "French Guiana (Guyane française)",
41723         "gf",
41724         "594"
41725       ],
41726       [
41727         "French Polynesia (Polynésie française)",
41728         "pf",
41729         "689"
41730       ],
41731       [
41732         "Gabon",
41733         "ga",
41734         "241"
41735       ],
41736       [
41737         "Gambia",
41738         "gm",
41739         "220"
41740       ],
41741       [
41742         "Georgia (საქართველო)",
41743         "ge",
41744         "995"
41745       ],
41746       [
41747         "Germany (Deutschland)",
41748         "de",
41749         "49"
41750       ],
41751       [
41752         "Ghana (Gaana)",
41753         "gh",
41754         "233"
41755       ],
41756       [
41757         "Gibraltar",
41758         "gi",
41759         "350"
41760       ],
41761       [
41762         "Greece (Ελλάδα)",
41763         "gr",
41764         "30"
41765       ],
41766       [
41767         "Greenland (Kalaallit Nunaat)",
41768         "gl",
41769         "299"
41770       ],
41771       [
41772         "Grenada",
41773         "gd",
41774         "1473"
41775       ],
41776       [
41777         "Guadeloupe",
41778         "gp",
41779         "590",
41780         0
41781       ],
41782       [
41783         "Guam",
41784         "gu",
41785         "1671"
41786       ],
41787       [
41788         "Guatemala",
41789         "gt",
41790         "502"
41791       ],
41792       [
41793         "Guernsey",
41794         "gg",
41795         "44",
41796         1
41797       ],
41798       [
41799         "Guinea (Guinée)",
41800         "gn",
41801         "224"
41802       ],
41803       [
41804         "Guinea-Bissau (Guiné Bissau)",
41805         "gw",
41806         "245"
41807       ],
41808       [
41809         "Guyana",
41810         "gy",
41811         "592"
41812       ],
41813       [
41814         "Haiti",
41815         "ht",
41816         "509"
41817       ],
41818       [
41819         "Honduras",
41820         "hn",
41821         "504"
41822       ],
41823       [
41824         "Hong Kong (香港)",
41825         "hk",
41826         "852"
41827       ],
41828       [
41829         "Hungary (Magyarország)",
41830         "hu",
41831         "36"
41832       ],
41833       [
41834         "Iceland (Ísland)",
41835         "is",
41836         "354"
41837       ],
41838       [
41839         "India (भारत)",
41840         "in",
41841         "91"
41842       ],
41843       [
41844         "Indonesia",
41845         "id",
41846         "62"
41847       ],
41848       [
41849         "Iran (‫ایران‬‎)",
41850         "ir",
41851         "98"
41852       ],
41853       [
41854         "Iraq (‫العراق‬‎)",
41855         "iq",
41856         "964"
41857       ],
41858       [
41859         "Ireland",
41860         "ie",
41861         "353"
41862       ],
41863       [
41864         "Isle of Man",
41865         "im",
41866         "44",
41867         2
41868       ],
41869       [
41870         "Israel (‫ישראל‬‎)",
41871         "il",
41872         "972"
41873       ],
41874       [
41875         "Italy (Italia)",
41876         "it",
41877         "39",
41878         0
41879       ],
41880       [
41881         "Jamaica",
41882         "jm",
41883         "1876"
41884       ],
41885       [
41886         "Japan (日本)",
41887         "jp",
41888         "81"
41889       ],
41890       [
41891         "Jersey",
41892         "je",
41893         "44",
41894         3
41895       ],
41896       [
41897         "Jordan (‫الأردن‬‎)",
41898         "jo",
41899         "962"
41900       ],
41901       [
41902         "Kazakhstan (Казахстан)",
41903         "kz",
41904         "7",
41905         1
41906       ],
41907       [
41908         "Kenya",
41909         "ke",
41910         "254"
41911       ],
41912       [
41913         "Kiribati",
41914         "ki",
41915         "686"
41916       ],
41917       [
41918         "Kosovo",
41919         "xk",
41920         "383"
41921       ],
41922       [
41923         "Kuwait (‫الكويت‬‎)",
41924         "kw",
41925         "965"
41926       ],
41927       [
41928         "Kyrgyzstan (Кыргызстан)",
41929         "kg",
41930         "996"
41931       ],
41932       [
41933         "Laos (ລາວ)",
41934         "la",
41935         "856"
41936       ],
41937       [
41938         "Latvia (Latvija)",
41939         "lv",
41940         "371"
41941       ],
41942       [
41943         "Lebanon (‫لبنان‬‎)",
41944         "lb",
41945         "961"
41946       ],
41947       [
41948         "Lesotho",
41949         "ls",
41950         "266"
41951       ],
41952       [
41953         "Liberia",
41954         "lr",
41955         "231"
41956       ],
41957       [
41958         "Libya (‫ليبيا‬‎)",
41959         "ly",
41960         "218"
41961       ],
41962       [
41963         "Liechtenstein",
41964         "li",
41965         "423"
41966       ],
41967       [
41968         "Lithuania (Lietuva)",
41969         "lt",
41970         "370"
41971       ],
41972       [
41973         "Luxembourg",
41974         "lu",
41975         "352"
41976       ],
41977       [
41978         "Macau (澳門)",
41979         "mo",
41980         "853"
41981       ],
41982       [
41983         "Macedonia (FYROM) (Македонија)",
41984         "mk",
41985         "389"
41986       ],
41987       [
41988         "Madagascar (Madagasikara)",
41989         "mg",
41990         "261"
41991       ],
41992       [
41993         "Malawi",
41994         "mw",
41995         "265"
41996       ],
41997       [
41998         "Malaysia",
41999         "my",
42000         "60"
42001       ],
42002       [
42003         "Maldives",
42004         "mv",
42005         "960"
42006       ],
42007       [
42008         "Mali",
42009         "ml",
42010         "223"
42011       ],
42012       [
42013         "Malta",
42014         "mt",
42015         "356"
42016       ],
42017       [
42018         "Marshall Islands",
42019         "mh",
42020         "692"
42021       ],
42022       [
42023         "Martinique",
42024         "mq",
42025         "596"
42026       ],
42027       [
42028         "Mauritania (‫موريتانيا‬‎)",
42029         "mr",
42030         "222"
42031       ],
42032       [
42033         "Mauritius (Moris)",
42034         "mu",
42035         "230"
42036       ],
42037       [
42038         "Mayotte",
42039         "yt",
42040         "262",
42041         1
42042       ],
42043       [
42044         "Mexico (México)",
42045         "mx",
42046         "52"
42047       ],
42048       [
42049         "Micronesia",
42050         "fm",
42051         "691"
42052       ],
42053       [
42054         "Moldova (Republica Moldova)",
42055         "md",
42056         "373"
42057       ],
42058       [
42059         "Monaco",
42060         "mc",
42061         "377"
42062       ],
42063       [
42064         "Mongolia (Монгол)",
42065         "mn",
42066         "976"
42067       ],
42068       [
42069         "Montenegro (Crna Gora)",
42070         "me",
42071         "382"
42072       ],
42073       [
42074         "Montserrat",
42075         "ms",
42076         "1664"
42077       ],
42078       [
42079         "Morocco (‫المغرب‬‎)",
42080         "ma",
42081         "212",
42082         0
42083       ],
42084       [
42085         "Mozambique (Moçambique)",
42086         "mz",
42087         "258"
42088       ],
42089       [
42090         "Myanmar (Burma) (မြန်မာ)",
42091         "mm",
42092         "95"
42093       ],
42094       [
42095         "Namibia (Namibië)",
42096         "na",
42097         "264"
42098       ],
42099       [
42100         "Nauru",
42101         "nr",
42102         "674"
42103       ],
42104       [
42105         "Nepal (नेपाल)",
42106         "np",
42107         "977"
42108       ],
42109       [
42110         "Netherlands (Nederland)",
42111         "nl",
42112         "31"
42113       ],
42114       [
42115         "New Caledonia (Nouvelle-Calédonie)",
42116         "nc",
42117         "687"
42118       ],
42119       [
42120         "New Zealand",
42121         "nz",
42122         "64"
42123       ],
42124       [
42125         "Nicaragua",
42126         "ni",
42127         "505"
42128       ],
42129       [
42130         "Niger (Nijar)",
42131         "ne",
42132         "227"
42133       ],
42134       [
42135         "Nigeria",
42136         "ng",
42137         "234"
42138       ],
42139       [
42140         "Niue",
42141         "nu",
42142         "683"
42143       ],
42144       [
42145         "Norfolk Island",
42146         "nf",
42147         "672"
42148       ],
42149       [
42150         "North Korea (조선 민주주의 인민 공화국)",
42151         "kp",
42152         "850"
42153       ],
42154       [
42155         "Northern Mariana Islands",
42156         "mp",
42157         "1670"
42158       ],
42159       [
42160         "Norway (Norge)",
42161         "no",
42162         "47",
42163         0
42164       ],
42165       [
42166         "Oman (‫عُمان‬‎)",
42167         "om",
42168         "968"
42169       ],
42170       [
42171         "Pakistan (‫پاکستان‬‎)",
42172         "pk",
42173         "92"
42174       ],
42175       [
42176         "Palau",
42177         "pw",
42178         "680"
42179       ],
42180       [
42181         "Palestine (‫فلسطين‬‎)",
42182         "ps",
42183         "970"
42184       ],
42185       [
42186         "Panama (Panamá)",
42187         "pa",
42188         "507"
42189       ],
42190       [
42191         "Papua New Guinea",
42192         "pg",
42193         "675"
42194       ],
42195       [
42196         "Paraguay",
42197         "py",
42198         "595"
42199       ],
42200       [
42201         "Peru (Perú)",
42202         "pe",
42203         "51"
42204       ],
42205       [
42206         "Philippines",
42207         "ph",
42208         "63"
42209       ],
42210       [
42211         "Poland (Polska)",
42212         "pl",
42213         "48"
42214       ],
42215       [
42216         "Portugal",
42217         "pt",
42218         "351"
42219       ],
42220       [
42221         "Puerto Rico",
42222         "pr",
42223         "1",
42224         3,
42225         ["787", "939"]
42226       ],
42227       [
42228         "Qatar (‫قطر‬‎)",
42229         "qa",
42230         "974"
42231       ],
42232       [
42233         "Réunion (La Réunion)",
42234         "re",
42235         "262",
42236         0
42237       ],
42238       [
42239         "Romania (România)",
42240         "ro",
42241         "40"
42242       ],
42243       [
42244         "Russia (Россия)",
42245         "ru",
42246         "7",
42247         0
42248       ],
42249       [
42250         "Rwanda",
42251         "rw",
42252         "250"
42253       ],
42254       [
42255         "Saint Barthélemy",
42256         "bl",
42257         "590",
42258         1
42259       ],
42260       [
42261         "Saint Helena",
42262         "sh",
42263         "290"
42264       ],
42265       [
42266         "Saint Kitts and Nevis",
42267         "kn",
42268         "1869"
42269       ],
42270       [
42271         "Saint Lucia",
42272         "lc",
42273         "1758"
42274       ],
42275       [
42276         "Saint Martin (Saint-Martin (partie française))",
42277         "mf",
42278         "590",
42279         2
42280       ],
42281       [
42282         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42283         "pm",
42284         "508"
42285       ],
42286       [
42287         "Saint Vincent and the Grenadines",
42288         "vc",
42289         "1784"
42290       ],
42291       [
42292         "Samoa",
42293         "ws",
42294         "685"
42295       ],
42296       [
42297         "San Marino",
42298         "sm",
42299         "378"
42300       ],
42301       [
42302         "São Tomé and Príncipe (São Tomé e Príncipe)",
42303         "st",
42304         "239"
42305       ],
42306       [
42307         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42308         "sa",
42309         "966"
42310       ],
42311       [
42312         "Senegal (Sénégal)",
42313         "sn",
42314         "221"
42315       ],
42316       [
42317         "Serbia (Србија)",
42318         "rs",
42319         "381"
42320       ],
42321       [
42322         "Seychelles",
42323         "sc",
42324         "248"
42325       ],
42326       [
42327         "Sierra Leone",
42328         "sl",
42329         "232"
42330       ],
42331       [
42332         "Singapore",
42333         "sg",
42334         "65"
42335       ],
42336       [
42337         "Sint Maarten",
42338         "sx",
42339         "1721"
42340       ],
42341       [
42342         "Slovakia (Slovensko)",
42343         "sk",
42344         "421"
42345       ],
42346       [
42347         "Slovenia (Slovenija)",
42348         "si",
42349         "386"
42350       ],
42351       [
42352         "Solomon Islands",
42353         "sb",
42354         "677"
42355       ],
42356       [
42357         "Somalia (Soomaaliya)",
42358         "so",
42359         "252"
42360       ],
42361       [
42362         "South Africa",
42363         "za",
42364         "27"
42365       ],
42366       [
42367         "South Korea (대한민국)",
42368         "kr",
42369         "82"
42370       ],
42371       [
42372         "South Sudan (‫جنوب السودان‬‎)",
42373         "ss",
42374         "211"
42375       ],
42376       [
42377         "Spain (España)",
42378         "es",
42379         "34"
42380       ],
42381       [
42382         "Sri Lanka (ශ්‍රී ලංකාව)",
42383         "lk",
42384         "94"
42385       ],
42386       [
42387         "Sudan (‫السودان‬‎)",
42388         "sd",
42389         "249"
42390       ],
42391       [
42392         "Suriname",
42393         "sr",
42394         "597"
42395       ],
42396       [
42397         "Svalbard and Jan Mayen",
42398         "sj",
42399         "47",
42400         1
42401       ],
42402       [
42403         "Swaziland",
42404         "sz",
42405         "268"
42406       ],
42407       [
42408         "Sweden (Sverige)",
42409         "se",
42410         "46"
42411       ],
42412       [
42413         "Switzerland (Schweiz)",
42414         "ch",
42415         "41"
42416       ],
42417       [
42418         "Syria (‫سوريا‬‎)",
42419         "sy",
42420         "963"
42421       ],
42422       [
42423         "Taiwan (台灣)",
42424         "tw",
42425         "886"
42426       ],
42427       [
42428         "Tajikistan",
42429         "tj",
42430         "992"
42431       ],
42432       [
42433         "Tanzania",
42434         "tz",
42435         "255"
42436       ],
42437       [
42438         "Thailand (ไทย)",
42439         "th",
42440         "66"
42441       ],
42442       [
42443         "Timor-Leste",
42444         "tl",
42445         "670"
42446       ],
42447       [
42448         "Togo",
42449         "tg",
42450         "228"
42451       ],
42452       [
42453         "Tokelau",
42454         "tk",
42455         "690"
42456       ],
42457       [
42458         "Tonga",
42459         "to",
42460         "676"
42461       ],
42462       [
42463         "Trinidad and Tobago",
42464         "tt",
42465         "1868"
42466       ],
42467       [
42468         "Tunisia (‫تونس‬‎)",
42469         "tn",
42470         "216"
42471       ],
42472       [
42473         "Turkey (Türkiye)",
42474         "tr",
42475         "90"
42476       ],
42477       [
42478         "Turkmenistan",
42479         "tm",
42480         "993"
42481       ],
42482       [
42483         "Turks and Caicos Islands",
42484         "tc",
42485         "1649"
42486       ],
42487       [
42488         "Tuvalu",
42489         "tv",
42490         "688"
42491       ],
42492       [
42493         "U.S. Virgin Islands",
42494         "vi",
42495         "1340"
42496       ],
42497       [
42498         "Uganda",
42499         "ug",
42500         "256"
42501       ],
42502       [
42503         "Ukraine (Україна)",
42504         "ua",
42505         "380"
42506       ],
42507       [
42508         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42509         "ae",
42510         "971"
42511       ],
42512       [
42513         "United Kingdom",
42514         "gb",
42515         "44",
42516         0
42517       ],
42518       [
42519         "United States",
42520         "us",
42521         "1",
42522         0
42523       ],
42524       [
42525         "Uruguay",
42526         "uy",
42527         "598"
42528       ],
42529       [
42530         "Uzbekistan (Oʻzbekiston)",
42531         "uz",
42532         "998"
42533       ],
42534       [
42535         "Vanuatu",
42536         "vu",
42537         "678"
42538       ],
42539       [
42540         "Vatican City (Città del Vaticano)",
42541         "va",
42542         "39",
42543         1
42544       ],
42545       [
42546         "Venezuela",
42547         "ve",
42548         "58"
42549       ],
42550       [
42551         "Vietnam (Việt Nam)",
42552         "vn",
42553         "84"
42554       ],
42555       [
42556         "Wallis and Futuna (Wallis-et-Futuna)",
42557         "wf",
42558         "681"
42559       ],
42560       [
42561         "Western Sahara (‫الصحراء الغربية‬‎)",
42562         "eh",
42563         "212",
42564         1
42565       ],
42566       [
42567         "Yemen (‫اليمن‬‎)",
42568         "ye",
42569         "967"
42570       ],
42571       [
42572         "Zambia",
42573         "zm",
42574         "260"
42575       ],
42576       [
42577         "Zimbabwe",
42578         "zw",
42579         "263"
42580       ],
42581       [
42582         "Åland Islands",
42583         "ax",
42584         "358",
42585         1
42586       ]
42587   ];
42588   
42589   return d;
42590 }/**
42591 *    This script refer to:
42592 *    Title: International Telephone Input
42593 *    Author: Jack O'Connor
42594 *    Code version:  v12.1.12
42595 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42596 **/
42597
42598 /**
42599  * @class Roo.bootstrap.PhoneInput
42600  * @extends Roo.bootstrap.TriggerField
42601  * An input with International dial-code selection
42602  
42603  * @cfg {String} defaultDialCode default '+852'
42604  * @cfg {Array} preferedCountries default []
42605   
42606  * @constructor
42607  * Create a new PhoneInput.
42608  * @param {Object} config Configuration options
42609  */
42610
42611 Roo.bootstrap.PhoneInput = function(config) {
42612     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42613 };
42614
42615 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42616         
42617         listWidth: undefined,
42618         
42619         selectedClass: 'active',
42620         
42621         invalidClass : "has-warning",
42622         
42623         validClass: 'has-success',
42624         
42625         allowed: '0123456789',
42626         
42627         max_length: 15,
42628         
42629         /**
42630          * @cfg {String} defaultDialCode The default dial code when initializing the input
42631          */
42632         defaultDialCode: '+852',
42633         
42634         /**
42635          * @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
42636          */
42637         preferedCountries: false,
42638         
42639         getAutoCreate : function()
42640         {
42641             var data = Roo.bootstrap.PhoneInputData();
42642             var align = this.labelAlign || this.parentLabelAlign();
42643             var id = Roo.id();
42644             
42645             this.allCountries = [];
42646             this.dialCodeMapping = [];
42647             
42648             for (var i = 0; i < data.length; i++) {
42649               var c = data[i];
42650               this.allCountries[i] = {
42651                 name: c[0],
42652                 iso2: c[1],
42653                 dialCode: c[2],
42654                 priority: c[3] || 0,
42655                 areaCodes: c[4] || null
42656               };
42657               this.dialCodeMapping[c[2]] = {
42658                   name: c[0],
42659                   iso2: c[1],
42660                   priority: c[3] || 0,
42661                   areaCodes: c[4] || null
42662               };
42663             }
42664             
42665             var cfg = {
42666                 cls: 'form-group',
42667                 cn: []
42668             };
42669             
42670             var input =  {
42671                 tag: 'input',
42672                 id : id,
42673                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42674                 maxlength: this.max_length,
42675                 cls : 'form-control tel-input',
42676                 autocomplete: 'new-password'
42677             };
42678             
42679             var hiddenInput = {
42680                 tag: 'input',
42681                 type: 'hidden',
42682                 cls: 'hidden-tel-input'
42683             };
42684             
42685             if (this.name) {
42686                 hiddenInput.name = this.name;
42687             }
42688             
42689             if (this.disabled) {
42690                 input.disabled = true;
42691             }
42692             
42693             var flag_container = {
42694                 tag: 'div',
42695                 cls: 'flag-box',
42696                 cn: [
42697                     {
42698                         tag: 'div',
42699                         cls: 'flag'
42700                     },
42701                     {
42702                         tag: 'div',
42703                         cls: 'caret'
42704                     }
42705                 ]
42706             };
42707             
42708             var box = {
42709                 tag: 'div',
42710                 cls: this.hasFeedback ? 'has-feedback' : '',
42711                 cn: [
42712                     hiddenInput,
42713                     input,
42714                     {
42715                         tag: 'input',
42716                         cls: 'dial-code-holder',
42717                         disabled: true
42718                     }
42719                 ]
42720             };
42721             
42722             var container = {
42723                 cls: 'roo-select2-container input-group',
42724                 cn: [
42725                     flag_container,
42726                     box
42727                 ]
42728             };
42729             
42730             if (this.fieldLabel.length) {
42731                 var indicator = {
42732                     tag: 'i',
42733                     tooltip: 'This field is required'
42734                 };
42735                 
42736                 var label = {
42737                     tag: 'label',
42738                     'for':  id,
42739                     cls: 'control-label',
42740                     cn: []
42741                 };
42742                 
42743                 var label_text = {
42744                     tag: 'span',
42745                     html: this.fieldLabel
42746                 };
42747                 
42748                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42749                 label.cn = [
42750                     indicator,
42751                     label_text
42752                 ];
42753                 
42754                 if(this.indicatorpos == 'right') {
42755                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42756                     label.cn = [
42757                         label_text,
42758                         indicator
42759                     ];
42760                 }
42761                 
42762                 if(align == 'left') {
42763                     container = {
42764                         tag: 'div',
42765                         cn: [
42766                             container
42767                         ]
42768                     };
42769                     
42770                     if(this.labelWidth > 12){
42771                         label.style = "width: " + this.labelWidth + 'px';
42772                     }
42773                     if(this.labelWidth < 13 && this.labelmd == 0){
42774                         this.labelmd = this.labelWidth;
42775                     }
42776                     if(this.labellg > 0){
42777                         label.cls += ' col-lg-' + this.labellg;
42778                         input.cls += ' col-lg-' + (12 - this.labellg);
42779                     }
42780                     if(this.labelmd > 0){
42781                         label.cls += ' col-md-' + this.labelmd;
42782                         container.cls += ' col-md-' + (12 - this.labelmd);
42783                     }
42784                     if(this.labelsm > 0){
42785                         label.cls += ' col-sm-' + this.labelsm;
42786                         container.cls += ' col-sm-' + (12 - this.labelsm);
42787                     }
42788                     if(this.labelxs > 0){
42789                         label.cls += ' col-xs-' + this.labelxs;
42790                         container.cls += ' col-xs-' + (12 - this.labelxs);
42791                     }
42792                 }
42793             }
42794             
42795             cfg.cn = [
42796                 label,
42797                 container
42798             ];
42799             
42800             var settings = this;
42801             
42802             ['xs','sm','md','lg'].map(function(size){
42803                 if (settings[size]) {
42804                     cfg.cls += ' col-' + size + '-' + settings[size];
42805                 }
42806             });
42807             
42808             this.store = new Roo.data.Store({
42809                 proxy : new Roo.data.MemoryProxy({}),
42810                 reader : new Roo.data.JsonReader({
42811                     fields : [
42812                         {
42813                             'name' : 'name',
42814                             'type' : 'string'
42815                         },
42816                         {
42817                             'name' : 'iso2',
42818                             'type' : 'string'
42819                         },
42820                         {
42821                             'name' : 'dialCode',
42822                             'type' : 'string'
42823                         },
42824                         {
42825                             'name' : 'priority',
42826                             'type' : 'string'
42827                         },
42828                         {
42829                             'name' : 'areaCodes',
42830                             'type' : 'string'
42831                         }
42832                     ]
42833                 })
42834             });
42835             
42836             if(!this.preferedCountries) {
42837                 this.preferedCountries = [
42838                     'hk',
42839                     'gb',
42840                     'us'
42841                 ];
42842             }
42843             
42844             var p = this.preferedCountries.reverse();
42845             
42846             if(p) {
42847                 for (var i = 0; i < p.length; i++) {
42848                     for (var j = 0; j < this.allCountries.length; j++) {
42849                         if(this.allCountries[j].iso2 == p[i]) {
42850                             var t = this.allCountries[j];
42851                             this.allCountries.splice(j,1);
42852                             this.allCountries.unshift(t);
42853                         }
42854                     } 
42855                 }
42856             }
42857             
42858             this.store.proxy.data = {
42859                 success: true,
42860                 data: this.allCountries
42861             };
42862             
42863             return cfg;
42864         },
42865         
42866         initEvents : function()
42867         {
42868             this.createList();
42869             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42870             
42871             this.indicator = this.indicatorEl();
42872             this.flag = this.flagEl();
42873             this.dialCodeHolder = this.dialCodeHolderEl();
42874             
42875             this.trigger = this.el.select('div.flag-box',true).first();
42876             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42877             
42878             var _this = this;
42879             
42880             (function(){
42881                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42882                 _this.list.setWidth(lw);
42883             }).defer(100);
42884             
42885             this.list.on('mouseover', this.onViewOver, this);
42886             this.list.on('mousemove', this.onViewMove, this);
42887             this.inputEl().on("keyup", this.onKeyUp, this);
42888             this.inputEl().on("keypress", this.onKeyPress, this);
42889             
42890             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42891
42892             this.view = new Roo.View(this.list, this.tpl, {
42893                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42894             });
42895             
42896             this.view.on('click', this.onViewClick, this);
42897             this.setValue(this.defaultDialCode);
42898         },
42899         
42900         onTriggerClick : function(e)
42901         {
42902             Roo.log('trigger click');
42903             if(this.disabled){
42904                 return;
42905             }
42906             
42907             if(this.isExpanded()){
42908                 this.collapse();
42909                 this.hasFocus = false;
42910             }else {
42911                 this.store.load({});
42912                 this.hasFocus = true;
42913                 this.expand();
42914             }
42915         },
42916         
42917         isExpanded : function()
42918         {
42919             return this.list.isVisible();
42920         },
42921         
42922         collapse : function()
42923         {
42924             if(!this.isExpanded()){
42925                 return;
42926             }
42927             this.list.hide();
42928             Roo.get(document).un('mousedown', this.collapseIf, this);
42929             Roo.get(document).un('mousewheel', this.collapseIf, this);
42930             this.fireEvent('collapse', this);
42931             this.validate();
42932         },
42933         
42934         expand : function()
42935         {
42936             Roo.log('expand');
42937
42938             if(this.isExpanded() || !this.hasFocus){
42939                 return;
42940             }
42941             
42942             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42943             this.list.setWidth(lw);
42944             
42945             this.list.show();
42946             this.restrictHeight();
42947             
42948             Roo.get(document).on('mousedown', this.collapseIf, this);
42949             Roo.get(document).on('mousewheel', this.collapseIf, this);
42950             
42951             this.fireEvent('expand', this);
42952         },
42953         
42954         restrictHeight : function()
42955         {
42956             this.list.alignTo(this.inputEl(), this.listAlign);
42957             this.list.alignTo(this.inputEl(), this.listAlign);
42958         },
42959         
42960         onViewOver : function(e, t)
42961         {
42962             if(this.inKeyMode){
42963                 return;
42964             }
42965             var item = this.view.findItemFromChild(t);
42966             
42967             if(item){
42968                 var index = this.view.indexOf(item);
42969                 this.select(index, false);
42970             }
42971         },
42972
42973         // private
42974         onViewClick : function(view, doFocus, el, e)
42975         {
42976             var index = this.view.getSelectedIndexes()[0];
42977             
42978             var r = this.store.getAt(index);
42979             
42980             if(r){
42981                 this.onSelect(r, index);
42982             }
42983             if(doFocus !== false && !this.blockFocus){
42984                 this.inputEl().focus();
42985             }
42986         },
42987         
42988         onViewMove : function(e, t)
42989         {
42990             this.inKeyMode = false;
42991         },
42992         
42993         select : function(index, scrollIntoView)
42994         {
42995             this.selectedIndex = index;
42996             this.view.select(index);
42997             if(scrollIntoView !== false){
42998                 var el = this.view.getNode(index);
42999                 if(el){
43000                     this.list.scrollChildIntoView(el, false);
43001                 }
43002             }
43003         },
43004         
43005         createList : function()
43006         {
43007             this.list = Roo.get(document.body).createChild({
43008                 tag: 'ul',
43009                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43010                 style: 'display:none'
43011             });
43012             
43013             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43014         },
43015         
43016         collapseIf : function(e)
43017         {
43018             var in_combo  = e.within(this.el);
43019             var in_list =  e.within(this.list);
43020             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43021             
43022             if (in_combo || in_list || is_list) {
43023                 return;
43024             }
43025             this.collapse();
43026         },
43027         
43028         onSelect : function(record, index)
43029         {
43030             if(this.fireEvent('beforeselect', this, record, index) !== false){
43031                 
43032                 this.setFlagClass(record.data.iso2);
43033                 this.setDialCode(record.data.dialCode);
43034                 this.hasFocus = false;
43035                 this.collapse();
43036                 this.fireEvent('select', this, record, index);
43037             }
43038         },
43039         
43040         flagEl : function()
43041         {
43042             var flag = this.el.select('div.flag',true).first();
43043             if(!flag){
43044                 return false;
43045             }
43046             return flag;
43047         },
43048         
43049         dialCodeHolderEl : function()
43050         {
43051             var d = this.el.select('input.dial-code-holder',true).first();
43052             if(!d){
43053                 return false;
43054             }
43055             return d;
43056         },
43057         
43058         setDialCode : function(v)
43059         {
43060             this.dialCodeHolder.dom.value = '+'+v;
43061         },
43062         
43063         setFlagClass : function(n)
43064         {
43065             this.flag.dom.className = 'flag '+n;
43066         },
43067         
43068         getValue : function()
43069         {
43070             var v = this.inputEl().getValue();
43071             if(this.dialCodeHolder) {
43072                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43073             }
43074             return v;
43075         },
43076         
43077         setValue : function(v)
43078         {
43079             var d = this.getDialCode(v);
43080             
43081             //invalid dial code
43082             if(v.length == 0 || !d || d.length == 0) {
43083                 if(this.rendered){
43084                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43085                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43086                 }
43087                 return;
43088             }
43089             
43090             //valid dial code
43091             this.setFlagClass(this.dialCodeMapping[d].iso2);
43092             this.setDialCode(d);
43093             this.inputEl().dom.value = v.replace('+'+d,'');
43094             this.hiddenEl().dom.value = this.getValue();
43095             
43096             this.validate();
43097         },
43098         
43099         getDialCode : function(v)
43100         {
43101             v = v ||  '';
43102             
43103             if (v.length == 0) {
43104                 return this.dialCodeHolder.dom.value;
43105             }
43106             
43107             var dialCode = "";
43108             if (v.charAt(0) != "+") {
43109                 return false;
43110             }
43111             var numericChars = "";
43112             for (var i = 1; i < v.length; i++) {
43113               var c = v.charAt(i);
43114               if (!isNaN(c)) {
43115                 numericChars += c;
43116                 if (this.dialCodeMapping[numericChars]) {
43117                   dialCode = v.substr(1, i);
43118                 }
43119                 if (numericChars.length == 4) {
43120                   break;
43121                 }
43122               }
43123             }
43124             return dialCode;
43125         },
43126         
43127         reset : function()
43128         {
43129             this.setValue(this.defaultDialCode);
43130             this.validate();
43131         },
43132         
43133         hiddenEl : function()
43134         {
43135             return this.el.select('input.hidden-tel-input',true).first();
43136         },
43137         
43138         // after setting val
43139         onKeyUp : function(e){
43140             this.setValue(this.getValue());
43141         },
43142         
43143         onKeyPress : function(e){
43144             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43145                 e.stopEvent();
43146             }
43147         }
43148         
43149 });
43150 /**
43151  * @class Roo.bootstrap.MoneyField
43152  * @extends Roo.bootstrap.ComboBox
43153  * Bootstrap MoneyField class
43154  * 
43155  * @constructor
43156  * Create a new MoneyField.
43157  * @param {Object} config Configuration options
43158  */
43159
43160 Roo.bootstrap.MoneyField = function(config) {
43161     
43162     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43163     
43164 };
43165
43166 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43167     
43168     /**
43169      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43170      */
43171     allowDecimals : true,
43172     /**
43173      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43174      */
43175     decimalSeparator : ".",
43176     /**
43177      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43178      */
43179     decimalPrecision : 0,
43180     /**
43181      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43182      */
43183     allowNegative : true,
43184     /**
43185      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43186      */
43187     allowZero: true,
43188     /**
43189      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43190      */
43191     minValue : Number.NEGATIVE_INFINITY,
43192     /**
43193      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43194      */
43195     maxValue : Number.MAX_VALUE,
43196     /**
43197      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43198      */
43199     minText : "The minimum value for this field is {0}",
43200     /**
43201      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43202      */
43203     maxText : "The maximum value for this field is {0}",
43204     /**
43205      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43206      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43207      */
43208     nanText : "{0} is not a valid number",
43209     /**
43210      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43211      */
43212     castInt : true,
43213     /**
43214      * @cfg {String} defaults currency of the MoneyField
43215      * value should be in lkey
43216      */
43217     defaultCurrency : false,
43218     /**
43219      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43220      */
43221     thousandsDelimiter : false,
43222     /**
43223      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43224      */
43225     max_length: false,
43226     
43227     inputlg : 9,
43228     inputmd : 9,
43229     inputsm : 9,
43230     inputxs : 6,
43231     
43232     store : false,
43233     
43234     getAutoCreate : function()
43235     {
43236         var align = this.labelAlign || this.parentLabelAlign();
43237         
43238         var id = Roo.id();
43239
43240         var cfg = {
43241             cls: 'form-group',
43242             cn: []
43243         };
43244
43245         var input =  {
43246             tag: 'input',
43247             id : id,
43248             cls : 'form-control roo-money-amount-input',
43249             autocomplete: 'new-password'
43250         };
43251         
43252         var hiddenInput = {
43253             tag: 'input',
43254             type: 'hidden',
43255             id: Roo.id(),
43256             cls: 'hidden-number-input'
43257         };
43258         
43259         if(this.max_length) {
43260             input.maxlength = this.max_length; 
43261         }
43262         
43263         if (this.name) {
43264             hiddenInput.name = this.name;
43265         }
43266
43267         if (this.disabled) {
43268             input.disabled = true;
43269         }
43270
43271         var clg = 12 - this.inputlg;
43272         var cmd = 12 - this.inputmd;
43273         var csm = 12 - this.inputsm;
43274         var cxs = 12 - this.inputxs;
43275         
43276         var container = {
43277             tag : 'div',
43278             cls : 'row roo-money-field',
43279             cn : [
43280                 {
43281                     tag : 'div',
43282                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43283                     cn : [
43284                         {
43285                             tag : 'div',
43286                             cls: 'roo-select2-container input-group',
43287                             cn: [
43288                                 {
43289                                     tag : 'input',
43290                                     cls : 'form-control roo-money-currency-input',
43291                                     autocomplete: 'new-password',
43292                                     readOnly : 1,
43293                                     name : this.currencyName
43294                                 },
43295                                 {
43296                                     tag :'span',
43297                                     cls : 'input-group-addon',
43298                                     cn : [
43299                                         {
43300                                             tag: 'span',
43301                                             cls: 'caret'
43302                                         }
43303                                     ]
43304                                 }
43305                             ]
43306                         }
43307                     ]
43308                 },
43309                 {
43310                     tag : 'div',
43311                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43312                     cn : [
43313                         {
43314                             tag: 'div',
43315                             cls: this.hasFeedback ? 'has-feedback' : '',
43316                             cn: [
43317                                 input
43318                             ]
43319                         }
43320                     ]
43321                 }
43322             ]
43323             
43324         };
43325         
43326         if (this.fieldLabel.length) {
43327             var indicator = {
43328                 tag: 'i',
43329                 tooltip: 'This field is required'
43330             };
43331
43332             var label = {
43333                 tag: 'label',
43334                 'for':  id,
43335                 cls: 'control-label',
43336                 cn: []
43337             };
43338
43339             var label_text = {
43340                 tag: 'span',
43341                 html: this.fieldLabel
43342             };
43343
43344             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43345             label.cn = [
43346                 indicator,
43347                 label_text
43348             ];
43349
43350             if(this.indicatorpos == 'right') {
43351                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43352                 label.cn = [
43353                     label_text,
43354                     indicator
43355                 ];
43356             }
43357
43358             if(align == 'left') {
43359                 container = {
43360                     tag: 'div',
43361                     cn: [
43362                         container
43363                     ]
43364                 };
43365
43366                 if(this.labelWidth > 12){
43367                     label.style = "width: " + this.labelWidth + 'px';
43368                 }
43369                 if(this.labelWidth < 13 && this.labelmd == 0){
43370                     this.labelmd = this.labelWidth;
43371                 }
43372                 if(this.labellg > 0){
43373                     label.cls += ' col-lg-' + this.labellg;
43374                     input.cls += ' col-lg-' + (12 - this.labellg);
43375                 }
43376                 if(this.labelmd > 0){
43377                     label.cls += ' col-md-' + this.labelmd;
43378                     container.cls += ' col-md-' + (12 - this.labelmd);
43379                 }
43380                 if(this.labelsm > 0){
43381                     label.cls += ' col-sm-' + this.labelsm;
43382                     container.cls += ' col-sm-' + (12 - this.labelsm);
43383                 }
43384                 if(this.labelxs > 0){
43385                     label.cls += ' col-xs-' + this.labelxs;
43386                     container.cls += ' col-xs-' + (12 - this.labelxs);
43387                 }
43388             }
43389         }
43390
43391         cfg.cn = [
43392             label,
43393             container,
43394             hiddenInput
43395         ];
43396         
43397         var settings = this;
43398
43399         ['xs','sm','md','lg'].map(function(size){
43400             if (settings[size]) {
43401                 cfg.cls += ' col-' + size + '-' + settings[size];
43402             }
43403         });
43404         
43405         return cfg;
43406     },
43407     
43408     initEvents : function()
43409     {
43410         this.indicator = this.indicatorEl();
43411         
43412         this.initCurrencyEvent();
43413         
43414         this.initNumberEvent();
43415     },
43416     
43417     initCurrencyEvent : function()
43418     {
43419         if (!this.store) {
43420             throw "can not find store for combo";
43421         }
43422         
43423         this.store = Roo.factory(this.store, Roo.data);
43424         this.store.parent = this;
43425         
43426         this.createList();
43427         
43428         this.triggerEl = this.el.select('.input-group-addon', true).first();
43429         
43430         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43431         
43432         var _this = this;
43433         
43434         (function(){
43435             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43436             _this.list.setWidth(lw);
43437         }).defer(100);
43438         
43439         this.list.on('mouseover', this.onViewOver, this);
43440         this.list.on('mousemove', this.onViewMove, this);
43441         this.list.on('scroll', this.onViewScroll, this);
43442         
43443         if(!this.tpl){
43444             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43445         }
43446         
43447         this.view = new Roo.View(this.list, this.tpl, {
43448             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43449         });
43450         
43451         this.view.on('click', this.onViewClick, this);
43452         
43453         this.store.on('beforeload', this.onBeforeLoad, this);
43454         this.store.on('load', this.onLoad, this);
43455         this.store.on('loadexception', this.onLoadException, this);
43456         
43457         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43458             "up" : function(e){
43459                 this.inKeyMode = true;
43460                 this.selectPrev();
43461             },
43462
43463             "down" : function(e){
43464                 if(!this.isExpanded()){
43465                     this.onTriggerClick();
43466                 }else{
43467                     this.inKeyMode = true;
43468                     this.selectNext();
43469                 }
43470             },
43471
43472             "enter" : function(e){
43473                 this.collapse();
43474                 
43475                 if(this.fireEvent("specialkey", this, e)){
43476                     this.onViewClick(false);
43477                 }
43478                 
43479                 return true;
43480             },
43481
43482             "esc" : function(e){
43483                 this.collapse();
43484             },
43485
43486             "tab" : function(e){
43487                 this.collapse();
43488                 
43489                 if(this.fireEvent("specialkey", this, e)){
43490                     this.onViewClick(false);
43491                 }
43492                 
43493                 return true;
43494             },
43495
43496             scope : this,
43497
43498             doRelay : function(foo, bar, hname){
43499                 if(hname == 'down' || this.scope.isExpanded()){
43500                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43501                 }
43502                 return true;
43503             },
43504
43505             forceKeyDown: true
43506         });
43507         
43508         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43509         
43510     },
43511     
43512     initNumberEvent : function(e)
43513     {
43514         this.inputEl().on("keydown" , this.fireKey,  this);
43515         this.inputEl().on("focus", this.onFocus,  this);
43516         this.inputEl().on("blur", this.onBlur,  this);
43517         
43518         this.inputEl().relayEvent('keyup', this);
43519         
43520         if(this.indicator){
43521             this.indicator.addClass('invisible');
43522         }
43523  
43524         this.originalValue = this.getValue();
43525         
43526         if(this.validationEvent == 'keyup'){
43527             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43528             this.inputEl().on('keyup', this.filterValidation, this);
43529         }
43530         else if(this.validationEvent !== false){
43531             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43532         }
43533         
43534         if(this.selectOnFocus){
43535             this.on("focus", this.preFocus, this);
43536             
43537         }
43538         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43539             this.inputEl().on("keypress", this.filterKeys, this);
43540         } else {
43541             this.inputEl().relayEvent('keypress', this);
43542         }
43543         
43544         var allowed = "0123456789";
43545         
43546         if(this.allowDecimals){
43547             allowed += this.decimalSeparator;
43548         }
43549         
43550         if(this.allowNegative){
43551             allowed += "-";
43552         }
43553         
43554         if(this.thousandsDelimiter) {
43555             allowed += ",";
43556         }
43557         
43558         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43559         
43560         var keyPress = function(e){
43561             
43562             var k = e.getKey();
43563             
43564             var c = e.getCharCode();
43565             
43566             if(
43567                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43568                     allowed.indexOf(String.fromCharCode(c)) === -1
43569             ){
43570                 e.stopEvent();
43571                 return;
43572             }
43573             
43574             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43575                 return;
43576             }
43577             
43578             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43579                 e.stopEvent();
43580             }
43581         };
43582         
43583         this.inputEl().on("keypress", keyPress, this);
43584         
43585     },
43586     
43587     onTriggerClick : function(e)
43588     {   
43589         if(this.disabled){
43590             return;
43591         }
43592         
43593         this.page = 0;
43594         this.loadNext = false;
43595         
43596         if(this.isExpanded()){
43597             this.collapse();
43598             return;
43599         }
43600         
43601         this.hasFocus = true;
43602         
43603         if(this.triggerAction == 'all') {
43604             this.doQuery(this.allQuery, true);
43605             return;
43606         }
43607         
43608         this.doQuery(this.getRawValue());
43609     },
43610     
43611     getCurrency : function()
43612     {   
43613         var v = this.currencyEl().getValue();
43614         
43615         return v;
43616     },
43617     
43618     restrictHeight : function()
43619     {
43620         this.list.alignTo(this.currencyEl(), this.listAlign);
43621         this.list.alignTo(this.currencyEl(), this.listAlign);
43622     },
43623     
43624     onViewClick : function(view, doFocus, el, e)
43625     {
43626         var index = this.view.getSelectedIndexes()[0];
43627         
43628         var r = this.store.getAt(index);
43629         
43630         if(r){
43631             this.onSelect(r, index);
43632         }
43633     },
43634     
43635     onSelect : function(record, index){
43636         
43637         if(this.fireEvent('beforeselect', this, record, index) !== false){
43638         
43639             this.setFromCurrencyData(index > -1 ? record.data : false);
43640             
43641             this.collapse();
43642             
43643             this.fireEvent('select', this, record, index);
43644         }
43645     },
43646     
43647     setFromCurrencyData : function(o)
43648     {
43649         var currency = '';
43650         
43651         this.lastCurrency = o;
43652         
43653         if (this.currencyField) {
43654             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43655         } else {
43656             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43657         }
43658         
43659         this.lastSelectionText = currency;
43660         
43661         //setting default currency
43662         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43663             this.setCurrency(this.defaultCurrency);
43664             return;
43665         }
43666         
43667         this.setCurrency(currency);
43668     },
43669     
43670     setFromData : function(o)
43671     {
43672         var c = {};
43673         
43674         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43675         
43676         this.setFromCurrencyData(c);
43677         
43678         var value = '';
43679         
43680         if (this.name) {
43681             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43682         } else {
43683             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43684         }
43685         
43686         this.setValue(value);
43687         
43688     },
43689     
43690     setCurrency : function(v)
43691     {   
43692         this.currencyValue = v;
43693         
43694         if(this.rendered){
43695             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43696             this.validate();
43697         }
43698     },
43699     
43700     setValue : function(v)
43701     {
43702         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43703         
43704         this.value = v;
43705         
43706         if(this.rendered){
43707             
43708             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43709             
43710             this.inputEl().dom.value = (v == '') ? '' :
43711                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43712             
43713             if(!this.allowZero && v === '0') {
43714                 this.hiddenEl().dom.value = '';
43715                 this.inputEl().dom.value = '';
43716             }
43717             
43718             this.validate();
43719         }
43720     },
43721     
43722     getRawValue : function()
43723     {
43724         var v = this.inputEl().getValue();
43725         
43726         return v;
43727     },
43728     
43729     getValue : function()
43730     {
43731         return this.fixPrecision(this.parseValue(this.getRawValue()));
43732     },
43733     
43734     parseValue : function(value)
43735     {
43736         if(this.thousandsDelimiter) {
43737             value += "";
43738             r = new RegExp(",", "g");
43739             value = value.replace(r, "");
43740         }
43741         
43742         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43743         return isNaN(value) ? '' : value;
43744         
43745     },
43746     
43747     fixPrecision : function(value)
43748     {
43749         if(this.thousandsDelimiter) {
43750             value += "";
43751             r = new RegExp(",", "g");
43752             value = value.replace(r, "");
43753         }
43754         
43755         var nan = isNaN(value);
43756         
43757         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43758             return nan ? '' : value;
43759         }
43760         return parseFloat(value).toFixed(this.decimalPrecision);
43761     },
43762     
43763     decimalPrecisionFcn : function(v)
43764     {
43765         return Math.floor(v);
43766     },
43767     
43768     validateValue : function(value)
43769     {
43770         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43771             return false;
43772         }
43773         
43774         var num = this.parseValue(value);
43775         
43776         if(isNaN(num)){
43777             this.markInvalid(String.format(this.nanText, value));
43778             return false;
43779         }
43780         
43781         if(num < this.minValue){
43782             this.markInvalid(String.format(this.minText, this.minValue));
43783             return false;
43784         }
43785         
43786         if(num > this.maxValue){
43787             this.markInvalid(String.format(this.maxText, this.maxValue));
43788             return false;
43789         }
43790         
43791         return true;
43792     },
43793     
43794     validate : function()
43795     {
43796         if(this.disabled || this.allowBlank){
43797             this.markValid();
43798             return true;
43799         }
43800         
43801         var currency = this.getCurrency();
43802         
43803         if(this.validateValue(this.getRawValue()) && currency.length){
43804             this.markValid();
43805             return true;
43806         }
43807         
43808         this.markInvalid();
43809         return false;
43810     },
43811     
43812     getName: function()
43813     {
43814         return this.name;
43815     },
43816     
43817     beforeBlur : function()
43818     {
43819         if(!this.castInt){
43820             return;
43821         }
43822         
43823         var v = this.parseValue(this.getRawValue());
43824         
43825         if(v || v == 0){
43826             this.setValue(v);
43827         }
43828     },
43829     
43830     onBlur : function()
43831     {
43832         this.beforeBlur();
43833         
43834         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43835             //this.el.removeClass(this.focusClass);
43836         }
43837         
43838         this.hasFocus = false;
43839         
43840         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43841             this.validate();
43842         }
43843         
43844         var v = this.getValue();
43845         
43846         if(String(v) !== String(this.startValue)){
43847             this.fireEvent('change', this, v, this.startValue);
43848         }
43849         
43850         this.fireEvent("blur", this);
43851     },
43852     
43853     inputEl : function()
43854     {
43855         return this.el.select('.roo-money-amount-input', true).first();
43856     },
43857     
43858     currencyEl : function()
43859     {
43860         return this.el.select('.roo-money-currency-input', true).first();
43861     },
43862     
43863     hiddenEl : function()
43864     {
43865         return this.el.select('input.hidden-number-input',true).first();
43866     }
43867     
43868 });/**
43869  * @class Roo.bootstrap.BezierSignature
43870  * @extends Roo.bootstrap.Component
43871  * Bootstrap BezierSignature class
43872  * This script refer to:
43873  *    Title: Signature Pad
43874  *    Author: szimek
43875  *    Availability: https://github.com/szimek/signature_pad
43876  *
43877  * @constructor
43878  * Create a new BezierSignature
43879  * @param {Object} config The config object
43880  */
43881
43882 Roo.bootstrap.BezierSignature = function(config){
43883     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43884     this.addEvents({
43885         "resize" : true
43886     });
43887 };
43888
43889 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43890 {
43891      
43892     curve_data: [],
43893     
43894     is_empty: true,
43895     
43896     mouse_btn_down: true,
43897     
43898     /**
43899      * @cfg {int} canvas height
43900      */
43901     canvas_height: '200px',
43902     
43903     /**
43904      * @cfg {float|function} Radius of a single dot.
43905      */ 
43906     dot_size: false,
43907     
43908     /**
43909      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43910      */
43911     min_width: 0.5,
43912     
43913     /**
43914      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43915      */
43916     max_width: 2.5,
43917     
43918     /**
43919      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43920      */
43921     throttle: 16,
43922     
43923     /**
43924      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43925      */
43926     min_distance: 5,
43927     
43928     /**
43929      * @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.
43930      */
43931     bg_color: 'rgba(0, 0, 0, 0)',
43932     
43933     /**
43934      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43935      */
43936     dot_color: 'black',
43937     
43938     /**
43939      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43940      */ 
43941     velocity_filter_weight: 0.7,
43942     
43943     /**
43944      * @cfg {function} Callback when stroke begin. 
43945      */
43946     onBegin: false,
43947     
43948     /**
43949      * @cfg {function} Callback when stroke end.
43950      */
43951     onEnd: false,
43952     
43953     getAutoCreate : function()
43954     {
43955         var cls = 'roo-signature column';
43956         
43957         if(this.cls){
43958             cls += ' ' + this.cls;
43959         }
43960         
43961         var col_sizes = [
43962             'lg',
43963             'md',
43964             'sm',
43965             'xs'
43966         ];
43967         
43968         for(var i = 0; i < col_sizes.length; i++) {
43969             if(this[col_sizes[i]]) {
43970                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43971             }
43972         }
43973         
43974         var cfg = {
43975             tag: 'div',
43976             cls: cls,
43977             cn: [
43978                 {
43979                     tag: 'div',
43980                     cls: 'roo-signature-body',
43981                     cn: [
43982                         {
43983                             tag: 'canvas',
43984                             cls: 'roo-signature-body-canvas',
43985                             height: this.canvas_height,
43986                             width: this.canvas_width
43987                         }
43988                     ]
43989                 },
43990                 {
43991                     tag: 'input',
43992                     type: 'file',
43993                     style: 'display: none'
43994                 }
43995             ]
43996         };
43997         
43998         return cfg;
43999     },
44000     
44001     initEvents: function() 
44002     {
44003         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44004         
44005         var canvas = this.canvasEl();
44006         
44007         // mouse && touch event swapping...
44008         canvas.dom.style.touchAction = 'none';
44009         canvas.dom.style.msTouchAction = 'none';
44010         
44011         this.mouse_btn_down = false;
44012         canvas.on('mousedown', this._handleMouseDown, this);
44013         canvas.on('mousemove', this._handleMouseMove, this);
44014         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44015         
44016         if (window.PointerEvent) {
44017             canvas.on('pointerdown', this._handleMouseDown, this);
44018             canvas.on('pointermove', this._handleMouseMove, this);
44019             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44020         }
44021         
44022         if ('ontouchstart' in window) {
44023             canvas.on('touchstart', this._handleTouchStart, this);
44024             canvas.on('touchmove', this._handleTouchMove, this);
44025             canvas.on('touchend', this._handleTouchEnd, this);
44026         }
44027         
44028         Roo.EventManager.onWindowResize(this.resize, this, true);
44029         
44030         // file input event
44031         this.fileEl().on('change', this.uploadImage, this);
44032         
44033         this.clear();
44034         
44035         this.resize();
44036     },
44037     
44038     resize: function(){
44039         
44040         var canvas = this.canvasEl().dom;
44041         var ctx = this.canvasElCtx();
44042         var img_data = false;
44043         
44044         if(canvas.width > 0) {
44045             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44046         }
44047         // setting canvas width will clean img data
44048         canvas.width = 0;
44049         
44050         var style = window.getComputedStyle ? 
44051             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44052             
44053         var padding_left = parseInt(style.paddingLeft) || 0;
44054         var padding_right = parseInt(style.paddingRight) || 0;
44055         
44056         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44057         
44058         if(img_data) {
44059             ctx.putImageData(img_data, 0, 0);
44060         }
44061     },
44062     
44063     _handleMouseDown: function(e)
44064     {
44065         if (e.browserEvent.which === 1) {
44066             this.mouse_btn_down = true;
44067             this.strokeBegin(e);
44068         }
44069     },
44070     
44071     _handleMouseMove: function (e)
44072     {
44073         if (this.mouse_btn_down) {
44074             this.strokeMoveUpdate(e);
44075         }
44076     },
44077     
44078     _handleMouseUp: function (e)
44079     {
44080         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44081             this.mouse_btn_down = false;
44082             this.strokeEnd(e);
44083         }
44084     },
44085     
44086     _handleTouchStart: function (e) {
44087         
44088         e.preventDefault();
44089         if (e.browserEvent.targetTouches.length === 1) {
44090             // var touch = e.browserEvent.changedTouches[0];
44091             // this.strokeBegin(touch);
44092             
44093              this.strokeBegin(e); // assume e catching the correct xy...
44094         }
44095     },
44096     
44097     _handleTouchMove: function (e) {
44098         e.preventDefault();
44099         // var touch = event.targetTouches[0];
44100         // _this._strokeMoveUpdate(touch);
44101         this.strokeMoveUpdate(e);
44102     },
44103     
44104     _handleTouchEnd: function (e) {
44105         var wasCanvasTouched = e.target === this.canvasEl().dom;
44106         if (wasCanvasTouched) {
44107             e.preventDefault();
44108             // var touch = event.changedTouches[0];
44109             // _this._strokeEnd(touch);
44110             this.strokeEnd(e);
44111         }
44112     },
44113     
44114     reset: function () {
44115         this._lastPoints = [];
44116         this._lastVelocity = 0;
44117         this._lastWidth = (this.min_width + this.max_width) / 2;
44118         this.canvasElCtx().fillStyle = this.dot_color;
44119     },
44120     
44121     strokeMoveUpdate: function(e)
44122     {
44123         this.strokeUpdate(e);
44124         
44125         if (this.throttle) {
44126             this.throttleStroke(this.strokeUpdate, this.throttle);
44127         }
44128         else {
44129             this.strokeUpdate(e);
44130         }
44131     },
44132     
44133     strokeBegin: function(e)
44134     {
44135         var newPointGroup = {
44136             color: this.dot_color,
44137             points: []
44138         };
44139         
44140         if (typeof this.onBegin === 'function') {
44141             this.onBegin(e);
44142         }
44143         
44144         this.curve_data.push(newPointGroup);
44145         this.reset();
44146         this.strokeUpdate(e);
44147     },
44148     
44149     strokeUpdate: function(e)
44150     {
44151         var rect = this.canvasEl().dom.getBoundingClientRect();
44152         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44153         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44154         var lastPoints = lastPointGroup.points;
44155         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44156         var isLastPointTooClose = lastPoint
44157             ? point.distanceTo(lastPoint) <= this.min_distance
44158             : false;
44159         var color = lastPointGroup.color;
44160         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44161             var curve = this.addPoint(point);
44162             if (!lastPoint) {
44163                 this.drawDot({color: color, point: point});
44164             }
44165             else if (curve) {
44166                 this.drawCurve({color: color, curve: curve});
44167             }
44168             lastPoints.push({
44169                 time: point.time,
44170                 x: point.x,
44171                 y: point.y
44172             });
44173         }
44174     },
44175     
44176     strokeEnd: function(e)
44177     {
44178         this.strokeUpdate(e);
44179         if (typeof this.onEnd === 'function') {
44180             this.onEnd(e);
44181         }
44182     },
44183     
44184     addPoint:  function (point) {
44185         var _lastPoints = this._lastPoints;
44186         _lastPoints.push(point);
44187         if (_lastPoints.length > 2) {
44188             if (_lastPoints.length === 3) {
44189                 _lastPoints.unshift(_lastPoints[0]);
44190             }
44191             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44192             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44193             _lastPoints.shift();
44194             return curve;
44195         }
44196         return null;
44197     },
44198     
44199     calculateCurveWidths: function (startPoint, endPoint) {
44200         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44201             (1 - this.velocity_filter_weight) * this._lastVelocity;
44202
44203         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44204         var widths = {
44205             end: newWidth,
44206             start: this._lastWidth
44207         };
44208         
44209         this._lastVelocity = velocity;
44210         this._lastWidth = newWidth;
44211         return widths;
44212     },
44213     
44214     drawDot: function (_a) {
44215         var color = _a.color, point = _a.point;
44216         var ctx = this.canvasElCtx();
44217         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44218         ctx.beginPath();
44219         this.drawCurveSegment(point.x, point.y, width);
44220         ctx.closePath();
44221         ctx.fillStyle = color;
44222         ctx.fill();
44223     },
44224     
44225     drawCurve: function (_a) {
44226         var color = _a.color, curve = _a.curve;
44227         var ctx = this.canvasElCtx();
44228         var widthDelta = curve.endWidth - curve.startWidth;
44229         var drawSteps = Math.floor(curve.length()) * 2;
44230         ctx.beginPath();
44231         ctx.fillStyle = color;
44232         for (var i = 0; i < drawSteps; i += 1) {
44233         var t = i / drawSteps;
44234         var tt = t * t;
44235         var ttt = tt * t;
44236         var u = 1 - t;
44237         var uu = u * u;
44238         var uuu = uu * u;
44239         var x = uuu * curve.startPoint.x;
44240         x += 3 * uu * t * curve.control1.x;
44241         x += 3 * u * tt * curve.control2.x;
44242         x += ttt * curve.endPoint.x;
44243         var y = uuu * curve.startPoint.y;
44244         y += 3 * uu * t * curve.control1.y;
44245         y += 3 * u * tt * curve.control2.y;
44246         y += ttt * curve.endPoint.y;
44247         var width = curve.startWidth + ttt * widthDelta;
44248         this.drawCurveSegment(x, y, width);
44249         }
44250         ctx.closePath();
44251         ctx.fill();
44252     },
44253     
44254     drawCurveSegment: function (x, y, width) {
44255         var ctx = this.canvasElCtx();
44256         ctx.moveTo(x, y);
44257         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44258         this.is_empty = false;
44259     },
44260     
44261     clear: function()
44262     {
44263         var ctx = this.canvasElCtx();
44264         var canvas = this.canvasEl().dom;
44265         ctx.fillStyle = this.bg_color;
44266         ctx.clearRect(0, 0, canvas.width, canvas.height);
44267         ctx.fillRect(0, 0, canvas.width, canvas.height);
44268         this.curve_data = [];
44269         this.reset();
44270         this.is_empty = true;
44271     },
44272     
44273     fileEl: function()
44274     {
44275         return  this.el.select('input',true).first();
44276     },
44277     
44278     canvasEl: function()
44279     {
44280         return this.el.select('canvas',true).first();
44281     },
44282     
44283     canvasElCtx: function()
44284     {
44285         return this.el.select('canvas',true).first().dom.getContext('2d');
44286     },
44287     
44288     getImage: function(type)
44289     {
44290         if(this.is_empty) {
44291             return false;
44292         }
44293         
44294         // encryption ?
44295         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44296     },
44297     
44298     drawFromImage: function(img_src)
44299     {
44300         var img = new Image();
44301         
44302         img.onload = function(){
44303             this.canvasElCtx().drawImage(img, 0, 0);
44304         }.bind(this);
44305         
44306         img.src = img_src;
44307         
44308         this.is_empty = false;
44309     },
44310     
44311     selectImage: function()
44312     {
44313         this.fileEl().dom.click();
44314     },
44315     
44316     uploadImage: function(e)
44317     {
44318         var reader = new FileReader();
44319         
44320         reader.onload = function(e){
44321             var img = new Image();
44322             img.onload = function(){
44323                 this.reset();
44324                 this.canvasElCtx().drawImage(img, 0, 0);
44325             }.bind(this);
44326             img.src = e.target.result;
44327         }.bind(this);
44328         
44329         reader.readAsDataURL(e.target.files[0]);
44330     },
44331     
44332     // Bezier Point Constructor
44333     Point: (function () {
44334         function Point(x, y, time) {
44335             this.x = x;
44336             this.y = y;
44337             this.time = time || Date.now();
44338         }
44339         Point.prototype.distanceTo = function (start) {
44340             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44341         };
44342         Point.prototype.equals = function (other) {
44343             return this.x === other.x && this.y === other.y && this.time === other.time;
44344         };
44345         Point.prototype.velocityFrom = function (start) {
44346             return this.time !== start.time
44347             ? this.distanceTo(start) / (this.time - start.time)
44348             : 0;
44349         };
44350         return Point;
44351     }()),
44352     
44353     
44354     // Bezier Constructor
44355     Bezier: (function () {
44356         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44357             this.startPoint = startPoint;
44358             this.control2 = control2;
44359             this.control1 = control1;
44360             this.endPoint = endPoint;
44361             this.startWidth = startWidth;
44362             this.endWidth = endWidth;
44363         }
44364         Bezier.fromPoints = function (points, widths, scope) {
44365             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44366             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44367             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44368         };
44369         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44370             var dx1 = s1.x - s2.x;
44371             var dy1 = s1.y - s2.y;
44372             var dx2 = s2.x - s3.x;
44373             var dy2 = s2.y - s3.y;
44374             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44375             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44376             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44377             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44378             var dxm = m1.x - m2.x;
44379             var dym = m1.y - m2.y;
44380             var k = l2 / (l1 + l2);
44381             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44382             var tx = s2.x - cm.x;
44383             var ty = s2.y - cm.y;
44384             return {
44385                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44386                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44387             };
44388         };
44389         Bezier.prototype.length = function () {
44390             var steps = 10;
44391             var length = 0;
44392             var px;
44393             var py;
44394             for (var i = 0; i <= steps; i += 1) {
44395                 var t = i / steps;
44396                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44397                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44398                 if (i > 0) {
44399                     var xdiff = cx - px;
44400                     var ydiff = cy - py;
44401                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44402                 }
44403                 px = cx;
44404                 py = cy;
44405             }
44406             return length;
44407         };
44408         Bezier.prototype.point = function (t, start, c1, c2, end) {
44409             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44410             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44411             + (3.0 * c2 * (1.0 - t) * t * t)
44412             + (end * t * t * t);
44413         };
44414         return Bezier;
44415     }()),
44416     
44417     throttleStroke: function(fn, wait) {
44418       if (wait === void 0) { wait = 250; }
44419       var previous = 0;
44420       var timeout = null;
44421       var result;
44422       var storedContext;
44423       var storedArgs;
44424       var later = function () {
44425           previous = Date.now();
44426           timeout = null;
44427           result = fn.apply(storedContext, storedArgs);
44428           if (!timeout) {
44429               storedContext = null;
44430               storedArgs = [];
44431           }
44432       };
44433       return function wrapper() {
44434           var args = [];
44435           for (var _i = 0; _i < arguments.length; _i++) {
44436               args[_i] = arguments[_i];
44437           }
44438           var now = Date.now();
44439           var remaining = wait - (now - previous);
44440           storedContext = this;
44441           storedArgs = args;
44442           if (remaining <= 0 || remaining > wait) {
44443               if (timeout) {
44444                   clearTimeout(timeout);
44445                   timeout = null;
44446               }
44447               previous = now;
44448               result = fn.apply(storedContext, storedArgs);
44449               if (!timeout) {
44450                   storedContext = null;
44451                   storedArgs = [];
44452               }
44453           }
44454           else if (!timeout) {
44455               timeout = window.setTimeout(later, remaining);
44456           }
44457           return result;
44458       };
44459   }
44460   
44461 });
44462
44463  
44464
44465