Fix #6790 - Upload Button
[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  * @cfg {String} role default blank - set to button to force cursor pointer
654  
655  * 
656  * @constructor
657  * Create a new Element
658  * @param {Object} config The config object
659  */
660
661 Roo.bootstrap.Element = function(config){
662     Roo.bootstrap.Element.superclass.constructor.call(this, config);
663     
664     this.addEvents({
665         // raw events
666         /**
667          * @event click
668          * When a element is chick
669          * @param {Roo.bootstrap.Element} this
670          * @param {Roo.EventObject} e
671          */
672         "click" : true 
673         
674       
675     });
676 };
677
678 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
679     
680     tag: 'div',
681     cls: '',
682     html: '',
683     preventDefault: false, 
684     clickable: false,
685     tapedTwice : false,
686     role : false,
687     
688     getAutoCreate : function(){
689         
690         var cfg = {
691             tag: this.tag,
692             // cls: this.cls, double assign in parent class Component.js :: onRender
693             html: this.html
694         };
695         if (this.role !== false) {
696             cfg.role = this.role;
697         }
698         
699         return cfg;
700     },
701     
702     initEvents: function() 
703     {
704         Roo.bootstrap.Element.superclass.initEvents.call(this);
705         
706         if(this.clickable){
707             this.el.on('click', this.onClick, this);
708         }
709         
710         
711     },
712     
713     onClick : function(e)
714     {
715         if(this.preventDefault){
716             e.preventDefault();
717         }
718         
719         this.fireEvent('click', this, e); // why was this double click before?
720     },
721     
722     
723     
724
725     
726     
727     getValue : function()
728     {
729         return this.el.dom.innerHTML;
730     },
731     
732     setValue : function(value)
733     {
734         this.el.dom.innerHTML = value;
735     }
736    
737 });
738
739  
740
741  /*
742  * - LGPL
743  *
744  * dropable area
745  * 
746  */
747
748 /**
749  * @class Roo.bootstrap.DropTarget
750  * @extends Roo.bootstrap.Element
751  * Bootstrap DropTarget class
752  
753  * @cfg {string} name dropable name
754  * 
755  * @constructor
756  * Create a new Dropable Area
757  * @param {Object} config The config object
758  */
759
760 Roo.bootstrap.DropTarget = function(config){
761     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
762     
763     this.addEvents({
764         // raw events
765         /**
766          * @event click
767          * When a element is chick
768          * @param {Roo.bootstrap.Element} this
769          * @param {Roo.EventObject} e
770          */
771         "drop" : true
772     });
773 };
774
775 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
776     
777     
778     getAutoCreate : function(){
779         
780          
781     },
782     
783     initEvents: function() 
784     {
785         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
786         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
787             ddGroup: this.name,
788             listeners : {
789                 drop : this.dragDrop.createDelegate(this),
790                 enter : this.dragEnter.createDelegate(this),
791                 out : this.dragOut.createDelegate(this),
792                 over : this.dragOver.createDelegate(this)
793             }
794             
795         });
796         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
797     },
798     
799     dragDrop : function(source,e,data)
800     {
801         // user has to decide how to impliment this.
802         Roo.log('drop');
803         Roo.log(this);
804         //this.fireEvent('drop', this, source, e ,data);
805         return false;
806     },
807     
808     dragEnter : function(n, dd, e, data)
809     {
810         // probably want to resize the element to match the dropped element..
811         Roo.log("enter");
812         this.originalSize = this.el.getSize();
813         this.el.setSize( n.el.getSize());
814         this.dropZone.DDM.refreshCache(this.name);
815         Roo.log([n, dd, e, data]);
816     },
817     
818     dragOut : function(value)
819     {
820         // resize back to normal
821         Roo.log("out");
822         this.el.setSize(this.originalSize);
823         this.dropZone.resetConstraints();
824     },
825     
826     dragOver : function()
827     {
828         // ??? do nothing?
829     }
830    
831 });
832
833  
834
835  /*
836  * - LGPL
837  *
838  * Body
839  *
840  */
841
842 /**
843  * @class Roo.bootstrap.Body
844  * @extends Roo.bootstrap.Component
845  * Bootstrap Body class
846  *
847  * @constructor
848  * Create a new body
849  * @param {Object} config The config object
850  */
851
852 Roo.bootstrap.Body = function(config){
853
854     config = config || {};
855
856     Roo.bootstrap.Body.superclass.constructor.call(this, config);
857     this.el = Roo.get(config.el ? config.el : document.body );
858     if (this.cls && this.cls.length) {
859         Roo.get(document.body).addClass(this.cls);
860     }
861 };
862
863 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
864
865     is_body : true,// just to make sure it's constructed?
866
867         autoCreate : {
868         cls: 'container'
869     },
870     onRender : function(ct, position)
871     {
872        /* Roo.log("Roo.bootstrap.Body - onRender");
873         if (this.cls && this.cls.length) {
874             Roo.get(document.body).addClass(this.cls);
875         }
876         // style??? xttr???
877         */
878     }
879
880
881
882
883 });
884 /*
885  * - LGPL
886  *
887  * button group
888  * 
889  */
890
891
892 /**
893  * @class Roo.bootstrap.ButtonGroup
894  * @extends Roo.bootstrap.Component
895  * Bootstrap ButtonGroup class
896  * @cfg {String} size lg | sm | xs (default empty normal)
897  * @cfg {String} align vertical | justified  (default none)
898  * @cfg {String} direction up | down (default down)
899  * @cfg {Boolean} toolbar false | true
900  * @cfg {Boolean} btn true | false
901  * 
902  * 
903  * @constructor
904  * Create a new Input
905  * @param {Object} config The config object
906  */
907
908 Roo.bootstrap.ButtonGroup = function(config){
909     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
910 };
911
912 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
913     
914     size: '',
915     align: '',
916     direction: '',
917     toolbar: false,
918     btn: true,
919
920     getAutoCreate : function(){
921         var cfg = {
922             cls: 'btn-group',
923             html : null
924         };
925         
926         cfg.html = this.html || cfg.html;
927         
928         if (this.toolbar) {
929             cfg = {
930                 cls: 'btn-toolbar',
931                 html: null
932             };
933             
934             return cfg;
935         }
936         
937         if (['vertical','justified'].indexOf(this.align)!==-1) {
938             cfg.cls = 'btn-group-' + this.align;
939             
940             if (this.align == 'justified') {
941                 console.log(this.items);
942             }
943         }
944         
945         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
946             cfg.cls += ' btn-group-' + this.size;
947         }
948         
949         if (this.direction == 'up') {
950             cfg.cls += ' dropup' ;
951         }
952         
953         return cfg;
954     },
955     /**
956      * Add a button to the group (similar to NavItem API.)
957      */
958     addItem : function(cfg)
959     {
960         var cn = new Roo.bootstrap.Button(cfg);
961         //this.register(cn);
962         cn.parentId = this.id;
963         cn.onRender(this.el, null);
964         return cn;
965     }
966    
967 });
968
969  /*
970  * - LGPL
971  *
972  * button
973  * 
974  */
975
976 /**
977  * @class Roo.bootstrap.Button
978  * @extends Roo.bootstrap.Component
979  * Bootstrap Button class
980  * @cfg {String} html The button content
981  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
982  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
983  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
984  * @cfg {String} size (lg|sm|xs)
985  * @cfg {String} tag (a|input|submit)
986  * @cfg {String} href empty or href
987  * @cfg {Boolean} disabled default false;
988  * @cfg {Boolean} isClose default false;
989  * @cfg {String} glyphicon depricated - use fa
990  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
991  * @cfg {String} badge text for badge
992  * @cfg {String} theme (default|glow)  
993  * @cfg {Boolean} inverse dark themed version
994  * @cfg {Boolean} toggle is it a slidy toggle button
995  * @cfg {Boolean} pressed   default null - if the button ahs active state
996  * @cfg {String} ontext text for on slidy toggle state
997  * @cfg {String} offtext text for off slidy toggle state
998  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
999  * @cfg {Boolean} removeClass remove the standard class..
1000  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1001  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1002  * 
1003  * @constructor
1004  * Create a new button
1005  * @param {Object} config The config object
1006  */
1007
1008
1009 Roo.bootstrap.Button = function(config){
1010     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1011     
1012     this.addEvents({
1013         // raw events
1014         /**
1015          * @event click
1016          * When a button is pressed
1017          * @param {Roo.bootstrap.Button} btn
1018          * @param {Roo.EventObject} e
1019          */
1020         "click" : true,
1021         /**
1022          * @event dblclick
1023          * When a button is double clicked
1024          * @param {Roo.bootstrap.Button} btn
1025          * @param {Roo.EventObject} e
1026          */
1027         "dblclick" : true,
1028          /**
1029          * @event toggle
1030          * After the button has been toggles
1031          * @param {Roo.bootstrap.Button} btn
1032          * @param {Roo.EventObject} e
1033          * @param {boolean} pressed (also available as button.pressed)
1034          */
1035         "toggle" : true
1036     });
1037 };
1038
1039 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1040     html: false,
1041     active: false,
1042     weight: '',
1043     badge_weight: '',
1044     outline : false,
1045     size: '',
1046     tag: 'button',
1047     href: '',
1048     disabled: false,
1049     isClose: false,
1050     glyphicon: '',
1051     fa: '',
1052     badge: '',
1053     theme: 'default',
1054     inverse: false,
1055     
1056     toggle: false,
1057     ontext: 'ON',
1058     offtext: 'OFF',
1059     defaulton: true,
1060     preventDefault: true,
1061     removeClass: false,
1062     name: false,
1063     target: false,
1064     group : false,
1065      
1066     pressed : null,
1067      
1068     
1069     getAutoCreate : function(){
1070         
1071         var cfg = {
1072             tag : 'button',
1073             cls : 'roo-button',
1074             html: ''
1075         };
1076         
1077         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1078             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1079             this.tag = 'button';
1080         } else {
1081             cfg.tag = this.tag;
1082         }
1083         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1084         
1085         if (this.toggle == true) {
1086             cfg={
1087                 tag: 'div',
1088                 cls: 'slider-frame roo-button',
1089                 cn: [
1090                     {
1091                         tag: 'span',
1092                         'data-on-text':'ON',
1093                         'data-off-text':'OFF',
1094                         cls: 'slider-button',
1095                         html: this.offtext
1096                     }
1097                 ]
1098             };
1099             // why are we validating the weights?
1100             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1101                 cfg.cls +=  ' ' + this.weight;
1102             }
1103             
1104             return cfg;
1105         }
1106         
1107         if (this.isClose) {
1108             cfg.cls += ' close';
1109             
1110             cfg["aria-hidden"] = true;
1111             
1112             cfg.html = "&times;";
1113             
1114             return cfg;
1115         }
1116              
1117         
1118         if (this.theme==='default') {
1119             cfg.cls = 'btn roo-button';
1120             
1121             //if (this.parentType != 'Navbar') {
1122             this.weight = this.weight.length ?  this.weight : 'default';
1123             //}
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1127                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1128                 cfg.cls += ' btn-' + outline + weight;
1129                 if (this.weight == 'default') {
1130                     // BC
1131                     cfg.cls += ' btn-' + this.weight;
1132                 }
1133             }
1134         } else if (this.theme==='glow') {
1135             
1136             cfg.tag = 'a';
1137             cfg.cls = 'btn-glow roo-button';
1138             
1139             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1140                 
1141                 cfg.cls += ' ' + this.weight;
1142             }
1143         }
1144    
1145         
1146         if (this.inverse) {
1147             this.cls += ' inverse';
1148         }
1149         
1150         
1151         if (this.active || this.pressed === true) {
1152             cfg.cls += ' active';
1153         }
1154         
1155         if (this.disabled) {
1156             cfg.disabled = 'disabled';
1157         }
1158         
1159         if (this.items) {
1160             Roo.log('changing to ul' );
1161             cfg.tag = 'ul';
1162             this.glyphicon = 'caret';
1163             if (Roo.bootstrap.version == 4) {
1164                 this.fa = 'caret-down';
1165             }
1166             
1167         }
1168         
1169         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1170          
1171         //gsRoo.log(this.parentType);
1172         if (this.parentType === 'Navbar' && !this.parent().bar) {
1173             Roo.log('changing to li?');
1174             
1175             cfg.tag = 'li';
1176             
1177             cfg.cls = '';
1178             cfg.cn =  [{
1179                 tag : 'a',
1180                 cls : 'roo-button',
1181                 html : this.html,
1182                 href : this.href || '#'
1183             }];
1184             if (this.menu) {
1185                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1186                 cfg.cls += ' dropdown';
1187             }   
1188             
1189             delete cfg.html;
1190             
1191         }
1192         
1193        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1194         
1195         if (this.glyphicon) {
1196             cfg.html = ' ' + cfg.html;
1197             
1198             cfg.cn = [
1199                 {
1200                     tag: 'span',
1201                     cls: 'glyphicon glyphicon-' + this.glyphicon
1202                 }
1203             ];
1204         }
1205         if (this.fa) {
1206             cfg.html = ' ' + cfg.html;
1207             
1208             cfg.cn = [
1209                 {
1210                     tag: 'i',
1211                     cls: 'fa fas fa-' + this.fa
1212                 }
1213             ];
1214         }
1215         
1216         if (this.badge) {
1217             cfg.html += ' ';
1218             
1219             cfg.tag = 'a';
1220             
1221 //            cfg.cls='btn roo-button';
1222             
1223             cfg.href=this.href;
1224             
1225             var value = cfg.html;
1226             
1227             if(this.glyphicon){
1228                 value = {
1229                     tag: 'span',
1230                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1231                     html: this.html
1232                 };
1233             }
1234             if(this.fa){
1235                 value = {
1236                     tag: 'i',
1237                     cls: 'fa fas fa-' + this.fa,
1238                     html: this.html
1239                 };
1240             }
1241             
1242             var bw = this.badge_weight.length ? this.badge_weight :
1243                 (this.weight.length ? this.weight : 'secondary');
1244             bw = bw == 'default' ? 'secondary' : bw;
1245             
1246             cfg.cn = [
1247                 value,
1248                 {
1249                     tag: 'span',
1250                     cls: 'badge badge-' + bw,
1251                     html: this.badge
1252                 }
1253             ];
1254             
1255             cfg.html='';
1256         }
1257         
1258         if (this.menu) {
1259             cfg.cls += ' dropdown';
1260             cfg.html = typeof(cfg.html) != 'undefined' ?
1261                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1262         }
1263         
1264         if (cfg.tag !== 'a' && this.href !== '') {
1265             throw "Tag must be a to set href.";
1266         } else if (this.href.length > 0) {
1267             cfg.href = this.href;
1268         }
1269         
1270         if(this.removeClass){
1271             cfg.cls = '';
1272         }
1273         
1274         if(this.target){
1275             cfg.target = this.target;
1276         }
1277         
1278         return cfg;
1279     },
1280     initEvents: function() {
1281        // Roo.log('init events?');
1282 //        Roo.log(this.el.dom);
1283         // add the menu...
1284         
1285         if (typeof (this.menu) != 'undefined') {
1286             this.menu.parentType = this.xtype;
1287             this.menu.triggerEl = this.el;
1288             this.addxtype(Roo.apply({}, this.menu));
1289         }
1290
1291
1292         if (this.el.hasClass('roo-button')) {
1293              this.el.on('click', this.onClick, this);
1294              this.el.on('dblclick', this.onDblClick, this);
1295         } else {
1296              this.el.select('.roo-button').on('click', this.onClick, this);
1297              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1298              
1299         }
1300         // why?
1301         if(this.removeClass){
1302             this.el.on('click', this.onClick, this);
1303         }
1304         
1305         if (this.group === true) {
1306              if (this.pressed === false || this.pressed === true) {
1307                 // nothing
1308             } else {
1309                 this.pressed = false;
1310                 this.setActive(this.pressed);
1311             }
1312             
1313         }
1314         
1315         this.el.enableDisplayMode();
1316         
1317     },
1318     onClick : function(e)
1319     {
1320         if (this.disabled) {
1321             return;
1322         }
1323         
1324         Roo.log('button on click ');
1325         if(this.preventDefault){
1326             e.preventDefault();
1327         }
1328         
1329         if (this.group) {
1330             if (this.pressed) {
1331                 // do nothing -
1332                 return;
1333             }
1334             this.setActive(true);
1335             var pi = this.parent().items;
1336             for (var i = 0;i < pi.length;i++) {
1337                 if (this == pi[i]) {
1338                     continue;
1339                 }
1340                 if (pi[i].el.hasClass('roo-button')) {
1341                     pi[i].setActive(false);
1342                 }
1343             }
1344             this.fireEvent('click', this, e);            
1345             return;
1346         }
1347         
1348         if (this.pressed === true || this.pressed === false) {
1349             this.toggleActive(e);
1350         }
1351         
1352         
1353         this.fireEvent('click', this, e);
1354     },
1355     onDblClick: function(e)
1356     {
1357         if (this.disabled) {
1358             return;
1359         }
1360         if(this.preventDefault){
1361             e.preventDefault();
1362         }
1363         this.fireEvent('dblclick', this, e);
1364     },
1365     /**
1366      * Enables this button
1367      */
1368     enable : function()
1369     {
1370         this.disabled = false;
1371         this.el.removeClass('disabled');
1372         this.el.dom.removeAttribute("disabled");
1373     },
1374     
1375     /**
1376      * Disable this button
1377      */
1378     disable : function()
1379     {
1380         this.disabled = true;
1381         this.el.addClass('disabled');
1382         this.el.attr("disabled", "disabled")
1383     },
1384      /**
1385      * sets the active state on/off, 
1386      * @param {Boolean} state (optional) Force a particular state
1387      */
1388     setActive : function(v) {
1389         
1390         this.el[v ? 'addClass' : 'removeClass']('active');
1391         this.pressed = v;
1392     },
1393      /**
1394      * toggles the current active state 
1395      */
1396     toggleActive : function(e)
1397     {
1398         this.setActive(!this.pressed); // this modifies pressed...
1399         this.fireEvent('toggle', this, e, this.pressed);
1400     },
1401      /**
1402      * get the current active state
1403      * @return {boolean} true if it's active
1404      */
1405     isActive : function()
1406     {
1407         return this.el.hasClass('active');
1408     },
1409     /**
1410      * set the text of the first selected button
1411      */
1412     setText : function(str)
1413     {
1414         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1415     },
1416     /**
1417      * get the text of the first selected button
1418      */
1419     getText : function()
1420     {
1421         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1422     },
1423     
1424     setWeight : function(str)
1425     {
1426         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1427         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1428         this.weight = str;
1429         var outline = this.outline ? 'outline-' : '';
1430         if (str == 'default') {
1431             this.el.addClass('btn-default btn-outline-secondary');        
1432             return;
1433         }
1434         this.el.addClass('btn-' + outline + str);        
1435     }
1436     
1437     
1438 });
1439 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1440
1441 Roo.bootstrap.Button.weights = [
1442     'default',
1443     'secondary' ,
1444     'primary',
1445     'success',
1446     'info',
1447     'warning',
1448     'danger',
1449     'link',
1450     'light',
1451     'dark'              
1452    
1453 ];/*
1454  * - LGPL
1455  *
1456  * column
1457  * 
1458  */
1459
1460 /**
1461  * @class Roo.bootstrap.Column
1462  * @extends Roo.bootstrap.Component
1463  * Bootstrap Column class
1464  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1465  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1466  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1467  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1468  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1469  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1470  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1471  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1472  *
1473  * 
1474  * @cfg {Boolean} hidden (true|false) hide the element
1475  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1476  * @cfg {String} fa (ban|check|...) font awesome icon
1477  * @cfg {Number} fasize (1|2|....) font awsome size
1478
1479  * @cfg {String} icon (info-sign|check|...) glyphicon name
1480
1481  * @cfg {String} html content of column.
1482  * 
1483  * @constructor
1484  * Create a new Column
1485  * @param {Object} config The config object
1486  */
1487
1488 Roo.bootstrap.Column = function(config){
1489     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1490 };
1491
1492 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1493     
1494     xs: false,
1495     sm: false,
1496     md: false,
1497     lg: false,
1498     xsoff: false,
1499     smoff: false,
1500     mdoff: false,
1501     lgoff: false,
1502     html: '',
1503     offset: 0,
1504     alert: false,
1505     fa: false,
1506     icon : false,
1507     hidden : false,
1508     fasize : 1,
1509     
1510     getAutoCreate : function(){
1511         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1512         
1513         cfg = {
1514             tag: 'div',
1515             cls: 'column'
1516         };
1517         
1518         var settings=this;
1519         var sizes =   ['xs','sm','md','lg'];
1520         sizes.map(function(size ,ix){
1521             //Roo.log( size + ':' + settings[size]);
1522             
1523             if (settings[size+'off'] !== false) {
1524                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1525             }
1526             
1527             if (settings[size] === false) {
1528                 return;
1529             }
1530             
1531             if (!settings[size]) { // 0 = hidden
1532                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1533                 // bootsrap4
1534                 for (var i = ix; i > -1; i--) {
1535                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1536                 }
1537                 
1538                 
1539                 return;
1540             }
1541             cfg.cls += ' col-' + size + '-' + settings[size] + (
1542                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1543             );
1544             
1545         });
1546         
1547         if (this.hidden) {
1548             cfg.cls += ' hidden';
1549         }
1550         
1551         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1552             cfg.cls +=' alert alert-' + this.alert;
1553         }
1554         
1555         
1556         if (this.html.length) {
1557             cfg.html = this.html;
1558         }
1559         if (this.fa) {
1560             var fasize = '';
1561             if (this.fasize > 1) {
1562                 fasize = ' fa-' + this.fasize + 'x';
1563             }
1564             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1565             
1566             
1567         }
1568         if (this.icon) {
1569             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1570         }
1571         
1572         return cfg;
1573     }
1574    
1575 });
1576
1577  
1578
1579  /*
1580  * - LGPL
1581  *
1582  * page container.
1583  * 
1584  */
1585
1586
1587 /**
1588  * @class Roo.bootstrap.Container
1589  * @extends Roo.bootstrap.Component
1590  * Bootstrap Container class
1591  * @cfg {Boolean} jumbotron is it a jumbotron element
1592  * @cfg {String} html content of element
1593  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1594  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1595  * @cfg {String} header content of header (for panel)
1596  * @cfg {String} footer content of footer (for panel)
1597  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1598  * @cfg {String} tag (header|aside|section) type of HTML tag.
1599  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1600  * @cfg {String} fa font awesome icon
1601  * @cfg {String} icon (info-sign|check|...) glyphicon name
1602  * @cfg {Boolean} hidden (true|false) hide the element
1603  * @cfg {Boolean} expandable (true|false) default false
1604  * @cfg {Boolean} expanded (true|false) default true
1605  * @cfg {String} rheader contet on the right of header
1606  * @cfg {Boolean} clickable (true|false) default false
1607
1608  *     
1609  * @constructor
1610  * Create a new Container
1611  * @param {Object} config The config object
1612  */
1613
1614 Roo.bootstrap.Container = function(config){
1615     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1616     
1617     this.addEvents({
1618         // raw events
1619          /**
1620          * @event expand
1621          * After the panel has been expand
1622          * 
1623          * @param {Roo.bootstrap.Container} this
1624          */
1625         "expand" : true,
1626         /**
1627          * @event collapse
1628          * After the panel has been collapsed
1629          * 
1630          * @param {Roo.bootstrap.Container} this
1631          */
1632         "collapse" : true,
1633         /**
1634          * @event click
1635          * When a element is chick
1636          * @param {Roo.bootstrap.Container} this
1637          * @param {Roo.EventObject} e
1638          */
1639         "click" : true
1640     });
1641 };
1642
1643 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1644     
1645     jumbotron : false,
1646     well: '',
1647     panel : '',
1648     header: '',
1649     footer : '',
1650     sticky: '',
1651     tag : false,
1652     alert : false,
1653     fa: false,
1654     icon : false,
1655     expandable : false,
1656     rheader : '',
1657     expanded : true,
1658     clickable: false,
1659   
1660      
1661     getChildContainer : function() {
1662         
1663         if(!this.el){
1664             return false;
1665         }
1666         
1667         if (this.panel.length) {
1668             return this.el.select('.panel-body',true).first();
1669         }
1670         
1671         return this.el;
1672     },
1673     
1674     
1675     getAutoCreate : function(){
1676         
1677         var cfg = {
1678             tag : this.tag || 'div',
1679             html : '',
1680             cls : ''
1681         };
1682         if (this.jumbotron) {
1683             cfg.cls = 'jumbotron';
1684         }
1685         
1686         
1687         
1688         // - this is applied by the parent..
1689         //if (this.cls) {
1690         //    cfg.cls = this.cls + '';
1691         //}
1692         
1693         if (this.sticky.length) {
1694             
1695             var bd = Roo.get(document.body);
1696             if (!bd.hasClass('bootstrap-sticky')) {
1697                 bd.addClass('bootstrap-sticky');
1698                 Roo.select('html',true).setStyle('height', '100%');
1699             }
1700              
1701             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1702         }
1703         
1704         
1705         if (this.well.length) {
1706             switch (this.well) {
1707                 case 'lg':
1708                 case 'sm':
1709                     cfg.cls +=' well well-' +this.well;
1710                     break;
1711                 default:
1712                     cfg.cls +=' well';
1713                     break;
1714             }
1715         }
1716         
1717         if (this.hidden) {
1718             cfg.cls += ' hidden';
1719         }
1720         
1721         
1722         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1723             cfg.cls +=' alert alert-' + this.alert;
1724         }
1725         
1726         var body = cfg;
1727         
1728         if (this.panel.length) {
1729             cfg.cls += ' panel panel-' + this.panel;
1730             cfg.cn = [];
1731             if (this.header.length) {
1732                 
1733                 var h = [];
1734                 
1735                 if(this.expandable){
1736                     
1737                     cfg.cls = cfg.cls + ' expandable';
1738                     
1739                     h.push({
1740                         tag: 'i',
1741                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1742                     });
1743                     
1744                 }
1745                 
1746                 h.push(
1747                     {
1748                         tag: 'span',
1749                         cls : 'panel-title',
1750                         html : (this.expandable ? '&nbsp;' : '') + this.header
1751                     },
1752                     {
1753                         tag: 'span',
1754                         cls: 'panel-header-right',
1755                         html: this.rheader
1756                     }
1757                 );
1758                 
1759                 cfg.cn.push({
1760                     cls : 'panel-heading',
1761                     style : this.expandable ? 'cursor: pointer' : '',
1762                     cn : h
1763                 });
1764                 
1765             }
1766             
1767             body = false;
1768             cfg.cn.push({
1769                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1770                 html : this.html
1771             });
1772             
1773             
1774             if (this.footer.length) {
1775                 cfg.cn.push({
1776                     cls : 'panel-footer',
1777                     html : this.footer
1778                     
1779                 });
1780             }
1781             
1782         }
1783         
1784         if (body) {
1785             body.html = this.html || cfg.html;
1786             // prefix with the icons..
1787             if (this.fa) {
1788                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1789             }
1790             if (this.icon) {
1791                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1792             }
1793             
1794             
1795         }
1796         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1797             cfg.cls =  'container';
1798         }
1799         
1800         return cfg;
1801     },
1802     
1803     initEvents: function() 
1804     {
1805         if(this.expandable){
1806             var headerEl = this.headerEl();
1807         
1808             if(headerEl){
1809                 headerEl.on('click', this.onToggleClick, this);
1810             }
1811         }
1812         
1813         if(this.clickable){
1814             this.el.on('click', this.onClick, this);
1815         }
1816         
1817     },
1818     
1819     onToggleClick : function()
1820     {
1821         var headerEl = this.headerEl();
1822         
1823         if(!headerEl){
1824             return;
1825         }
1826         
1827         if(this.expanded){
1828             this.collapse();
1829             return;
1830         }
1831         
1832         this.expand();
1833     },
1834     
1835     expand : function()
1836     {
1837         if(this.fireEvent('expand', this)) {
1838             
1839             this.expanded = true;
1840             
1841             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1842             
1843             this.el.select('.panel-body',true).first().removeClass('hide');
1844             
1845             var toggleEl = this.toggleEl();
1846
1847             if(!toggleEl){
1848                 return;
1849             }
1850
1851             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1852         }
1853         
1854     },
1855     
1856     collapse : function()
1857     {
1858         if(this.fireEvent('collapse', this)) {
1859             
1860             this.expanded = false;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1863             this.el.select('.panel-body',true).first().addClass('hide');
1864         
1865             var toggleEl = this.toggleEl();
1866
1867             if(!toggleEl){
1868                 return;
1869             }
1870
1871             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1872         }
1873     },
1874     
1875     toggleEl : function()
1876     {
1877         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1878             return;
1879         }
1880         
1881         return this.el.select('.panel-heading .fa',true).first();
1882     },
1883     
1884     headerEl : function()
1885     {
1886         if(!this.el || !this.panel.length || !this.header.length){
1887             return;
1888         }
1889         
1890         return this.el.select('.panel-heading',true).first()
1891     },
1892     
1893     bodyEl : function()
1894     {
1895         if(!this.el || !this.panel.length){
1896             return;
1897         }
1898         
1899         return this.el.select('.panel-body',true).first()
1900     },
1901     
1902     titleEl : function()
1903     {
1904         if(!this.el || !this.panel.length || !this.header.length){
1905             return;
1906         }
1907         
1908         return this.el.select('.panel-title',true).first();
1909     },
1910     
1911     setTitle : function(v)
1912     {
1913         var titleEl = this.titleEl();
1914         
1915         if(!titleEl){
1916             return;
1917         }
1918         
1919         titleEl.dom.innerHTML = v;
1920     },
1921     
1922     getTitle : function()
1923     {
1924         
1925         var titleEl = this.titleEl();
1926         
1927         if(!titleEl){
1928             return '';
1929         }
1930         
1931         return titleEl.dom.innerHTML;
1932     },
1933     
1934     setRightTitle : function(v)
1935     {
1936         var t = this.el.select('.panel-header-right',true).first();
1937         
1938         if(!t){
1939             return;
1940         }
1941         
1942         t.dom.innerHTML = v;
1943     },
1944     
1945     onClick : function(e)
1946     {
1947         e.preventDefault();
1948         
1949         this.fireEvent('click', this, e);
1950     }
1951 });
1952
1953  /*
1954  *  - LGPL
1955  *
1956  *  This is BS4's Card element.. - similar to our containers probably..
1957  * 
1958  */
1959 /**
1960  * @class Roo.bootstrap.Card
1961  * @extends Roo.bootstrap.Component
1962  * Bootstrap Card class
1963  *
1964  *
1965  * possible... may not be implemented..
1966  * @cfg {String} header_image  src url of image.
1967  * @cfg {String|Object} header
1968  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1969  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1970  * 
1971  * @cfg {String} title
1972  * @cfg {String} subtitle
1973  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1974  * @cfg {String} footer
1975  
1976  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1977  * 
1978  * @cfg {String} margin (0|1|2|3|4|5|auto)
1979  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1980  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1981  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1982  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1983  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1985  *
1986  * @cfg {String} padding (0|1|2|3|4|5)
1987  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1988  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1989  * @cfg {String} padding_left (0|1|2|3|4|5)
1990  * @cfg {String} padding_right (0|1|2|3|4|5)
1991  * @cfg {String} padding_x (0|1|2|3|4|5)
1992  * @cfg {String} padding_y (0|1|2|3|4|5)
1993  *
1994  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1995  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1996  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1997  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1998  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1999  
2000  * @config {Boolean} dragable  if this card can be dragged.
2001  * @config {String} drag_group  group for drag
2002  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2003  * @config {String} drop_group  group for drag
2004  * 
2005  * @config {Boolean} collapsable can the body be collapsed.
2006  * @config {Boolean} collapsed is the body collapsed when rendered...
2007  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2008  * @config {Boolean} rotated is the body rotated when rendered...
2009  * 
2010  * @constructor
2011  * Create a new Container
2012  * @param {Object} config The config object
2013  */
2014
2015 Roo.bootstrap.Card = function(config){
2016     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2017     
2018     this.addEvents({
2019          // raw events
2020         /**
2021          * @event drop
2022          * When a element a card is dropped
2023          * @param {Roo.bootstrap.Card} this
2024          *
2025          * 
2026          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2027          * @param {String} position 'above' or 'below'
2028          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2029         
2030          */
2031         'drop' : true,
2032          /**
2033          * @event rotate
2034          * When a element a card is rotate
2035          * @param {Roo.bootstrap.Card} this
2036          * @param {Roo.Element} n the node being dropped?
2037          * @param {Boolean} rotate status
2038          */
2039         'rotate' : true,
2040         /**
2041          * @event cardover
2042          * When a card element is dragged over ready to drop (return false to block dropable)
2043          * @param {Roo.bootstrap.Card} this
2044          * @param {Object} data from dragdrop 
2045          */
2046          'cardover' : true
2047          
2048     });
2049 };
2050
2051
2052 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2053     
2054     
2055     weight : '',
2056     
2057     margin: '', /// may be better in component?
2058     margin_top: '', 
2059     margin_bottom: '', 
2060     margin_left: '',
2061     margin_right: '',
2062     margin_x: '',
2063     margin_y: '',
2064     
2065     padding : '',
2066     padding_top: '', 
2067     padding_bottom: '', 
2068     padding_left: '',
2069     padding_right: '',
2070     padding_x: '',
2071     padding_y: '',
2072     
2073     display: '', 
2074     display_xs: '', 
2075     display_sm: '', 
2076     display_lg: '',
2077     display_xl: '',
2078  
2079     header_image  : '',
2080     header : '',
2081     header_size : 0,
2082     title : '',
2083     subtitle : '',
2084     html : '',
2085     footer: '',
2086
2087     collapsable : false,
2088     collapsed : false,
2089     rotateable : false,
2090     rotated : false,
2091     
2092     dragable : false,
2093     drag_group : false,
2094     dropable : false,
2095     drop_group : false,
2096     childContainer : false,
2097     dropEl : false, /// the dom placeholde element that indicates drop location.
2098     containerEl: false, // body container
2099     bodyEl: false, // card-body
2100     headerContainerEl : false, //
2101     headerEl : false,
2102     header_imageEl : false,
2103     
2104     
2105     layoutCls : function()
2106     {
2107         var cls = '';
2108         var t = this;
2109         Roo.log(this.margin_bottom.length);
2110         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2111             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2112             
2113             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2114                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2115             }
2116             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2117                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2118             }
2119         });
2120         
2121         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2122             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2123                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2124             }
2125         });
2126         
2127         // more generic support?
2128         if (this.hidden) {
2129             cls += ' d-none';
2130         }
2131         
2132         return cls;
2133     },
2134  
2135        // Roo.log("Call onRender: " + this.xtype);
2136         /*  We are looking at something like this.
2137 <div class="card">
2138     <img src="..." class="card-img-top" alt="...">
2139     <div class="card-body">
2140         <h5 class="card-title">Card title</h5>
2141          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2142
2143         >> this bit is really the body...
2144         <div> << we will ad dthis in hopefully it will not break shit.
2145         
2146         ** card text does not actually have any styling...
2147         
2148             <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>
2149         
2150         </div> <<
2151           <a href="#" class="card-link">Card link</a>
2152           
2153     </div>
2154     <div class="card-footer">
2155         <small class="text-muted">Last updated 3 mins ago</small>
2156     </div>
2157 </div>
2158          */
2159     getAutoCreate : function(){
2160         
2161         var cfg = {
2162             tag : 'div',
2163             cls : 'card',
2164             cn : [ ]
2165         };
2166         
2167         if (this.weight.length && this.weight != 'light') {
2168             cfg.cls += ' text-white';
2169         } else {
2170             cfg.cls += ' text-dark'; // need as it's nested..
2171         }
2172         if (this.weight.length) {
2173             cfg.cls += ' bg-' + this.weight;
2174         }
2175         
2176         cfg.cls += ' ' + this.layoutCls(); 
2177         
2178         var hdr = false;
2179         var hdr_ctr = false;
2180         if (this.header.length) {
2181             hdr = {
2182                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2183                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2184                 cn : []
2185             };
2186             cfg.cn.push(hdr);
2187             hdr_ctr = hdr;
2188         } else {
2189             hdr = {
2190                 tag : 'div',
2191                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2192                 cn : []
2193             };
2194             cfg.cn.push(hdr);
2195             hdr_ctr = hdr;
2196         }
2197         if (this.collapsable) {
2198             hdr_ctr = {
2199             tag : 'a',
2200             cls : 'd-block user-select-none',
2201             cn: [
2202                     {
2203                         tag: 'i',
2204                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2205                     }
2206                    
2207                 ]
2208             };
2209             hdr.cn.push(hdr_ctr);
2210         }
2211         
2212         hdr_ctr.cn.push(        {
2213             tag: 'span',
2214             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2215             html : this.header
2216         });
2217         
2218         
2219         if (this.header_image.length) {
2220             cfg.cn.push({
2221                 tag : 'img',
2222                 cls : 'card-img-top',
2223                 src: this.header_image // escape?
2224             });
2225         } else {
2226             cfg.cn.push({
2227                     tag : 'div',
2228                     cls : 'card-img-top d-none' 
2229                 });
2230         }
2231             
2232         var body = {
2233             tag : 'div',
2234             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2235             cn : []
2236         };
2237         var obody = body;
2238         if (this.collapsable || this.rotateable) {
2239             obody = {
2240                 tag: 'div',
2241                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2242                 cn : [  body ]
2243             };
2244         }
2245         
2246         cfg.cn.push(obody);
2247         
2248         if (this.title.length) {
2249             body.cn.push({
2250                 tag : 'div',
2251                 cls : 'card-title',
2252                 src: this.title // escape?
2253             });
2254         }  
2255         
2256         if (this.subtitle.length) {
2257             body.cn.push({
2258                 tag : 'div',
2259                 cls : 'card-title',
2260                 src: this.subtitle // escape?
2261             });
2262         }
2263         
2264         body.cn.push({
2265             tag : 'div',
2266             cls : 'roo-card-body-ctr'
2267         });
2268         
2269         if (this.html.length) {
2270             body.cn.push({
2271                 tag: 'div',
2272                 html : this.html
2273             });
2274         }
2275         // fixme ? handle objects?
2276         
2277         if (this.footer.length) {
2278            
2279             cfg.cn.push({
2280                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2281                 html : this.footer
2282             });
2283             
2284         } else {
2285             cfg.cn.push({cls : 'card-footer d-none'});
2286         }
2287         
2288         // footer...
2289         
2290         return cfg;
2291     },
2292     
2293     
2294     getCardHeader : function()
2295     {
2296         var  ret = this.el.select('.card-header',true).first();
2297         if (ret.hasClass('d-none')) {
2298             ret.removeClass('d-none');
2299         }
2300         
2301         return ret;
2302     },
2303     getCardFooter : function()
2304     {
2305         var  ret = this.el.select('.card-footer',true).first();
2306         if (ret.hasClass('d-none')) {
2307             ret.removeClass('d-none');
2308         }
2309         
2310         return ret;
2311     },
2312     getCardImageTop : function()
2313     {
2314         var  ret = this.header_imageEl;
2315         if (ret.hasClass('d-none')) {
2316             ret.removeClass('d-none');
2317         }
2318             
2319         return ret;
2320     },
2321     
2322     getChildContainer : function()
2323     {
2324         
2325         if(!this.el){
2326             return false;
2327         }
2328         return this.el.select('.roo-card-body-ctr',true).first();    
2329     },
2330     
2331     initEvents: function() 
2332     {
2333         this.bodyEl = this.el.select('.card-body',true).first(); 
2334         this.containerEl = this.getChildContainer();
2335         if(this.dragable){
2336             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2337                     containerScroll: true,
2338                     ddGroup: this.drag_group || 'default_card_drag_group'
2339             });
2340             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2341         }
2342         if (this.dropable) {
2343             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2344                 containerScroll: true,
2345                 ddGroup: this.drop_group || 'default_card_drag_group'
2346             });
2347             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2348             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2349             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2350             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2351             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2352         }
2353         
2354         if (this.collapsable) {
2355             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2356         }
2357         if (this.rotateable) {
2358             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2359         }
2360         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2361          
2362         this.footerEl = this.el.select('.card-footer',true).first();
2363         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2364         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2365         this.headerEl = this.el.select('.card-header',true).first();
2366         
2367         if (this.rotated) {
2368             this.el.addClass('roo-card-rotated');
2369             this.fireEvent('rotate', this, true);
2370         }
2371         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2372         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2373         
2374     },
2375     getDragData : function(e)
2376     {
2377         var target = this.getEl();
2378         if (target) {
2379             //this.handleSelection(e);
2380             
2381             var dragData = {
2382                 source: this,
2383                 copy: false,
2384                 nodes: this.getEl(),
2385                 records: []
2386             };
2387             
2388             
2389             dragData.ddel = target.dom ;    // the div element
2390             Roo.log(target.getWidth( ));
2391             dragData.ddel.style.width = target.getWidth() + 'px';
2392             
2393             return dragData;
2394         }
2395         return false;
2396     },
2397     /**
2398     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2399     *    whole Element becomes the target, and this causes the drop gesture to append.
2400     *
2401     *    Returns an object:
2402     *     {
2403            
2404            position : 'below' or 'above'
2405            card  : relateive to card OBJECT (or true for no cards listed)
2406            items_n : relative to nth item in list
2407            card_n : relative to  nth card in list
2408     }
2409     *
2410     *    
2411     */
2412     getTargetFromEvent : function(e, dragged_card_el)
2413     {
2414         var target = e.getTarget();
2415         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2416             target = target.parentNode;
2417         }
2418         
2419         var ret = {
2420             position: '',
2421             cards : [],
2422             card_n : -1,
2423             items_n : -1,
2424             card : false 
2425         };
2426         
2427         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2428         // see if target is one of the 'cards'...
2429         
2430         
2431         //Roo.log(this.items.length);
2432         var pos = false;
2433         
2434         var last_card_n = 0;
2435         var cards_len  = 0;
2436         for (var i = 0;i< this.items.length;i++) {
2437             
2438             if (!this.items[i].el.hasClass('card')) {
2439                  continue;
2440             }
2441             pos = this.getDropPoint(e, this.items[i].el.dom);
2442             
2443             cards_len = ret.cards.length;
2444             //Roo.log(this.items[i].el.dom.id);
2445             ret.cards.push(this.items[i]);
2446             last_card_n  = i;
2447             if (ret.card_n < 0 && pos == 'above') {
2448                 ret.position = cards_len > 0 ? 'below' : pos;
2449                 ret.items_n = i > 0 ? i - 1 : 0;
2450                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2451                 ret.card = ret.cards[ret.card_n];
2452             }
2453         }
2454         if (!ret.cards.length) {
2455             ret.card = true;
2456             ret.position = 'below';
2457             ret.items_n;
2458             return ret;
2459         }
2460         // could not find a card.. stick it at the end..
2461         if (ret.card_n < 0) {
2462             ret.card_n = last_card_n;
2463             ret.card = ret.cards[last_card_n];
2464             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2465             ret.position = 'below';
2466         }
2467         
2468         if (this.items[ret.items_n].el == dragged_card_el) {
2469             return false;
2470         }
2471         
2472         if (ret.position == 'below') {
2473             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2474             
2475             if (card_after  && card_after.el == dragged_card_el) {
2476                 return false;
2477             }
2478             return ret;
2479         }
2480         
2481         // its's after ..
2482         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2483         
2484         if (card_before  && card_before.el == dragged_card_el) {
2485             return false;
2486         }
2487         
2488         return ret;
2489     },
2490     
2491     onNodeEnter : function(n, dd, e, data){
2492         return false;
2493     },
2494     onNodeOver : function(n, dd, e, data)
2495     {
2496        
2497         var target_info = this.getTargetFromEvent(e,data.source.el);
2498         if (target_info === false) {
2499             this.dropPlaceHolder('hide');
2500             return false;
2501         }
2502         Roo.log(['getTargetFromEvent', target_info ]);
2503         
2504         
2505         if (this.fireEvent('cardover', this, [ data ]) === false) {
2506             return false;
2507         }
2508         
2509         this.dropPlaceHolder('show', target_info,data);
2510         
2511         return false; 
2512     },
2513     onNodeOut : function(n, dd, e, data){
2514         this.dropPlaceHolder('hide');
2515      
2516     },
2517     onNodeDrop : function(n, dd, e, data)
2518     {
2519         
2520         // call drop - return false if
2521         
2522         // this could actually fail - if the Network drops..
2523         // we will ignore this at present..- client should probably reload
2524         // the whole set of cards if stuff like that fails.
2525         
2526         
2527         var info = this.getTargetFromEvent(e,data.source.el);
2528         if (info === false) {
2529             return false;
2530         }
2531         this.dropPlaceHolder('hide');
2532   
2533           
2534     
2535         this.acceptCard(data.source, info.position, info.card, info.items_n);
2536         return true;
2537          
2538     },
2539     firstChildCard : function()
2540     {
2541         for (var i = 0;i< this.items.length;i++) {
2542             
2543             if (!this.items[i].el.hasClass('card')) {
2544                  continue;
2545             }
2546             return this.items[i];
2547         }
2548         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2549     },
2550     /**
2551      * accept card
2552      *
2553      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2554      */
2555     acceptCard : function(move_card,  position, next_to_card )
2556     {
2557         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2558             return false;
2559         }
2560         
2561         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2562         
2563         move_card.parent().removeCard(move_card);
2564         
2565         
2566         var dom = move_card.el.dom;
2567         dom.style.width = ''; // clear with - which is set by drag.
2568         
2569         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2570             var cardel = next_to_card.el.dom;
2571             
2572             if (position == 'above' ) {
2573                 cardel.parentNode.insertBefore(dom, cardel);
2574             } else if (cardel.nextSibling) {
2575                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2576             } else {
2577                 cardel.parentNode.append(dom);
2578             }
2579         } else {
2580             // card container???
2581             this.containerEl.dom.append(dom);
2582         }
2583         
2584         //FIXME HANDLE card = true 
2585         
2586         // add this to the correct place in items.
2587         
2588         // remove Card from items.
2589         
2590        
2591         if (this.items.length) {
2592             var nitems = [];
2593             //Roo.log([info.items_n, info.position, this.items.length]);
2594             for (var i =0; i < this.items.length; i++) {
2595                 if (i == to_items_n && position == 'above') {
2596                     nitems.push(move_card);
2597                 }
2598                 nitems.push(this.items[i]);
2599                 if (i == to_items_n && position == 'below') {
2600                     nitems.push(move_card);
2601                 }
2602             }
2603             this.items = nitems;
2604             Roo.log(this.items);
2605         } else {
2606             this.items.push(move_card);
2607         }
2608         
2609         move_card.parentId = this.id;
2610         
2611         return true;
2612         
2613         
2614     },
2615     removeCard : function(c)
2616     {
2617         this.items = this.items.filter(function(e) { return e != c });
2618  
2619         var dom = c.el.dom;
2620         dom.parentNode.removeChild(dom);
2621         dom.style.width = ''; // clear with - which is set by drag.
2622         c.parentId = false;
2623         
2624     },
2625     
2626     /**    Decide whether to drop above or below a View node. */
2627     getDropPoint : function(e, n, dd)
2628     {
2629         if (dd) {
2630              return false;
2631         }
2632         if (n == this.containerEl.dom) {
2633             return "above";
2634         }
2635         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2636         var c = t + (b - t) / 2;
2637         var y = Roo.lib.Event.getPageY(e);
2638         if(y <= c) {
2639             return "above";
2640         }else{
2641             return "below";
2642         }
2643     },
2644     onToggleCollapse : function(e)
2645         {
2646         if (this.collapsed) {
2647             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2648             this.collapsableEl.addClass('show');
2649             this.collapsed = false;
2650             return;
2651         }
2652         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2653         this.collapsableEl.removeClass('show');
2654         this.collapsed = true;
2655         
2656     
2657     },
2658     
2659     onToggleRotate : function(e)
2660     {
2661         this.collapsableEl.removeClass('show');
2662         this.footerEl.removeClass('d-none');
2663         this.el.removeClass('roo-card-rotated');
2664         this.el.removeClass('d-none');
2665         if (this.rotated) {
2666             
2667             this.collapsableEl.addClass('show');
2668             this.rotated = false;
2669             this.fireEvent('rotate', this, this.rotated);
2670             return;
2671         }
2672         this.el.addClass('roo-card-rotated');
2673         this.footerEl.addClass('d-none');
2674         this.el.select('.roo-collapsable').removeClass('show');
2675         
2676         this.rotated = true;
2677         this.fireEvent('rotate', this, this.rotated);
2678     
2679     },
2680     
2681     dropPlaceHolder: function (action, info, data)
2682     {
2683         if (this.dropEl === false) {
2684             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2685             cls : 'd-none'
2686             },true);
2687         }
2688         this.dropEl.removeClass(['d-none', 'd-block']);        
2689         if (action == 'hide') {
2690             
2691             this.dropEl.addClass('d-none');
2692             return;
2693         }
2694         // FIXME - info.card == true!!!
2695         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2696         
2697         if (info.card !== true) {
2698             var cardel = info.card.el.dom;
2699             
2700             if (info.position == 'above') {
2701                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2702             } else if (cardel.nextSibling) {
2703                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2704             } else {
2705                 cardel.parentNode.append(this.dropEl.dom);
2706             }
2707         } else {
2708             // card container???
2709             this.containerEl.dom.append(this.dropEl.dom);
2710         }
2711         
2712         this.dropEl.addClass('d-block roo-card-dropzone');
2713         
2714         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2715         
2716         
2717     
2718     
2719     
2720     },
2721     setHeaderText: function(html)
2722     {
2723         this.header = html;
2724         if (this.headerContainerEl) {
2725             this.headerContainerEl.dom.innerHTML = html;
2726         }
2727     },
2728     onHeaderImageLoad : function(ev, he)
2729     {
2730         if (!this.header_image_fit_square) {
2731             return;
2732         }
2733         
2734         var hw = he.naturalHeight / he.naturalWidth;
2735         // wide image = < 0
2736         // tall image = > 1
2737         //var w = he.dom.naturalWidth;
2738         var ww = he.width;
2739         he.style.left =  0;
2740         he.style.position =  'relative';
2741         if (hw > 1) {
2742             var nw = (ww * (1/hw));
2743             Roo.get(he).setSize( ww * (1/hw),  ww);
2744             he.style.left =  ((ww - nw)/ 2) + 'px';
2745             he.style.position =  'relative';
2746         }
2747
2748     }
2749
2750     
2751 });
2752
2753 /*
2754  * - LGPL
2755  *
2756  * Card header - holder for the card header elements.
2757  * 
2758  */
2759
2760 /**
2761  * @class Roo.bootstrap.CardHeader
2762  * @extends Roo.bootstrap.Element
2763  * Bootstrap CardHeader class
2764  * @constructor
2765  * Create a new Card Header - that you can embed children into
2766  * @param {Object} config The config object
2767  */
2768
2769 Roo.bootstrap.CardHeader = function(config){
2770     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2771 };
2772
2773 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2774     
2775     
2776     container_method : 'getCardHeader' 
2777     
2778      
2779     
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * Card footer - holder for the card footer elements.
2790  * 
2791  */
2792
2793 /**
2794  * @class Roo.bootstrap.CardFooter
2795  * @extends Roo.bootstrap.Element
2796  * Bootstrap CardFooter class
2797  * @constructor
2798  * Create a new Card Footer - that you can embed children into
2799  * @param {Object} config The config object
2800  */
2801
2802 Roo.bootstrap.CardFooter = function(config){
2803     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2804 };
2805
2806 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2807     
2808     
2809     container_method : 'getCardFooter' 
2810     
2811      
2812     
2813     
2814    
2815 });
2816
2817  
2818
2819  /*
2820  * - LGPL
2821  *
2822  * Card header - holder for the card header elements.
2823  * 
2824  */
2825
2826 /**
2827  * @class Roo.bootstrap.CardImageTop
2828  * @extends Roo.bootstrap.Element
2829  * Bootstrap CardImageTop class
2830  * @constructor
2831  * Create a new Card Image Top container
2832  * @param {Object} config The config object
2833  */
2834
2835 Roo.bootstrap.CardImageTop = function(config){
2836     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2837 };
2838
2839 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2840     
2841    
2842     container_method : 'getCardImageTop' 
2843     
2844      
2845     
2846    
2847 });
2848
2849  
2850
2851  
2852 /*
2853 * Licence: LGPL
2854 */
2855
2856 /**
2857  * @class Roo.bootstrap.ButtonUploader
2858  * @extends Roo.bootstrap.Button
2859  * Bootstrap Button Uploader class - it's a button which when you add files to it
2860  *
2861  * 
2862  * @cfg {Number} errorTimeout default 3000
2863  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2864  * @cfg {Array}  html The button text.
2865
2866  *
2867  * @constructor
2868  * Create a new CardUploader
2869  * @param {Object} config The config object
2870  */
2871
2872 Roo.bootstrap.ButtonUploader = function(config){
2873     
2874  
2875     
2876     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2877     
2878      
2879      this.addEvents({
2880          // raw events
2881         /**
2882          * @event beforeselect
2883          * When button is pressed, before show upload files dialog is shown
2884          * @param {Roo.bootstrap.UploaderButton} this
2885          *
2886          */
2887         'beforeselect' : true,
2888          /**
2889          * @event fired when files have been selected, 
2890          * When a the download link is clicked
2891          * @param {Roo.bootstrap.Card} this
2892          * @param {Object} The image information data  contains 
2893          */
2894         'uploaded' : true
2895         
2896     });
2897 };
2898  
2899 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2900     
2901      
2902     errorTimeout : 3000,
2903      
2904     images : false,
2905    
2906     fileCollection : false,
2907     allowBlank : true,
2908     
2909     getAutoCreate : function()
2910     {
2911         
2912         
2913         return  {
2914             cls :'div' ,
2915             cn : [
2916                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2917                 {
2918                     tag: 'input',
2919                     multiple : 'multiple',
2920                     type : 'file',
2921                     cls : 'd-none  roo-card-upload-selector' 
2922                   
2923                 }
2924                  
2925
2926             ]
2927         };
2928            
2929          
2930     },
2931      
2932    
2933     initEvents : function()
2934     {
2935         
2936         Roo.bootstrap.Button.prototype.initEvents.call(this);
2937         
2938         
2939         
2940         
2941         
2942         this.urlAPI = (window.createObjectURL && window) || 
2943                                 (window.URL && URL.revokeObjectURL && URL) || 
2944                                 (window.webkitURL && webkitURL);
2945                         
2946          
2947          
2948          
2949         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2950         
2951         this.selectorEl.on('change', this.onFileSelected, this);
2952          
2953          
2954        
2955     },
2956     
2957    
2958     onClick : function(e)
2959     {
2960         e.preventDefault();
2961         
2962         if ( this.fireEvent('beforeselect', this) === false) {
2963             return;
2964         }
2965          
2966         this.selectorEl.dom.click();
2967          
2968     },
2969     
2970     onFileSelected : function(e)
2971     {
2972         e.preventDefault();
2973         
2974         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2975             return;
2976         }
2977         
2978         Roo.each(this.selectorEl.dom.files, function(file){
2979             var url = this.urlAPI.createObjectURL(file); // not sure...
2980             this.fireEvent('uploaded', this, [file, url]);
2981         }, this);
2982          
2983     },
2984     
2985        
2986    
2987     
2988     /**
2989      * addCard - add an Attachment to the uploader
2990      * @param data - the data about the image to upload
2991      *
2992      * {
2993           id : 123
2994           title : "Title of file",
2995           is_uploaded : false,
2996           src : "http://.....",
2997           srcfile : { the File upload object },
2998           mimetype : file.type,
2999           preview : false,
3000           is_deleted : 0
3001           .. any other data...
3002         }
3003      *
3004      * 
3005     */
3006      
3007     reset: function()
3008     {
3009          
3010          this.selectorEl
3011     } 
3012     
3013     
3014     
3015     
3016 });
3017  /*
3018  * - LGPL
3019  *
3020  * image
3021  * 
3022  */
3023
3024
3025 /**
3026  * @class Roo.bootstrap.Img
3027  * @extends Roo.bootstrap.Component
3028  * Bootstrap Img class
3029  * @cfg {Boolean} imgResponsive false | true
3030  * @cfg {String} border rounded | circle | thumbnail
3031  * @cfg {String} src image source
3032  * @cfg {String} alt image alternative text
3033  * @cfg {String} href a tag href
3034  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3035  * @cfg {String} xsUrl xs image source
3036  * @cfg {String} smUrl sm image source
3037  * @cfg {String} mdUrl md image source
3038  * @cfg {String} lgUrl lg image source
3039  * 
3040  * @constructor
3041  * Create a new Input
3042  * @param {Object} config The config object
3043  */
3044
3045 Roo.bootstrap.Img = function(config){
3046     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3047     
3048     this.addEvents({
3049         // img events
3050         /**
3051          * @event click
3052          * The img click event for the img.
3053          * @param {Roo.EventObject} e
3054          */
3055         "click" : true
3056     });
3057 };
3058
3059 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3060     
3061     imgResponsive: true,
3062     border: '',
3063     src: 'about:blank',
3064     href: false,
3065     target: false,
3066     xsUrl: '',
3067     smUrl: '',
3068     mdUrl: '',
3069     lgUrl: '',
3070
3071     getAutoCreate : function()
3072     {   
3073         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3074             return this.createSingleImg();
3075         }
3076         
3077         var cfg = {
3078             tag: 'div',
3079             cls: 'roo-image-responsive-group',
3080             cn: []
3081         };
3082         var _this = this;
3083         
3084         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3085             
3086             if(!_this[size + 'Url']){
3087                 return;
3088             }
3089             
3090             var img = {
3091                 tag: 'img',
3092                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3093                 html: _this.html || cfg.html,
3094                 src: _this[size + 'Url']
3095             };
3096             
3097             img.cls += ' roo-image-responsive-' + size;
3098             
3099             var s = ['xs', 'sm', 'md', 'lg'];
3100             
3101             s.splice(s.indexOf(size), 1);
3102             
3103             Roo.each(s, function(ss){
3104                 img.cls += ' hidden-' + ss;
3105             });
3106             
3107             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3108                 cfg.cls += ' img-' + _this.border;
3109             }
3110             
3111             if(_this.alt){
3112                 cfg.alt = _this.alt;
3113             }
3114             
3115             if(_this.href){
3116                 var a = {
3117                     tag: 'a',
3118                     href: _this.href,
3119                     cn: [
3120                         img
3121                     ]
3122                 };
3123
3124                 if(this.target){
3125                     a.target = _this.target;
3126                 }
3127             }
3128             
3129             cfg.cn.push((_this.href) ? a : img);
3130             
3131         });
3132         
3133         return cfg;
3134     },
3135     
3136     createSingleImg : function()
3137     {
3138         var cfg = {
3139             tag: 'img',
3140             cls: (this.imgResponsive) ? 'img-responsive' : '',
3141             html : null,
3142             src : 'about:blank'  // just incase src get's set to undefined?!?
3143         };
3144         
3145         cfg.html = this.html || cfg.html;
3146         
3147         cfg.src = this.src || cfg.src;
3148         
3149         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3150             cfg.cls += ' img-' + this.border;
3151         }
3152         
3153         if(this.alt){
3154             cfg.alt = this.alt;
3155         }
3156         
3157         if(this.href){
3158             var a = {
3159                 tag: 'a',
3160                 href: this.href,
3161                 cn: [
3162                     cfg
3163                 ]
3164             };
3165             
3166             if(this.target){
3167                 a.target = this.target;
3168             }
3169             
3170         }
3171         
3172         return (this.href) ? a : cfg;
3173     },
3174     
3175     initEvents: function() 
3176     {
3177         if(!this.href){
3178             this.el.on('click', this.onClick, this);
3179         }
3180         
3181     },
3182     
3183     onClick : function(e)
3184     {
3185         Roo.log('img onclick');
3186         this.fireEvent('click', this, e);
3187     },
3188     /**
3189      * Sets the url of the image - used to update it
3190      * @param {String} url the url of the image
3191      */
3192     
3193     setSrc : function(url)
3194     {
3195         this.src =  url;
3196         
3197         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3198             this.el.dom.src =  url;
3199             return;
3200         }
3201         
3202         this.el.select('img', true).first().dom.src =  url;
3203     }
3204     
3205     
3206    
3207 });
3208
3209  /*
3210  * - LGPL
3211  *
3212  * image
3213  * 
3214  */
3215
3216
3217 /**
3218  * @class Roo.bootstrap.Link
3219  * @extends Roo.bootstrap.Component
3220  * Bootstrap Link Class
3221  * @cfg {String} alt image alternative text
3222  * @cfg {String} href a tag href
3223  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3224  * @cfg {String} html the content of the link.
3225  * @cfg {String} anchor name for the anchor link
3226  * @cfg {String} fa - favicon
3227
3228  * @cfg {Boolean} preventDefault (true | false) default false
3229
3230  * 
3231  * @constructor
3232  * Create a new Input
3233  * @param {Object} config The config object
3234  */
3235
3236 Roo.bootstrap.Link = function(config){
3237     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3238     
3239     this.addEvents({
3240         // img events
3241         /**
3242          * @event click
3243          * The img click event for the img.
3244          * @param {Roo.EventObject} e
3245          */
3246         "click" : true
3247     });
3248 };
3249
3250 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3251     
3252     href: false,
3253     target: false,
3254     preventDefault: false,
3255     anchor : false,
3256     alt : false,
3257     fa: false,
3258
3259
3260     getAutoCreate : function()
3261     {
3262         var html = this.html || '';
3263         
3264         if (this.fa !== false) {
3265             html = '<i class="fa fa-' + this.fa + '"></i>';
3266         }
3267         var cfg = {
3268             tag: 'a'
3269         };
3270         // anchor's do not require html/href...
3271         if (this.anchor === false) {
3272             cfg.html = html;
3273             cfg.href = this.href || '#';
3274         } else {
3275             cfg.name = this.anchor;
3276             if (this.html !== false || this.fa !== false) {
3277                 cfg.html = html;
3278             }
3279             if (this.href !== false) {
3280                 cfg.href = this.href;
3281             }
3282         }
3283         
3284         if(this.alt !== false){
3285             cfg.alt = this.alt;
3286         }
3287         
3288         
3289         if(this.target !== false) {
3290             cfg.target = this.target;
3291         }
3292         
3293         return cfg;
3294     },
3295     
3296     initEvents: function() {
3297         
3298         if(!this.href || this.preventDefault){
3299             this.el.on('click', this.onClick, this);
3300         }
3301     },
3302     
3303     onClick : function(e)
3304     {
3305         if(this.preventDefault){
3306             e.preventDefault();
3307         }
3308         //Roo.log('img onclick');
3309         this.fireEvent('click', this, e);
3310     }
3311    
3312 });
3313
3314  /*
3315  * - LGPL
3316  *
3317  * header
3318  * 
3319  */
3320
3321 /**
3322  * @class Roo.bootstrap.Header
3323  * @extends Roo.bootstrap.Component
3324  * Bootstrap Header class
3325  * @cfg {String} html content of header
3326  * @cfg {Number} level (1|2|3|4|5|6) default 1
3327  * 
3328  * @constructor
3329  * Create a new Header
3330  * @param {Object} config The config object
3331  */
3332
3333
3334 Roo.bootstrap.Header  = function(config){
3335     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3336 };
3337
3338 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3339     
3340     //href : false,
3341     html : false,
3342     level : 1,
3343     
3344     
3345     
3346     getAutoCreate : function(){
3347         
3348         
3349         
3350         var cfg = {
3351             tag: 'h' + (1 *this.level),
3352             html: this.html || ''
3353         } ;
3354         
3355         return cfg;
3356     }
3357    
3358 });
3359
3360  
3361
3362  /*
3363  * Based on:
3364  * Ext JS Library 1.1.1
3365  * Copyright(c) 2006-2007, Ext JS, LLC.
3366  *
3367  * Originally Released Under LGPL - original licence link has changed is not relivant.
3368  *
3369  * Fork - LGPL
3370  * <script type="text/javascript">
3371  */
3372  
3373 /**
3374  * @class Roo.bootstrap.MenuMgr
3375  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3376  * @singleton
3377  */
3378 Roo.bootstrap.MenuMgr = function(){
3379    var menus, active, groups = {}, attached = false, lastShow = new Date();
3380
3381    // private - called when first menu is created
3382    function init(){
3383        menus = {};
3384        active = new Roo.util.MixedCollection();
3385        Roo.get(document).addKeyListener(27, function(){
3386            if(active.length > 0){
3387                hideAll();
3388            }
3389        });
3390    }
3391
3392    // private
3393    function hideAll(){
3394        if(active && active.length > 0){
3395            var c = active.clone();
3396            c.each(function(m){
3397                m.hide();
3398            });
3399        }
3400    }
3401
3402    // private
3403    function onHide(m){
3404        active.remove(m);
3405        if(active.length < 1){
3406            Roo.get(document).un("mouseup", onMouseDown);
3407             
3408            attached = false;
3409        }
3410    }
3411
3412    // private
3413    function onShow(m){
3414        var last = active.last();
3415        lastShow = new Date();
3416        active.add(m);
3417        if(!attached){
3418           Roo.get(document).on("mouseup", onMouseDown);
3419            
3420            attached = true;
3421        }
3422        if(m.parentMenu){
3423           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3424           m.parentMenu.activeChild = m;
3425        }else if(last && last.isVisible()){
3426           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3427        }
3428    }
3429
3430    // private
3431    function onBeforeHide(m){
3432        if(m.activeChild){
3433            m.activeChild.hide();
3434        }
3435        if(m.autoHideTimer){
3436            clearTimeout(m.autoHideTimer);
3437            delete m.autoHideTimer;
3438        }
3439    }
3440
3441    // private
3442    function onBeforeShow(m){
3443        var pm = m.parentMenu;
3444        if(!pm && !m.allowOtherMenus){
3445            hideAll();
3446        }else if(pm && pm.activeChild && active != m){
3447            pm.activeChild.hide();
3448        }
3449    }
3450
3451    // private this should really trigger on mouseup..
3452    function onMouseDown(e){
3453         Roo.log("on Mouse Up");
3454         
3455         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3456             Roo.log("MenuManager hideAll");
3457             hideAll();
3458             e.stopEvent();
3459         }
3460         
3461         
3462    }
3463
3464    // private
3465    function onBeforeCheck(mi, state){
3466        if(state){
3467            var g = groups[mi.group];
3468            for(var i = 0, l = g.length; i < l; i++){
3469                if(g[i] != mi){
3470                    g[i].setChecked(false);
3471                }
3472            }
3473        }
3474    }
3475
3476    return {
3477
3478        /**
3479         * Hides all menus that are currently visible
3480         */
3481        hideAll : function(){
3482             hideAll();  
3483        },
3484
3485        // private
3486        register : function(menu){
3487            if(!menus){
3488                init();
3489            }
3490            menus[menu.id] = menu;
3491            menu.on("beforehide", onBeforeHide);
3492            menu.on("hide", onHide);
3493            menu.on("beforeshow", onBeforeShow);
3494            menu.on("show", onShow);
3495            var g = menu.group;
3496            if(g && menu.events["checkchange"]){
3497                if(!groups[g]){
3498                    groups[g] = [];
3499                }
3500                groups[g].push(menu);
3501                menu.on("checkchange", onCheck);
3502            }
3503        },
3504
3505         /**
3506          * Returns a {@link Roo.menu.Menu} object
3507          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3508          * be used to generate and return a new Menu instance.
3509          */
3510        get : function(menu){
3511            if(typeof menu == "string"){ // menu id
3512                return menus[menu];
3513            }else if(menu.events){  // menu instance
3514                return menu;
3515            }
3516            /*else if(typeof menu.length == 'number'){ // array of menu items?
3517                return new Roo.bootstrap.Menu({items:menu});
3518            }else{ // otherwise, must be a config
3519                return new Roo.bootstrap.Menu(menu);
3520            }
3521            */
3522            return false;
3523        },
3524
3525        // private
3526        unregister : function(menu){
3527            delete menus[menu.id];
3528            menu.un("beforehide", onBeforeHide);
3529            menu.un("hide", onHide);
3530            menu.un("beforeshow", onBeforeShow);
3531            menu.un("show", onShow);
3532            var g = menu.group;
3533            if(g && menu.events["checkchange"]){
3534                groups[g].remove(menu);
3535                menu.un("checkchange", onCheck);
3536            }
3537        },
3538
3539        // private
3540        registerCheckable : function(menuItem){
3541            var g = menuItem.group;
3542            if(g){
3543                if(!groups[g]){
3544                    groups[g] = [];
3545                }
3546                groups[g].push(menuItem);
3547                menuItem.on("beforecheckchange", onBeforeCheck);
3548            }
3549        },
3550
3551        // private
3552        unregisterCheckable : function(menuItem){
3553            var g = menuItem.group;
3554            if(g){
3555                groups[g].remove(menuItem);
3556                menuItem.un("beforecheckchange", onBeforeCheck);
3557            }
3558        }
3559    };
3560 }();/*
3561  * - LGPL
3562  *
3563  * menu
3564  * 
3565  */
3566
3567 /**
3568  * @class Roo.bootstrap.Menu
3569  * @extends Roo.bootstrap.Component
3570  * Bootstrap Menu class - container for MenuItems
3571  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3572  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3573  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3574  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3575  * 
3576  * @constructor
3577  * Create a new Menu
3578  * @param {Object} config The config object
3579  */
3580
3581
3582 Roo.bootstrap.Menu = function(config){
3583     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3584     if (this.registerMenu && this.type != 'treeview')  {
3585         Roo.bootstrap.MenuMgr.register(this);
3586     }
3587     
3588     
3589     this.addEvents({
3590         /**
3591          * @event beforeshow
3592          * Fires before this menu is displayed (return false to block)
3593          * @param {Roo.menu.Menu} this
3594          */
3595         beforeshow : true,
3596         /**
3597          * @event beforehide
3598          * Fires before this menu is hidden (return false to block)
3599          * @param {Roo.menu.Menu} this
3600          */
3601         beforehide : true,
3602         /**
3603          * @event show
3604          * Fires after this menu is displayed
3605          * @param {Roo.menu.Menu} this
3606          */
3607         show : true,
3608         /**
3609          * @event hide
3610          * Fires after this menu is hidden
3611          * @param {Roo.menu.Menu} this
3612          */
3613         hide : true,
3614         /**
3615          * @event click
3616          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3617          * @param {Roo.menu.Menu} this
3618          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3619          * @param {Roo.EventObject} e
3620          */
3621         click : true,
3622         /**
3623          * @event mouseover
3624          * Fires when the mouse is hovering over this menu
3625          * @param {Roo.menu.Menu} this
3626          * @param {Roo.EventObject} e
3627          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3628          */
3629         mouseover : true,
3630         /**
3631          * @event mouseout
3632          * Fires when the mouse exits this menu
3633          * @param {Roo.menu.Menu} this
3634          * @param {Roo.EventObject} e
3635          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3636          */
3637         mouseout : true,
3638         /**
3639          * @event itemclick
3640          * Fires when a menu item contained in this menu is clicked
3641          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3642          * @param {Roo.EventObject} e
3643          */
3644         itemclick: true
3645     });
3646     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3647 };
3648
3649 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3650     
3651    /// html : false,
3652     //align : '',
3653     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3654     type: false,
3655     /**
3656      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3657      */
3658     registerMenu : true,
3659     
3660     menuItems :false, // stores the menu items..
3661     
3662     hidden:true,
3663         
3664     parentMenu : false,
3665     
3666     stopEvent : true,
3667     
3668     isLink : false,
3669     
3670     getChildContainer : function() {
3671         return this.el;  
3672     },
3673     
3674     getAutoCreate : function(){
3675          
3676         //if (['right'].indexOf(this.align)!==-1) {
3677         //    cfg.cn[1].cls += ' pull-right'
3678         //}
3679         
3680         
3681         var cfg = {
3682             tag : 'ul',
3683             cls : 'dropdown-menu' ,
3684             style : 'z-index:1000'
3685             
3686         };
3687         
3688         if (this.type === 'submenu') {
3689             cfg.cls = 'submenu active';
3690         }
3691         if (this.type === 'treeview') {
3692             cfg.cls = 'treeview-menu';
3693         }
3694         
3695         return cfg;
3696     },
3697     initEvents : function() {
3698         
3699        // Roo.log("ADD event");
3700        // Roo.log(this.triggerEl.dom);
3701         
3702         this.triggerEl.on('click', this.onTriggerClick, this);
3703         
3704         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3705         
3706         
3707         if (this.triggerEl.hasClass('nav-item')) {
3708             // dropdown toggle on the 'a' in BS4?
3709             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3710         } else {
3711             this.triggerEl.addClass('dropdown-toggle');
3712         }
3713         if (Roo.isTouch) {
3714             this.el.on('touchstart'  , this.onTouch, this);
3715         }
3716         this.el.on('click' , this.onClick, this);
3717
3718         this.el.on("mouseover", this.onMouseOver, this);
3719         this.el.on("mouseout", this.onMouseOut, this);
3720         
3721     },
3722     
3723     findTargetItem : function(e)
3724     {
3725         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3726         if(!t){
3727             return false;
3728         }
3729         //Roo.log(t);         Roo.log(t.id);
3730         if(t && t.id){
3731             //Roo.log(this.menuitems);
3732             return this.menuitems.get(t.id);
3733             
3734             //return this.items.get(t.menuItemId);
3735         }
3736         
3737         return false;
3738     },
3739     
3740     onTouch : function(e) 
3741     {
3742         Roo.log("menu.onTouch");
3743         //e.stopEvent(); this make the user popdown broken
3744         this.onClick(e);
3745     },
3746     
3747     onClick : function(e)
3748     {
3749         Roo.log("menu.onClick");
3750         
3751         var t = this.findTargetItem(e);
3752         if(!t || t.isContainer){
3753             return;
3754         }
3755         Roo.log(e);
3756         /*
3757         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3758             if(t == this.activeItem && t.shouldDeactivate(e)){
3759                 this.activeItem.deactivate();
3760                 delete this.activeItem;
3761                 return;
3762             }
3763             if(t.canActivate){
3764                 this.setActiveItem(t, true);
3765             }
3766             return;
3767             
3768             
3769         }
3770         */
3771        
3772         Roo.log('pass click event');
3773         
3774         t.onClick(e);
3775         
3776         this.fireEvent("click", this, t, e);
3777         
3778         var _this = this;
3779         
3780         if(!t.href.length || t.href == '#'){
3781             (function() { _this.hide(); }).defer(100);
3782         }
3783         
3784     },
3785     
3786     onMouseOver : function(e){
3787         var t  = this.findTargetItem(e);
3788         //Roo.log(t);
3789         //if(t){
3790         //    if(t.canActivate && !t.disabled){
3791         //        this.setActiveItem(t, true);
3792         //    }
3793         //}
3794         
3795         this.fireEvent("mouseover", this, e, t);
3796     },
3797     isVisible : function(){
3798         return !this.hidden;
3799     },
3800     onMouseOut : function(e){
3801         var t  = this.findTargetItem(e);
3802         
3803         //if(t ){
3804         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3805         //        this.activeItem.deactivate();
3806         //        delete this.activeItem;
3807         //    }
3808         //}
3809         this.fireEvent("mouseout", this, e, t);
3810     },
3811     
3812     
3813     /**
3814      * Displays this menu relative to another element
3815      * @param {String/HTMLElement/Roo.Element} element The element to align to
3816      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3817      * the element (defaults to this.defaultAlign)
3818      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3819      */
3820     show : function(el, pos, parentMenu)
3821     {
3822         if (false === this.fireEvent("beforeshow", this)) {
3823             Roo.log("show canceled");
3824             return;
3825         }
3826         this.parentMenu = parentMenu;
3827         if(!this.el){
3828             this.render();
3829         }
3830         
3831         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3832     },
3833      /**
3834      * Displays this menu at a specific xy position
3835      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3836      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3837      */
3838     showAt : function(xy, parentMenu, /* private: */_e){
3839         this.parentMenu = parentMenu;
3840         if(!this.el){
3841             this.render();
3842         }
3843         if(_e !== false){
3844             this.fireEvent("beforeshow", this);
3845             //xy = this.el.adjustForConstraints(xy);
3846         }
3847         
3848         //this.el.show();
3849         this.hideMenuItems();
3850         this.hidden = false;
3851         this.triggerEl.addClass('open');
3852         this.el.addClass('show');
3853         
3854         // reassign x when hitting right
3855         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3856             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3857         }
3858         
3859         // reassign y when hitting bottom
3860         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3861             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3862         }
3863         
3864         // but the list may align on trigger left or trigger top... should it be a properity?
3865         
3866         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3867             this.el.setXY(xy);
3868         }
3869         
3870         this.focus();
3871         this.fireEvent("show", this);
3872     },
3873     
3874     focus : function(){
3875         return;
3876         if(!this.hidden){
3877             this.doFocus.defer(50, this);
3878         }
3879     },
3880
3881     doFocus : function(){
3882         if(!this.hidden){
3883             this.focusEl.focus();
3884         }
3885     },
3886
3887     /**
3888      * Hides this menu and optionally all parent menus
3889      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3890      */
3891     hide : function(deep)
3892     {
3893         if (false === this.fireEvent("beforehide", this)) {
3894             Roo.log("hide canceled");
3895             return;
3896         }
3897         this.hideMenuItems();
3898         if(this.el && this.isVisible()){
3899            
3900             if(this.activeItem){
3901                 this.activeItem.deactivate();
3902                 this.activeItem = null;
3903             }
3904             this.triggerEl.removeClass('open');;
3905             this.el.removeClass('show');
3906             this.hidden = true;
3907             this.fireEvent("hide", this);
3908         }
3909         if(deep === true && this.parentMenu){
3910             this.parentMenu.hide(true);
3911         }
3912     },
3913     
3914     onTriggerClick : function(e)
3915     {
3916         Roo.log('trigger click');
3917         
3918         var target = e.getTarget();
3919         
3920         Roo.log(target.nodeName.toLowerCase());
3921         
3922         if(target.nodeName.toLowerCase() === 'i'){
3923             e.preventDefault();
3924         }
3925         
3926     },
3927     
3928     onTriggerPress  : function(e)
3929     {
3930         Roo.log('trigger press');
3931         //Roo.log(e.getTarget());
3932        // Roo.log(this.triggerEl.dom);
3933        
3934         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3935         var pel = Roo.get(e.getTarget());
3936         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3937             Roo.log('is treeview or dropdown?');
3938             return;
3939         }
3940         
3941         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3942             return;
3943         }
3944         
3945         if (this.isVisible()) {
3946             Roo.log('hide');
3947             this.hide();
3948         } else {
3949             Roo.log('show');
3950             this.show(this.triggerEl, '?', false);
3951         }
3952         
3953         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3954             e.stopEvent();
3955         }
3956         
3957     },
3958        
3959     
3960     hideMenuItems : function()
3961     {
3962         Roo.log("hide Menu Items");
3963         if (!this.el) { 
3964             return;
3965         }
3966         
3967         this.el.select('.open',true).each(function(aa) {
3968             
3969             aa.removeClass('open');
3970          
3971         });
3972     },
3973     addxtypeChild : function (tree, cntr) {
3974         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3975           
3976         this.menuitems.add(comp);
3977         return comp;
3978
3979     },
3980     getEl : function()
3981     {
3982         Roo.log(this.el);
3983         return this.el;
3984     },
3985     
3986     clear : function()
3987     {
3988         this.getEl().dom.innerHTML = '';
3989         this.menuitems.clear();
3990     }
3991 });
3992
3993  
3994  /*
3995  * - LGPL
3996  *
3997  * menu item
3998  * 
3999  */
4000
4001
4002 /**
4003  * @class Roo.bootstrap.MenuItem
4004  * @extends Roo.bootstrap.Component
4005  * Bootstrap MenuItem class
4006  * @cfg {String} html the menu label
4007  * @cfg {String} href the link
4008  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4009  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4010  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4011  * @cfg {String} fa favicon to show on left of menu item.
4012  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4013  * 
4014  * 
4015  * @constructor
4016  * Create a new MenuItem
4017  * @param {Object} config The config object
4018  */
4019
4020
4021 Roo.bootstrap.MenuItem = function(config){
4022     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4023     this.addEvents({
4024         // raw events
4025         /**
4026          * @event click
4027          * The raw click event for the entire grid.
4028          * @param {Roo.bootstrap.MenuItem} this
4029          * @param {Roo.EventObject} e
4030          */
4031         "click" : true
4032     });
4033 };
4034
4035 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4036     
4037     href : false,
4038     html : false,
4039     preventDefault: false,
4040     isContainer : false,
4041     active : false,
4042     fa: false,
4043     
4044     getAutoCreate : function(){
4045         
4046         if(this.isContainer){
4047             return {
4048                 tag: 'li',
4049                 cls: 'dropdown-menu-item '
4050             };
4051         }
4052         var ctag = {
4053             tag: 'span',
4054             html: 'Link'
4055         };
4056         
4057         var anc = {
4058             tag : 'a',
4059             cls : 'dropdown-item',
4060             href : '#',
4061             cn : [  ]
4062         };
4063         
4064         if (this.fa !== false) {
4065             anc.cn.push({
4066                 tag : 'i',
4067                 cls : 'fa fa-' + this.fa
4068             });
4069         }
4070         
4071         anc.cn.push(ctag);
4072         
4073         
4074         var cfg= {
4075             tag: 'li',
4076             cls: 'dropdown-menu-item',
4077             cn: [ anc ]
4078         };
4079         if (this.parent().type == 'treeview') {
4080             cfg.cls = 'treeview-menu';
4081         }
4082         if (this.active) {
4083             cfg.cls += ' active';
4084         }
4085         
4086         
4087         
4088         anc.href = this.href || cfg.cn[0].href ;
4089         ctag.html = this.html || cfg.cn[0].html ;
4090         return cfg;
4091     },
4092     
4093     initEvents: function()
4094     {
4095         if (this.parent().type == 'treeview') {
4096             this.el.select('a').on('click', this.onClick, this);
4097         }
4098         
4099         if (this.menu) {
4100             this.menu.parentType = this.xtype;
4101             this.menu.triggerEl = this.el;
4102             this.menu = this.addxtype(Roo.apply({}, this.menu));
4103         }
4104         
4105     },
4106     onClick : function(e)
4107     {
4108         Roo.log('item on click ');
4109         
4110         if(this.preventDefault){
4111             e.preventDefault();
4112         }
4113         //this.parent().hideMenuItems();
4114         
4115         this.fireEvent('click', this, e);
4116     },
4117     getEl : function()
4118     {
4119         return this.el;
4120     } 
4121 });
4122
4123  
4124
4125  /*
4126  * - LGPL
4127  *
4128  * menu separator
4129  * 
4130  */
4131
4132
4133 /**
4134  * @class Roo.bootstrap.MenuSeparator
4135  * @extends Roo.bootstrap.Component
4136  * Bootstrap MenuSeparator class
4137  * 
4138  * @constructor
4139  * Create a new MenuItem
4140  * @param {Object} config The config object
4141  */
4142
4143
4144 Roo.bootstrap.MenuSeparator = function(config){
4145     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4146 };
4147
4148 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4149     
4150     getAutoCreate : function(){
4151         var cfg = {
4152             cls: 'divider',
4153             tag : 'li'
4154         };
4155         
4156         return cfg;
4157     }
4158    
4159 });
4160
4161  
4162
4163  
4164 /*
4165 * Licence: LGPL
4166 */
4167
4168 /**
4169  * @class Roo.bootstrap.Modal
4170  * @extends Roo.bootstrap.Component
4171  * Bootstrap Modal class
4172  * @cfg {String} title Title of dialog
4173  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4174  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4175  * @cfg {Boolean} specificTitle default false
4176  * @cfg {Array} buttons Array of buttons or standard button set..
4177  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4178  * @cfg {Boolean} animate default true
4179  * @cfg {Boolean} allow_close default true
4180  * @cfg {Boolean} fitwindow default false
4181  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4182  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4183  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4184  * @cfg {String} size (sm|lg|xl) default empty
4185  * @cfg {Number} max_width set the max width of modal
4186  * @cfg {Boolean} editableTitle can the title be edited
4187
4188  *
4189  *
4190  * @constructor
4191  * Create a new Modal Dialog
4192  * @param {Object} config The config object
4193  */
4194
4195 Roo.bootstrap.Modal = function(config){
4196     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4197     this.addEvents({
4198         // raw events
4199         /**
4200          * @event btnclick
4201          * The raw btnclick event for the button
4202          * @param {Roo.EventObject} e
4203          */
4204         "btnclick" : true,
4205         /**
4206          * @event resize
4207          * Fire when dialog resize
4208          * @param {Roo.bootstrap.Modal} this
4209          * @param {Roo.EventObject} e
4210          */
4211         "resize" : true,
4212         /**
4213          * @event titlechanged
4214          * Fire when the editable title has been changed
4215          * @param {Roo.bootstrap.Modal} this
4216          * @param {Roo.EventObject} value
4217          */
4218         "titlechanged" : true 
4219         
4220     });
4221     this.buttons = this.buttons || [];
4222
4223     if (this.tmpl) {
4224         this.tmpl = Roo.factory(this.tmpl);
4225     }
4226
4227 };
4228
4229 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4230
4231     title : 'test dialog',
4232
4233     buttons : false,
4234
4235     // set on load...
4236
4237     html: false,
4238
4239     tmp: false,
4240
4241     specificTitle: false,
4242
4243     buttonPosition: 'right',
4244
4245     allow_close : true,
4246
4247     animate : true,
4248
4249     fitwindow: false,
4250     
4251      // private
4252     dialogEl: false,
4253     bodyEl:  false,
4254     footerEl:  false,
4255     titleEl:  false,
4256     closeEl:  false,
4257
4258     size: '',
4259     
4260     max_width: 0,
4261     
4262     max_height: 0,
4263     
4264     fit_content: false,
4265     editableTitle  : false,
4266
4267     onRender : function(ct, position)
4268     {
4269         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4270
4271         if(!this.el){
4272             var cfg = Roo.apply({},  this.getAutoCreate());
4273             cfg.id = Roo.id();
4274             //if(!cfg.name){
4275             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4276             //}
4277             //if (!cfg.name.length) {
4278             //    delete cfg.name;
4279            // }
4280             if (this.cls) {
4281                 cfg.cls += ' ' + this.cls;
4282             }
4283             if (this.style) {
4284                 cfg.style = this.style;
4285             }
4286             this.el = Roo.get(document.body).createChild(cfg, position);
4287         }
4288         //var type = this.el.dom.type;
4289
4290
4291         if(this.tabIndex !== undefined){
4292             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4293         }
4294
4295         this.dialogEl = this.el.select('.modal-dialog',true).first();
4296         this.bodyEl = this.el.select('.modal-body',true).first();
4297         this.closeEl = this.el.select('.modal-header .close', true).first();
4298         this.headerEl = this.el.select('.modal-header',true).first();
4299         this.titleEl = this.el.select('.modal-title',true).first();
4300         this.footerEl = this.el.select('.modal-footer',true).first();
4301
4302         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4303         
4304         //this.el.addClass("x-dlg-modal");
4305
4306         if (this.buttons.length) {
4307             Roo.each(this.buttons, function(bb) {
4308                 var b = Roo.apply({}, bb);
4309                 b.xns = b.xns || Roo.bootstrap;
4310                 b.xtype = b.xtype || 'Button';
4311                 if (typeof(b.listeners) == 'undefined') {
4312                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4313                 }
4314
4315                 var btn = Roo.factory(b);
4316
4317                 btn.render(this.getButtonContainer());
4318
4319             },this);
4320         }
4321         // render the children.
4322         var nitems = [];
4323
4324         if(typeof(this.items) != 'undefined'){
4325             var items = this.items;
4326             delete this.items;
4327
4328             for(var i =0;i < items.length;i++) {
4329                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4330             }
4331         }
4332
4333         this.items = nitems;
4334
4335         // where are these used - they used to be body/close/footer
4336
4337
4338         this.initEvents();
4339         //this.el.addClass([this.fieldClass, this.cls]);
4340
4341     },
4342
4343     getAutoCreate : function()
4344     {
4345         // we will default to modal-body-overflow - might need to remove or make optional later.
4346         var bdy = {
4347                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4348                 html : this.html || ''
4349         };
4350
4351         var title = {
4352             tag: 'h5',
4353             cls : 'modal-title',
4354             html : this.title
4355         };
4356
4357         if(this.specificTitle){ // WTF is this?
4358             title = this.title;
4359         }
4360
4361         var header = [];
4362         if (this.allow_close && Roo.bootstrap.version == 3) {
4363             header.push({
4364                 tag: 'button',
4365                 cls : 'close',
4366                 html : '&times'
4367             });
4368         }
4369
4370         header.push(title);
4371
4372         if (this.editableTitle) {
4373             header.push({
4374                 cls: 'form-control roo-editable-title d-none',
4375                 tag: 'input',
4376                 type: 'text'
4377             });
4378         }
4379         
4380         if (this.allow_close && Roo.bootstrap.version == 4) {
4381             header.push({
4382                 tag: 'button',
4383                 cls : 'close',
4384                 html : '&times'
4385             });
4386         }
4387         
4388         var size = '';
4389
4390         if(this.size.length){
4391             size = 'modal-' + this.size;
4392         }
4393         
4394         var footer = Roo.bootstrap.version == 3 ?
4395             {
4396                 cls : 'modal-footer',
4397                 cn : [
4398                     {
4399                         tag: 'div',
4400                         cls: 'btn-' + this.buttonPosition
4401                     }
4402                 ]
4403
4404             } :
4405             {  // BS4 uses mr-auto on left buttons....
4406                 cls : 'modal-footer'
4407             };
4408
4409             
4410
4411         
4412         
4413         var modal = {
4414             cls: "modal",
4415              cn : [
4416                 {
4417                     cls: "modal-dialog " + size,
4418                     cn : [
4419                         {
4420                             cls : "modal-content",
4421                             cn : [
4422                                 {
4423                                     cls : 'modal-header',
4424                                     cn : header
4425                                 },
4426                                 bdy,
4427                                 footer
4428                             ]
4429
4430                         }
4431                     ]
4432
4433                 }
4434             ]
4435         };
4436
4437         if(this.animate){
4438             modal.cls += ' fade';
4439         }
4440
4441         return modal;
4442
4443     },
4444     getChildContainer : function() {
4445
4446          return this.bodyEl;
4447
4448     },
4449     getButtonContainer : function() {
4450         
4451          return Roo.bootstrap.version == 4 ?
4452             this.el.select('.modal-footer',true).first()
4453             : this.el.select('.modal-footer div',true).first();
4454
4455     },
4456     initEvents : function()
4457     {
4458         if (this.allow_close) {
4459             this.closeEl.on('click', this.hide, this);
4460         }
4461         Roo.EventManager.onWindowResize(this.resize, this, true);
4462         if (this.editableTitle) {
4463             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4464             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4465             this.headerEditEl.on('keyup', function(e) {
4466                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4467                         this.toggleHeaderInput(false)
4468                     }
4469                 }, this);
4470             this.headerEditEl.on('blur', function(e) {
4471                 this.toggleHeaderInput(false)
4472             },this);
4473         }
4474
4475     },
4476   
4477
4478     resize : function()
4479     {
4480         this.maskEl.setSize(
4481             Roo.lib.Dom.getViewWidth(true),
4482             Roo.lib.Dom.getViewHeight(true)
4483         );
4484         
4485         if (this.fitwindow) {
4486             
4487            this.dialogEl.setStyle( { 'max-width' : '100%' });
4488             this.setSize(
4489                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4490                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4491             );
4492             return;
4493         }
4494         
4495         if(this.max_width !== 0) {
4496             
4497             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4498             
4499             if(this.height) {
4500                 this.setSize(w, this.height);
4501                 return;
4502             }
4503             
4504             if(this.max_height) {
4505                 this.setSize(w,Math.min(
4506                     this.max_height,
4507                     Roo.lib.Dom.getViewportHeight(true) - 60
4508                 ));
4509                 
4510                 return;
4511             }
4512             
4513             if(!this.fit_content) {
4514                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4515                 return;
4516             }
4517             
4518             this.setSize(w, Math.min(
4519                 60 +
4520                 this.headerEl.getHeight() + 
4521                 this.footerEl.getHeight() + 
4522                 this.getChildHeight(this.bodyEl.dom.childNodes),
4523                 Roo.lib.Dom.getViewportHeight(true) - 60)
4524             );
4525         }
4526         
4527     },
4528
4529     setSize : function(w,h)
4530     {
4531         if (!w && !h) {
4532             return;
4533         }
4534         
4535         this.resizeTo(w,h);
4536     },
4537
4538     show : function() {
4539
4540         if (!this.rendered) {
4541             this.render();
4542         }
4543         this.toggleHeaderInput(false);
4544         //this.el.setStyle('display', 'block');
4545         this.el.removeClass('hideing');
4546         this.el.dom.style.display='block';
4547         
4548         Roo.get(document.body).addClass('modal-open');
4549  
4550         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4551             
4552             (function(){
4553                 this.el.addClass('show');
4554                 this.el.addClass('in');
4555             }).defer(50, this);
4556         }else{
4557             this.el.addClass('show');
4558             this.el.addClass('in');
4559         }
4560
4561         // not sure how we can show data in here..
4562         //if (this.tmpl) {
4563         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4564         //}
4565
4566         Roo.get(document.body).addClass("x-body-masked");
4567         
4568         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4569         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4570         this.maskEl.dom.style.display = 'block';
4571         this.maskEl.addClass('show');
4572         
4573         
4574         this.resize();
4575         
4576         this.fireEvent('show', this);
4577
4578         // set zindex here - otherwise it appears to be ignored...
4579         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4580
4581         (function () {
4582             this.items.forEach( function(e) {
4583                 e.layout ? e.layout() : false;
4584
4585             });
4586         }).defer(100,this);
4587
4588     },
4589     hide : function()
4590     {
4591         if(this.fireEvent("beforehide", this) !== false){
4592             
4593             this.maskEl.removeClass('show');
4594             
4595             this.maskEl.dom.style.display = '';
4596             Roo.get(document.body).removeClass("x-body-masked");
4597             this.el.removeClass('in');
4598             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4599
4600             if(this.animate){ // why
4601                 this.el.addClass('hideing');
4602                 this.el.removeClass('show');
4603                 (function(){
4604                     if (!this.el.hasClass('hideing')) {
4605                         return; // it's been shown again...
4606                     }
4607                     
4608                     this.el.dom.style.display='';
4609
4610                     Roo.get(document.body).removeClass('modal-open');
4611                     this.el.removeClass('hideing');
4612                 }).defer(150,this);
4613                 
4614             }else{
4615                 this.el.removeClass('show');
4616                 this.el.dom.style.display='';
4617                 Roo.get(document.body).removeClass('modal-open');
4618
4619             }
4620             this.fireEvent('hide', this);
4621         }
4622     },
4623     isVisible : function()
4624     {
4625         
4626         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4627         
4628     },
4629
4630     addButton : function(str, cb)
4631     {
4632
4633
4634         var b = Roo.apply({}, { html : str } );
4635         b.xns = b.xns || Roo.bootstrap;
4636         b.xtype = b.xtype || 'Button';
4637         if (typeof(b.listeners) == 'undefined') {
4638             b.listeners = { click : cb.createDelegate(this)  };
4639         }
4640
4641         var btn = Roo.factory(b);
4642
4643         btn.render(this.getButtonContainer());
4644
4645         return btn;
4646
4647     },
4648
4649     setDefaultButton : function(btn)
4650     {
4651         //this.el.select('.modal-footer').()
4652     },
4653
4654     resizeTo: function(w,h)
4655     {
4656         this.dialogEl.setWidth(w);
4657         
4658         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4659
4660         this.bodyEl.setHeight(h - diff);
4661         
4662         this.fireEvent('resize', this);
4663     },
4664     
4665     setContentSize  : function(w, h)
4666     {
4667
4668     },
4669     onButtonClick: function(btn,e)
4670     {
4671         //Roo.log([a,b,c]);
4672         this.fireEvent('btnclick', btn.name, e);
4673     },
4674      /**
4675      * Set the title of the Dialog
4676      * @param {String} str new Title
4677      */
4678     setTitle: function(str) {
4679         this.titleEl.dom.innerHTML = str;
4680         this.title = str;
4681     },
4682     /**
4683      * Set the body of the Dialog
4684      * @param {String} str new Title
4685      */
4686     setBody: function(str) {
4687         this.bodyEl.dom.innerHTML = str;
4688     },
4689     /**
4690      * Set the body of the Dialog using the template
4691      * @param {Obj} data - apply this data to the template and replace the body contents.
4692      */
4693     applyBody: function(obj)
4694     {
4695         if (!this.tmpl) {
4696             Roo.log("Error - using apply Body without a template");
4697             //code
4698         }
4699         this.tmpl.overwrite(this.bodyEl, obj);
4700     },
4701     
4702     getChildHeight : function(child_nodes)
4703     {
4704         if(
4705             !child_nodes ||
4706             child_nodes.length == 0
4707         ) {
4708             return 0;
4709         }
4710         
4711         var child_height = 0;
4712         
4713         for(var i = 0; i < child_nodes.length; i++) {
4714             
4715             /*
4716             * for modal with tabs...
4717             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4718                 
4719                 var layout_childs = child_nodes[i].childNodes;
4720                 
4721                 for(var j = 0; j < layout_childs.length; j++) {
4722                     
4723                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4724                         
4725                         var layout_body_childs = layout_childs[j].childNodes;
4726                         
4727                         for(var k = 0; k < layout_body_childs.length; k++) {
4728                             
4729                             if(layout_body_childs[k].classList.contains('navbar')) {
4730                                 child_height += layout_body_childs[k].offsetHeight;
4731                                 continue;
4732                             }
4733                             
4734                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4735                                 
4736                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4737                                 
4738                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4739                                     
4740                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4741                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4742                                         continue;
4743                                     }
4744                                     
4745                                 }
4746                                 
4747                             }
4748                             
4749                         }
4750                     }
4751                 }
4752                 continue;
4753             }
4754             */
4755             
4756             child_height += child_nodes[i].offsetHeight;
4757             // Roo.log(child_nodes[i].offsetHeight);
4758         }
4759         
4760         return child_height;
4761     },
4762     toggleHeaderInput : function(is_edit)
4763     {
4764         if (!this.editableTitle) {
4765             return; // not editable.
4766         }
4767         if (is_edit && this.is_header_editing) {
4768             return; // already editing..
4769         }
4770         if (is_edit) {
4771     
4772             this.headerEditEl.dom.value = this.title;
4773             this.headerEditEl.removeClass('d-none');
4774             this.headerEditEl.dom.focus();
4775             this.titleEl.addClass('d-none');
4776             
4777             this.is_header_editing = true;
4778             return
4779         }
4780         // flip back to not editing.
4781         this.title = this.headerEditEl.dom.value;
4782         this.headerEditEl.addClass('d-none');
4783         this.titleEl.removeClass('d-none');
4784         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4785         this.is_header_editing = false;
4786         this.fireEvent('titlechanged', this, this.title);
4787     
4788             
4789         
4790     }
4791
4792 });
4793
4794
4795 Roo.apply(Roo.bootstrap.Modal,  {
4796     /**
4797          * Button config that displays a single OK button
4798          * @type Object
4799          */
4800         OK :  [{
4801             name : 'ok',
4802             weight : 'primary',
4803             html : 'OK'
4804         }],
4805         /**
4806          * Button config that displays Yes and No buttons
4807          * @type Object
4808          */
4809         YESNO : [
4810             {
4811                 name  : 'no',
4812                 html : 'No'
4813             },
4814             {
4815                 name  :'yes',
4816                 weight : 'primary',
4817                 html : 'Yes'
4818             }
4819         ],
4820
4821         /**
4822          * Button config that displays OK and Cancel buttons
4823          * @type Object
4824          */
4825         OKCANCEL : [
4826             {
4827                name : 'cancel',
4828                 html : 'Cancel'
4829             },
4830             {
4831                 name : 'ok',
4832                 weight : 'primary',
4833                 html : 'OK'
4834             }
4835         ],
4836         /**
4837          * Button config that displays Yes, No and Cancel buttons
4838          * @type Object
4839          */
4840         YESNOCANCEL : [
4841             {
4842                 name : 'yes',
4843                 weight : 'primary',
4844                 html : 'Yes'
4845             },
4846             {
4847                 name : 'no',
4848                 html : 'No'
4849             },
4850             {
4851                 name : 'cancel',
4852                 html : 'Cancel'
4853             }
4854         ],
4855         
4856         zIndex : 10001
4857 });
4858
4859 /*
4860  * - LGPL
4861  *
4862  * messagebox - can be used as a replace
4863  * 
4864  */
4865 /**
4866  * @class Roo.MessageBox
4867  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4868  * Example usage:
4869  *<pre><code>
4870 // Basic alert:
4871 Roo.Msg.alert('Status', 'Changes saved successfully.');
4872
4873 // Prompt for user data:
4874 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4875     if (btn == 'ok'){
4876         // process text value...
4877     }
4878 });
4879
4880 // Show a dialog using config options:
4881 Roo.Msg.show({
4882    title:'Save Changes?',
4883    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4884    buttons: Roo.Msg.YESNOCANCEL,
4885    fn: processResult,
4886    animEl: 'elId'
4887 });
4888 </code></pre>
4889  * @singleton
4890  */
4891 Roo.bootstrap.MessageBox = function(){
4892     var dlg, opt, mask, waitTimer;
4893     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4894     var buttons, activeTextEl, bwidth;
4895
4896     
4897     // private
4898     var handleButton = function(button){
4899         dlg.hide();
4900         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4901     };
4902
4903     // private
4904     var handleHide = function(){
4905         if(opt && opt.cls){
4906             dlg.el.removeClass(opt.cls);
4907         }
4908         //if(waitTimer){
4909         //    Roo.TaskMgr.stop(waitTimer);
4910         //    waitTimer = null;
4911         //}
4912     };
4913
4914     // private
4915     var updateButtons = function(b){
4916         var width = 0;
4917         if(!b){
4918             buttons["ok"].hide();
4919             buttons["cancel"].hide();
4920             buttons["yes"].hide();
4921             buttons["no"].hide();
4922             dlg.footerEl.hide();
4923             
4924             return width;
4925         }
4926         dlg.footerEl.show();
4927         for(var k in buttons){
4928             if(typeof buttons[k] != "function"){
4929                 if(b[k]){
4930                     buttons[k].show();
4931                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4932                     width += buttons[k].el.getWidth()+15;
4933                 }else{
4934                     buttons[k].hide();
4935                 }
4936             }
4937         }
4938         return width;
4939     };
4940
4941     // private
4942     var handleEsc = function(d, k, e){
4943         if(opt && opt.closable !== false){
4944             dlg.hide();
4945         }
4946         if(e){
4947             e.stopEvent();
4948         }
4949     };
4950
4951     return {
4952         /**
4953          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4954          * @return {Roo.BasicDialog} The BasicDialog element
4955          */
4956         getDialog : function(){
4957            if(!dlg){
4958                 dlg = new Roo.bootstrap.Modal( {
4959                     //draggable: true,
4960                     //resizable:false,
4961                     //constraintoviewport:false,
4962                     //fixedcenter:true,
4963                     //collapsible : false,
4964                     //shim:true,
4965                     //modal: true,
4966                 //    width: 'auto',
4967                   //  height:100,
4968                     //buttonAlign:"center",
4969                     closeClick : function(){
4970                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4971                             handleButton("no");
4972                         }else{
4973                             handleButton("cancel");
4974                         }
4975                     }
4976                 });
4977                 dlg.render();
4978                 dlg.on("hide", handleHide);
4979                 mask = dlg.mask;
4980                 //dlg.addKeyListener(27, handleEsc);
4981                 buttons = {};
4982                 this.buttons = buttons;
4983                 var bt = this.buttonText;
4984                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4985                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4986                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4987                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4988                 //Roo.log(buttons);
4989                 bodyEl = dlg.bodyEl.createChild({
4990
4991                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4992                         '<textarea class="roo-mb-textarea"></textarea>' +
4993                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4994                 });
4995                 msgEl = bodyEl.dom.firstChild;
4996                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4997                 textboxEl.enableDisplayMode();
4998                 textboxEl.addKeyListener([10,13], function(){
4999                     if(dlg.isVisible() && opt && opt.buttons){
5000                         if(opt.buttons.ok){
5001                             handleButton("ok");
5002                         }else if(opt.buttons.yes){
5003                             handleButton("yes");
5004                         }
5005                     }
5006                 });
5007                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5008                 textareaEl.enableDisplayMode();
5009                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5010                 progressEl.enableDisplayMode();
5011                 
5012                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5013                 var pf = progressEl.dom.firstChild;
5014                 if (pf) {
5015                     pp = Roo.get(pf.firstChild);
5016                     pp.setHeight(pf.offsetHeight);
5017                 }
5018                 
5019             }
5020             return dlg;
5021         },
5022
5023         /**
5024          * Updates the message box body text
5025          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5026          * the XHTML-compliant non-breaking space character '&amp;#160;')
5027          * @return {Roo.MessageBox} This message box
5028          */
5029         updateText : function(text)
5030         {
5031             if(!dlg.isVisible() && !opt.width){
5032                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5033                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5034             }
5035             msgEl.innerHTML = text || '&#160;';
5036       
5037             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5038             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5039             var w = Math.max(
5040                     Math.min(opt.width || cw , this.maxWidth), 
5041                     Math.max(opt.minWidth || this.minWidth, bwidth)
5042             );
5043             if(opt.prompt){
5044                 activeTextEl.setWidth(w);
5045             }
5046             if(dlg.isVisible()){
5047                 dlg.fixedcenter = false;
5048             }
5049             // to big, make it scroll. = But as usual stupid IE does not support
5050             // !important..
5051             
5052             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5053                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5054                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5055             } else {
5056                 bodyEl.dom.style.height = '';
5057                 bodyEl.dom.style.overflowY = '';
5058             }
5059             if (cw > w) {
5060                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5061             } else {
5062                 bodyEl.dom.style.overflowX = '';
5063             }
5064             
5065             dlg.setContentSize(w, bodyEl.getHeight());
5066             if(dlg.isVisible()){
5067                 dlg.fixedcenter = true;
5068             }
5069             return this;
5070         },
5071
5072         /**
5073          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5074          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5075          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5076          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5077          * @return {Roo.MessageBox} This message box
5078          */
5079         updateProgress : function(value, text){
5080             if(text){
5081                 this.updateText(text);
5082             }
5083             
5084             if (pp) { // weird bug on my firefox - for some reason this is not defined
5085                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5086                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5087             }
5088             return this;
5089         },        
5090
5091         /**
5092          * Returns true if the message box is currently displayed
5093          * @return {Boolean} True if the message box is visible, else false
5094          */
5095         isVisible : function(){
5096             return dlg && dlg.isVisible();  
5097         },
5098
5099         /**
5100          * Hides the message box if it is displayed
5101          */
5102         hide : function(){
5103             if(this.isVisible()){
5104                 dlg.hide();
5105             }  
5106         },
5107
5108         /**
5109          * Displays a new message box, or reinitializes an existing message box, based on the config options
5110          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5111          * The following config object properties are supported:
5112          * <pre>
5113 Property    Type             Description
5114 ----------  ---------------  ------------------------------------------------------------------------------------
5115 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5116                                    closes (defaults to undefined)
5117 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5118                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5119 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5120                                    progress and wait dialogs will ignore this property and always hide the
5121                                    close button as they can only be closed programmatically.
5122 cls               String           A custom CSS class to apply to the message box element
5123 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5124                                    displayed (defaults to 75)
5125 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5126                                    function will be btn (the name of the button that was clicked, if applicable,
5127                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5128                                    Progress and wait dialogs will ignore this option since they do not respond to
5129                                    user actions and can only be closed programmatically, so any required function
5130                                    should be called by the same code after it closes the dialog.
5131 icon              String           A CSS class that provides a background image to be used as an icon for
5132                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5133 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5134 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5135 modal             Boolean          False to allow user interaction with the page while the message box is
5136                                    displayed (defaults to true)
5137 msg               String           A string that will replace the existing message box body text (defaults
5138                                    to the XHTML-compliant non-breaking space character '&#160;')
5139 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5140 progress          Boolean          True to display a progress bar (defaults to false)
5141 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5142 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5143 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5144 title             String           The title text
5145 value             String           The string value to set into the active textbox element if displayed
5146 wait              Boolean          True to display a progress bar (defaults to false)
5147 width             Number           The width of the dialog in pixels
5148 </pre>
5149          *
5150          * Example usage:
5151          * <pre><code>
5152 Roo.Msg.show({
5153    title: 'Address',
5154    msg: 'Please enter your address:',
5155    width: 300,
5156    buttons: Roo.MessageBox.OKCANCEL,
5157    multiline: true,
5158    fn: saveAddress,
5159    animEl: 'addAddressBtn'
5160 });
5161 </code></pre>
5162          * @param {Object} config Configuration options
5163          * @return {Roo.MessageBox} This message box
5164          */
5165         show : function(options)
5166         {
5167             
5168             // this causes nightmares if you show one dialog after another
5169             // especially on callbacks..
5170              
5171             if(this.isVisible()){
5172                 
5173                 this.hide();
5174                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5175                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5176                 Roo.log("New Dialog Message:" +  options.msg )
5177                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5178                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5179                 
5180             }
5181             var d = this.getDialog();
5182             opt = options;
5183             d.setTitle(opt.title || "&#160;");
5184             d.closeEl.setDisplayed(opt.closable !== false);
5185             activeTextEl = textboxEl;
5186             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5187             if(opt.prompt){
5188                 if(opt.multiline){
5189                     textboxEl.hide();
5190                     textareaEl.show();
5191                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5192                         opt.multiline : this.defaultTextHeight);
5193                     activeTextEl = textareaEl;
5194                 }else{
5195                     textboxEl.show();
5196                     textareaEl.hide();
5197                 }
5198             }else{
5199                 textboxEl.hide();
5200                 textareaEl.hide();
5201             }
5202             progressEl.setDisplayed(opt.progress === true);
5203             if (opt.progress) {
5204                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5205             }
5206             this.updateProgress(0);
5207             activeTextEl.dom.value = opt.value || "";
5208             if(opt.prompt){
5209                 dlg.setDefaultButton(activeTextEl);
5210             }else{
5211                 var bs = opt.buttons;
5212                 var db = null;
5213                 if(bs && bs.ok){
5214                     db = buttons["ok"];
5215                 }else if(bs && bs.yes){
5216                     db = buttons["yes"];
5217                 }
5218                 dlg.setDefaultButton(db);
5219             }
5220             bwidth = updateButtons(opt.buttons);
5221             this.updateText(opt.msg);
5222             if(opt.cls){
5223                 d.el.addClass(opt.cls);
5224             }
5225             d.proxyDrag = opt.proxyDrag === true;
5226             d.modal = opt.modal !== false;
5227             d.mask = opt.modal !== false ? mask : false;
5228             if(!d.isVisible()){
5229                 // force it to the end of the z-index stack so it gets a cursor in FF
5230                 document.body.appendChild(dlg.el.dom);
5231                 d.animateTarget = null;
5232                 d.show(options.animEl);
5233             }
5234             return this;
5235         },
5236
5237         /**
5238          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5239          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5240          * and closing the message box when the process is complete.
5241          * @param {String} title The title bar text
5242          * @param {String} msg The message box body text
5243          * @return {Roo.MessageBox} This message box
5244          */
5245         progress : function(title, msg){
5246             this.show({
5247                 title : title,
5248                 msg : msg,
5249                 buttons: false,
5250                 progress:true,
5251                 closable:false,
5252                 minWidth: this.minProgressWidth,
5253                 modal : true
5254             });
5255             return this;
5256         },
5257
5258         /**
5259          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5260          * If a callback function is passed it will be called after the user clicks the button, and the
5261          * id of the button that was clicked will be passed as the only parameter to the callback
5262          * (could also be the top-right close button).
5263          * @param {String} title The title bar text
5264          * @param {String} msg The message box body text
5265          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5266          * @param {Object} scope (optional) The scope of the callback function
5267          * @return {Roo.MessageBox} This message box
5268          */
5269         alert : function(title, msg, fn, scope)
5270         {
5271             this.show({
5272                 title : title,
5273                 msg : msg,
5274                 buttons: this.OK,
5275                 fn: fn,
5276                 closable : false,
5277                 scope : scope,
5278                 modal : true
5279             });
5280             return this;
5281         },
5282
5283         /**
5284          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5285          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5286          * You are responsible for closing the message box when the process is complete.
5287          * @param {String} msg The message box body text
5288          * @param {String} title (optional) The title bar text
5289          * @return {Roo.MessageBox} This message box
5290          */
5291         wait : function(msg, title){
5292             this.show({
5293                 title : title,
5294                 msg : msg,
5295                 buttons: false,
5296                 closable:false,
5297                 progress:true,
5298                 modal:true,
5299                 width:300,
5300                 wait:true
5301             });
5302             waitTimer = Roo.TaskMgr.start({
5303                 run: function(i){
5304                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5305                 },
5306                 interval: 1000
5307             });
5308             return this;
5309         },
5310
5311         /**
5312          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5313          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5314          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5315          * @param {String} title The title bar text
5316          * @param {String} msg The message box body text
5317          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318          * @param {Object} scope (optional) The scope of the callback function
5319          * @return {Roo.MessageBox} This message box
5320          */
5321         confirm : function(title, msg, fn, scope){
5322             this.show({
5323                 title : title,
5324                 msg : msg,
5325                 buttons: this.YESNO,
5326                 fn: fn,
5327                 scope : scope,
5328                 modal : true
5329             });
5330             return this;
5331         },
5332
5333         /**
5334          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5335          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5336          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5337          * (could also be the top-right close button) and the text that was entered will be passed as the two
5338          * parameters to the callback.
5339          * @param {String} title The title bar text
5340          * @param {String} msg The message box body text
5341          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5342          * @param {Object} scope (optional) The scope of the callback function
5343          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5344          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5345          * @return {Roo.MessageBox} This message box
5346          */
5347         prompt : function(title, msg, fn, scope, multiline){
5348             this.show({
5349                 title : title,
5350                 msg : msg,
5351                 buttons: this.OKCANCEL,
5352                 fn: fn,
5353                 minWidth:250,
5354                 scope : scope,
5355                 prompt:true,
5356                 multiline: multiline,
5357                 modal : true
5358             });
5359             return this;
5360         },
5361
5362         /**
5363          * Button config that displays a single OK button
5364          * @type Object
5365          */
5366         OK : {ok:true},
5367         /**
5368          * Button config that displays Yes and No buttons
5369          * @type Object
5370          */
5371         YESNO : {yes:true, no:true},
5372         /**
5373          * Button config that displays OK and Cancel buttons
5374          * @type Object
5375          */
5376         OKCANCEL : {ok:true, cancel:true},
5377         /**
5378          * Button config that displays Yes, No and Cancel buttons
5379          * @type Object
5380          */
5381         YESNOCANCEL : {yes:true, no:true, cancel:true},
5382
5383         /**
5384          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5385          * @type Number
5386          */
5387         defaultTextHeight : 75,
5388         /**
5389          * The maximum width in pixels of the message box (defaults to 600)
5390          * @type Number
5391          */
5392         maxWidth : 600,
5393         /**
5394          * The minimum width in pixels of the message box (defaults to 100)
5395          * @type Number
5396          */
5397         minWidth : 100,
5398         /**
5399          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5400          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5401          * @type Number
5402          */
5403         minProgressWidth : 250,
5404         /**
5405          * An object containing the default button text strings that can be overriden for localized language support.
5406          * Supported properties are: ok, cancel, yes and no.
5407          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5408          * @type Object
5409          */
5410         buttonText : {
5411             ok : "OK",
5412             cancel : "Cancel",
5413             yes : "Yes",
5414             no : "No"
5415         }
5416     };
5417 }();
5418
5419 /**
5420  * Shorthand for {@link Roo.MessageBox}
5421  */
5422 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5423 Roo.Msg = Roo.Msg || Roo.MessageBox;
5424 /*
5425  * - LGPL
5426  *
5427  * navbar
5428  * 
5429  */
5430
5431 /**
5432  * @class Roo.bootstrap.Navbar
5433  * @extends Roo.bootstrap.Component
5434  * Bootstrap Navbar class
5435
5436  * @constructor
5437  * Create a new Navbar
5438  * @param {Object} config The config object
5439  */
5440
5441
5442 Roo.bootstrap.Navbar = function(config){
5443     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5444     this.addEvents({
5445         // raw events
5446         /**
5447          * @event beforetoggle
5448          * Fire before toggle the menu
5449          * @param {Roo.EventObject} e
5450          */
5451         "beforetoggle" : true
5452     });
5453 };
5454
5455 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5456     
5457     
5458    
5459     // private
5460     navItems : false,
5461     loadMask : false,
5462     
5463     
5464     getAutoCreate : function(){
5465         
5466         
5467         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5468         
5469     },
5470     
5471     initEvents :function ()
5472     {
5473         //Roo.log(this.el.select('.navbar-toggle',true));
5474         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5475         
5476         var mark = {
5477             tag: "div",
5478             cls:"x-dlg-mask"
5479         };
5480         
5481         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5482         
5483         var size = this.el.getSize();
5484         this.maskEl.setSize(size.width, size.height);
5485         this.maskEl.enableDisplayMode("block");
5486         this.maskEl.hide();
5487         
5488         if(this.loadMask){
5489             this.maskEl.show();
5490         }
5491     },
5492     
5493     
5494     getChildContainer : function()
5495     {
5496         if (this.el && this.el.select('.collapse').getCount()) {
5497             return this.el.select('.collapse',true).first();
5498         }
5499         
5500         return this.el;
5501     },
5502     
5503     mask : function()
5504     {
5505         this.maskEl.show();
5506     },
5507     
5508     unmask : function()
5509     {
5510         this.maskEl.hide();
5511     },
5512     onToggle : function()
5513     {
5514         
5515         if(this.fireEvent('beforetoggle', this) === false){
5516             return;
5517         }
5518         var ce = this.el.select('.navbar-collapse',true).first();
5519       
5520         if (!ce.hasClass('show')) {
5521            this.expand();
5522         } else {
5523             this.collapse();
5524         }
5525         
5526         
5527     
5528     },
5529     /**
5530      * Expand the navbar pulldown 
5531      */
5532     expand : function ()
5533     {
5534        
5535         var ce = this.el.select('.navbar-collapse',true).first();
5536         if (ce.hasClass('collapsing')) {
5537             return;
5538         }
5539         ce.dom.style.height = '';
5540                // show it...
5541         ce.addClass('in'); // old...
5542         ce.removeClass('collapse');
5543         ce.addClass('show');
5544         var h = ce.getHeight();
5545         Roo.log(h);
5546         ce.removeClass('show');
5547         // at this point we should be able to see it..
5548         ce.addClass('collapsing');
5549         
5550         ce.setHeight(0); // resize it ...
5551         ce.on('transitionend', function() {
5552             //Roo.log('done transition');
5553             ce.removeClass('collapsing');
5554             ce.addClass('show');
5555             ce.removeClass('collapse');
5556
5557             ce.dom.style.height = '';
5558         }, this, { single: true} );
5559         ce.setHeight(h);
5560         ce.dom.scrollTop = 0;
5561     },
5562     /**
5563      * Collapse the navbar pulldown 
5564      */
5565     collapse : function()
5566     {
5567          var ce = this.el.select('.navbar-collapse',true).first();
5568        
5569         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5570             // it's collapsed or collapsing..
5571             return;
5572         }
5573         ce.removeClass('in'); // old...
5574         ce.setHeight(ce.getHeight());
5575         ce.removeClass('show');
5576         ce.addClass('collapsing');
5577         
5578         ce.on('transitionend', function() {
5579             ce.dom.style.height = '';
5580             ce.removeClass('collapsing');
5581             ce.addClass('collapse');
5582         }, this, { single: true} );
5583         ce.setHeight(0);
5584     }
5585     
5586     
5587     
5588 });
5589
5590
5591
5592  
5593
5594  /*
5595  * - LGPL
5596  *
5597  * navbar
5598  * 
5599  */
5600
5601 /**
5602  * @class Roo.bootstrap.NavSimplebar
5603  * @extends Roo.bootstrap.Navbar
5604  * Bootstrap Sidebar class
5605  *
5606  * @cfg {Boolean} inverse is inverted color
5607  * 
5608  * @cfg {String} type (nav | pills | tabs)
5609  * @cfg {Boolean} arrangement stacked | justified
5610  * @cfg {String} align (left | right) alignment
5611  * 
5612  * @cfg {Boolean} main (true|false) main nav bar? default false
5613  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5614  * 
5615  * @cfg {String} tag (header|footer|nav|div) default is nav 
5616
5617  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5618  * 
5619  * 
5620  * @constructor
5621  * Create a new Sidebar
5622  * @param {Object} config The config object
5623  */
5624
5625
5626 Roo.bootstrap.NavSimplebar = function(config){
5627     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5628 };
5629
5630 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5631     
5632     inverse: false,
5633     
5634     type: false,
5635     arrangement: '',
5636     align : false,
5637     
5638     weight : 'light',
5639     
5640     main : false,
5641     
5642     
5643     tag : false,
5644     
5645     
5646     getAutoCreate : function(){
5647         
5648         
5649         var cfg = {
5650             tag : this.tag || 'div',
5651             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5652         };
5653         if (['light','white'].indexOf(this.weight) > -1) {
5654             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5655         }
5656         cfg.cls += ' bg-' + this.weight;
5657         
5658         if (this.inverse) {
5659             cfg.cls += ' navbar-inverse';
5660             
5661         }
5662         
5663         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5664         
5665         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5666             return cfg;
5667         }
5668         
5669         
5670     
5671         
5672         cfg.cn = [
5673             {
5674                 cls: 'nav nav-' + this.xtype,
5675                 tag : 'ul'
5676             }
5677         ];
5678         
5679          
5680         this.type = this.type || 'nav';
5681         if (['tabs','pills'].indexOf(this.type) != -1) {
5682             cfg.cn[0].cls += ' nav-' + this.type
5683         
5684         
5685         } else {
5686             if (this.type!=='nav') {
5687                 Roo.log('nav type must be nav/tabs/pills')
5688             }
5689             cfg.cn[0].cls += ' navbar-nav'
5690         }
5691         
5692         
5693         
5694         
5695         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5696             cfg.cn[0].cls += ' nav-' + this.arrangement;
5697         }
5698         
5699         
5700         if (this.align === 'right') {
5701             cfg.cn[0].cls += ' navbar-right';
5702         }
5703         
5704         
5705         
5706         
5707         return cfg;
5708     
5709         
5710     }
5711     
5712     
5713     
5714 });
5715
5716
5717
5718  
5719
5720  
5721        /*
5722  * - LGPL
5723  *
5724  * navbar
5725  * navbar-fixed-top
5726  * navbar-expand-md  fixed-top 
5727  */
5728
5729 /**
5730  * @class Roo.bootstrap.NavHeaderbar
5731  * @extends Roo.bootstrap.NavSimplebar
5732  * Bootstrap Sidebar class
5733  *
5734  * @cfg {String} brand what is brand
5735  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5736  * @cfg {String} brand_href href of the brand
5737  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5738  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5739  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5740  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5741  * 
5742  * @constructor
5743  * Create a new Sidebar
5744  * @param {Object} config The config object
5745  */
5746
5747
5748 Roo.bootstrap.NavHeaderbar = function(config){
5749     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5750       
5751 };
5752
5753 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5754     
5755     position: '',
5756     brand: '',
5757     brand_href: false,
5758     srButton : true,
5759     autohide : false,
5760     desktopCenter : false,
5761    
5762     
5763     getAutoCreate : function(){
5764         
5765         var   cfg = {
5766             tag: this.nav || 'nav',
5767             cls: 'navbar navbar-expand-md',
5768             role: 'navigation',
5769             cn: []
5770         };
5771         
5772         var cn = cfg.cn;
5773         if (this.desktopCenter) {
5774             cn.push({cls : 'container', cn : []});
5775             cn = cn[0].cn;
5776         }
5777         
5778         if(this.srButton){
5779             var btn = {
5780                 tag: 'button',
5781                 type: 'button',
5782                 cls: 'navbar-toggle navbar-toggler',
5783                 'data-toggle': 'collapse',
5784                 cn: [
5785                     {
5786                         tag: 'span',
5787                         cls: 'sr-only',
5788                         html: 'Toggle navigation'
5789                     },
5790                     {
5791                         tag: 'span',
5792                         cls: 'icon-bar navbar-toggler-icon'
5793                     },
5794                     {
5795                         tag: 'span',
5796                         cls: 'icon-bar'
5797                     },
5798                     {
5799                         tag: 'span',
5800                         cls: 'icon-bar'
5801                     }
5802                 ]
5803             };
5804             
5805             cn.push( Roo.bootstrap.version == 4 ? btn : {
5806                 tag: 'div',
5807                 cls: 'navbar-header',
5808                 cn: [
5809                     btn
5810                 ]
5811             });
5812         }
5813         
5814         cn.push({
5815             tag: 'div',
5816             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5817             cn : []
5818         });
5819         
5820         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5821         
5822         if (['light','white'].indexOf(this.weight) > -1) {
5823             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5824         }
5825         cfg.cls += ' bg-' + this.weight;
5826         
5827         
5828         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5829             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5830             
5831             // tag can override this..
5832             
5833             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5834         }
5835         
5836         if (this.brand !== '') {
5837             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5838             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5839                 tag: 'a',
5840                 href: this.brand_href ? this.brand_href : '#',
5841                 cls: 'navbar-brand',
5842                 cn: [
5843                 this.brand
5844                 ]
5845             });
5846         }
5847         
5848         if(this.main){
5849             cfg.cls += ' main-nav';
5850         }
5851         
5852         
5853         return cfg;
5854
5855         
5856     },
5857     getHeaderChildContainer : function()
5858     {
5859         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5860             return this.el.select('.navbar-header',true).first();
5861         }
5862         
5863         return this.getChildContainer();
5864     },
5865     
5866     getChildContainer : function()
5867     {
5868          
5869         return this.el.select('.roo-navbar-collapse',true).first();
5870          
5871         
5872     },
5873     
5874     initEvents : function()
5875     {
5876         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5877         
5878         if (this.autohide) {
5879             
5880             var prevScroll = 0;
5881             var ft = this.el;
5882             
5883             Roo.get(document).on('scroll',function(e) {
5884                 var ns = Roo.get(document).getScroll().top;
5885                 var os = prevScroll;
5886                 prevScroll = ns;
5887                 
5888                 if(ns > os){
5889                     ft.removeClass('slideDown');
5890                     ft.addClass('slideUp');
5891                     return;
5892                 }
5893                 ft.removeClass('slideUp');
5894                 ft.addClass('slideDown');
5895                  
5896               
5897           },this);
5898         }
5899     }    
5900     
5901 });
5902
5903
5904
5905  
5906
5907  /*
5908  * - LGPL
5909  *
5910  * navbar
5911  * 
5912  */
5913
5914 /**
5915  * @class Roo.bootstrap.NavSidebar
5916  * @extends Roo.bootstrap.Navbar
5917  * Bootstrap Sidebar class
5918  * 
5919  * @constructor
5920  * Create a new Sidebar
5921  * @param {Object} config The config object
5922  */
5923
5924
5925 Roo.bootstrap.NavSidebar = function(config){
5926     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5927 };
5928
5929 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5930     
5931     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5932     
5933     getAutoCreate : function(){
5934         
5935         
5936         return  {
5937             tag: 'div',
5938             cls: 'sidebar sidebar-nav'
5939         };
5940     
5941         
5942     }
5943     
5944     
5945     
5946 });
5947
5948
5949
5950  
5951
5952  /*
5953  * - LGPL
5954  *
5955  * nav group
5956  * 
5957  */
5958
5959 /**
5960  * @class Roo.bootstrap.NavGroup
5961  * @extends Roo.bootstrap.Component
5962  * Bootstrap NavGroup class
5963  * @cfg {String} align (left|right)
5964  * @cfg {Boolean} inverse
5965  * @cfg {String} type (nav|pills|tab) default nav
5966  * @cfg {String} navId - reference Id for navbar.
5967  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5968  * 
5969  * @constructor
5970  * Create a new nav group
5971  * @param {Object} config The config object
5972  */
5973
5974 Roo.bootstrap.NavGroup = function(config){
5975     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5976     this.navItems = [];
5977    
5978     Roo.bootstrap.NavGroup.register(this);
5979      this.addEvents({
5980         /**
5981              * @event changed
5982              * Fires when the active item changes
5983              * @param {Roo.bootstrap.NavGroup} this
5984              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5985              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5986          */
5987         'changed': true
5988      });
5989     
5990 };
5991
5992 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5993     
5994     align: '',
5995     inverse: false,
5996     form: false,
5997     type: 'nav',
5998     navId : '',
5999     // private
6000     pilltype : true,
6001     
6002     navItems : false, 
6003     
6004     getAutoCreate : function()
6005     {
6006         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6007         
6008         cfg = {
6009             tag : 'ul',
6010             cls: 'nav' 
6011         };
6012         if (Roo.bootstrap.version == 4) {
6013             if (['tabs','pills'].indexOf(this.type) != -1) {
6014                 cfg.cls += ' nav-' + this.type; 
6015             } else {
6016                 // trying to remove so header bar can right align top?
6017                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6018                     // do not use on header bar... 
6019                     cfg.cls += ' navbar-nav';
6020                 }
6021             }
6022             
6023         } else {
6024             if (['tabs','pills'].indexOf(this.type) != -1) {
6025                 cfg.cls += ' nav-' + this.type
6026             } else {
6027                 if (this.type !== 'nav') {
6028                     Roo.log('nav type must be nav/tabs/pills')
6029                 }
6030                 cfg.cls += ' navbar-nav'
6031             }
6032         }
6033         
6034         if (this.parent() && this.parent().sidebar) {
6035             cfg = {
6036                 tag: 'ul',
6037                 cls: 'dashboard-menu sidebar-menu'
6038             };
6039             
6040             return cfg;
6041         }
6042         
6043         if (this.form === true) {
6044             cfg = {
6045                 tag: 'form',
6046                 cls: 'navbar-form form-inline'
6047             };
6048             //nav navbar-right ml-md-auto
6049             if (this.align === 'right') {
6050                 cfg.cls += ' navbar-right ml-md-auto';
6051             } else {
6052                 cfg.cls += ' navbar-left';
6053             }
6054         }
6055         
6056         if (this.align === 'right') {
6057             cfg.cls += ' navbar-right ml-md-auto';
6058         } else {
6059             cfg.cls += ' mr-auto';
6060         }
6061         
6062         if (this.inverse) {
6063             cfg.cls += ' navbar-inverse';
6064             
6065         }
6066         
6067         
6068         return cfg;
6069     },
6070     /**
6071     * sets the active Navigation item
6072     * @param {Roo.bootstrap.NavItem} the new current navitem
6073     */
6074     setActiveItem : function(item)
6075     {
6076         var prev = false;
6077         Roo.each(this.navItems, function(v){
6078             if (v == item) {
6079                 return ;
6080             }
6081             if (v.isActive()) {
6082                 v.setActive(false, true);
6083                 prev = v;
6084                 
6085             }
6086             
6087         });
6088
6089         item.setActive(true, true);
6090         this.fireEvent('changed', this, item, prev);
6091         
6092         
6093     },
6094     /**
6095     * gets the active Navigation item
6096     * @return {Roo.bootstrap.NavItem} the current navitem
6097     */
6098     getActive : function()
6099     {
6100         
6101         var prev = false;
6102         Roo.each(this.navItems, function(v){
6103             
6104             if (v.isActive()) {
6105                 prev = v;
6106                 
6107             }
6108             
6109         });
6110         return prev;
6111     },
6112     
6113     indexOfNav : function()
6114     {
6115         
6116         var prev = false;
6117         Roo.each(this.navItems, function(v,i){
6118             
6119             if (v.isActive()) {
6120                 prev = i;
6121                 
6122             }
6123             
6124         });
6125         return prev;
6126     },
6127     /**
6128     * adds a Navigation item
6129     * @param {Roo.bootstrap.NavItem} the navitem to add
6130     */
6131     addItem : function(cfg)
6132     {
6133         if (this.form && Roo.bootstrap.version == 4) {
6134             cfg.tag = 'div';
6135         }
6136         var cn = new Roo.bootstrap.NavItem(cfg);
6137         this.register(cn);
6138         cn.parentId = this.id;
6139         cn.onRender(this.el, null);
6140         return cn;
6141     },
6142     /**
6143     * register a Navigation item
6144     * @param {Roo.bootstrap.NavItem} the navitem to add
6145     */
6146     register : function(item)
6147     {
6148         this.navItems.push( item);
6149         item.navId = this.navId;
6150     
6151     },
6152     
6153     /**
6154     * clear all the Navigation item
6155     */
6156    
6157     clearAll : function()
6158     {
6159         this.navItems = [];
6160         this.el.dom.innerHTML = '';
6161     },
6162     
6163     getNavItem: function(tabId)
6164     {
6165         var ret = false;
6166         Roo.each(this.navItems, function(e) {
6167             if (e.tabId == tabId) {
6168                ret =  e;
6169                return false;
6170             }
6171             return true;
6172             
6173         });
6174         return ret;
6175     },
6176     
6177     setActiveNext : function()
6178     {
6179         var i = this.indexOfNav(this.getActive());
6180         if (i > this.navItems.length) {
6181             return;
6182         }
6183         this.setActiveItem(this.navItems[i+1]);
6184     },
6185     setActivePrev : function()
6186     {
6187         var i = this.indexOfNav(this.getActive());
6188         if (i  < 1) {
6189             return;
6190         }
6191         this.setActiveItem(this.navItems[i-1]);
6192     },
6193     clearWasActive : function(except) {
6194         Roo.each(this.navItems, function(e) {
6195             if (e.tabId != except.tabId && e.was_active) {
6196                e.was_active = false;
6197                return false;
6198             }
6199             return true;
6200             
6201         });
6202     },
6203     getWasActive : function ()
6204     {
6205         var r = false;
6206         Roo.each(this.navItems, function(e) {
6207             if (e.was_active) {
6208                r = e;
6209                return false;
6210             }
6211             return true;
6212             
6213         });
6214         return r;
6215     }
6216     
6217     
6218 });
6219
6220  
6221 Roo.apply(Roo.bootstrap.NavGroup, {
6222     
6223     groups: {},
6224      /**
6225     * register a Navigation Group
6226     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6227     */
6228     register : function(navgrp)
6229     {
6230         this.groups[navgrp.navId] = navgrp;
6231         
6232     },
6233     /**
6234     * fetch a Navigation Group based on the navigation ID
6235     * @param {string} the navgroup to add
6236     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6237     */
6238     get: function(navId) {
6239         if (typeof(this.groups[navId]) == 'undefined') {
6240             return false;
6241             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6242         }
6243         return this.groups[navId] ;
6244     }
6245     
6246     
6247     
6248 });
6249
6250  /*
6251  * - LGPL
6252  *
6253  * row
6254  * 
6255  */
6256
6257 /**
6258  * @class Roo.bootstrap.NavItem
6259  * @extends Roo.bootstrap.Component
6260  * Bootstrap Navbar.NavItem class
6261  * @cfg {String} href  link to
6262  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6263  * @cfg {Boolean} button_outline show and outlined button
6264  * @cfg {String} html content of button
6265  * @cfg {String} badge text inside badge
6266  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6267  * @cfg {String} glyphicon DEPRICATED - use fa
6268  * @cfg {String} icon DEPRICATED - use fa
6269  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6270  * @cfg {Boolean} active Is item active
6271  * @cfg {Boolean} disabled Is item disabled
6272  * @cfg {String} linkcls  Link Class
6273  * @cfg {Boolean} preventDefault (true | false) default false
6274  * @cfg {String} tabId the tab that this item activates.
6275  * @cfg {String} tagtype (a|span) render as a href or span?
6276  * @cfg {Boolean} animateRef (true|false) link to element default false  
6277   
6278  * @constructor
6279  * Create a new Navbar Item
6280  * @param {Object} config The config object
6281  */
6282 Roo.bootstrap.NavItem = function(config){
6283     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6284     this.addEvents({
6285         // raw events
6286         /**
6287          * @event click
6288          * The raw click event for the entire grid.
6289          * @param {Roo.EventObject} e
6290          */
6291         "click" : true,
6292          /**
6293             * @event changed
6294             * Fires when the active item active state changes
6295             * @param {Roo.bootstrap.NavItem} this
6296             * @param {boolean} state the new state
6297              
6298          */
6299         'changed': true,
6300         /**
6301             * @event scrollto
6302             * Fires when scroll to element
6303             * @param {Roo.bootstrap.NavItem} this
6304             * @param {Object} options
6305             * @param {Roo.EventObject} e
6306              
6307          */
6308         'scrollto': true
6309     });
6310    
6311 };
6312
6313 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6314     
6315     href: false,
6316     html: '',
6317     badge: '',
6318     icon: false,
6319     fa : false,
6320     glyphicon: false,
6321     active: false,
6322     preventDefault : false,
6323     tabId : false,
6324     tagtype : 'a',
6325     tag: 'li',
6326     disabled : false,
6327     animateRef : false,
6328     was_active : false,
6329     button_weight : '',
6330     button_outline : false,
6331     linkcls : '',
6332     navLink: false,
6333     
6334     getAutoCreate : function(){
6335          
6336         var cfg = {
6337             tag: this.tag,
6338             cls: 'nav-item'
6339         };
6340         
6341         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6342         
6343         if (this.active) {
6344             cfg.cls +=  ' active' ;
6345         }
6346         if (this.disabled) {
6347             cfg.cls += ' disabled';
6348         }
6349         
6350         // BS4 only?
6351         if (this.button_weight.length) {
6352             cfg.tag = this.href ? 'a' : 'button';
6353             cfg.html = this.html || '';
6354             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6355             if (this.href) {
6356                 cfg.href = this.href;
6357             }
6358             if (this.fa) {
6359                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6360             } else {
6361                 cfg.cls += " nav-html";
6362             }
6363             
6364             // menu .. should add dropdown-menu class - so no need for carat..
6365             
6366             if (this.badge !== '') {
6367                  
6368                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6369             }
6370             return cfg;
6371         }
6372         
6373         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6374             cfg.cn = [
6375                 {
6376                     tag: this.tagtype,
6377                     href : this.href || "#",
6378                     html: this.html || '',
6379                     cls : ''
6380                 }
6381             ];
6382             if (this.tagtype == 'a') {
6383                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6384         
6385             }
6386             if (this.icon) {
6387                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6388             } else  if (this.fa) {
6389                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6390             } else if(this.glyphicon) {
6391                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6392             } else {
6393                 cfg.cn[0].cls += " nav-html";
6394             }
6395             
6396             if (this.menu) {
6397                 cfg.cn[0].html += " <span class='caret'></span>";
6398              
6399             }
6400             
6401             if (this.badge !== '') {
6402                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6403             }
6404         }
6405         
6406         
6407         
6408         return cfg;
6409     },
6410     onRender : function(ct, position)
6411     {
6412        // Roo.log("Call onRender: " + this.xtype);
6413         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6414             this.tag = 'div';
6415         }
6416         
6417         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6418         this.navLink = this.el.select('.nav-link',true).first();
6419         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6420         return ret;
6421     },
6422       
6423     
6424     initEvents: function() 
6425     {
6426         if (typeof (this.menu) != 'undefined') {
6427             this.menu.parentType = this.xtype;
6428             this.menu.triggerEl = this.el;
6429             this.menu = this.addxtype(Roo.apply({}, this.menu));
6430         }
6431         
6432         this.el.on('click', this.onClick, this);
6433         
6434         //if(this.tagtype == 'span'){
6435         //    this.el.select('span',true).on('click', this.onClick, this);
6436         //}
6437        
6438         // at this point parent should be available..
6439         this.parent().register(this);
6440     },
6441     
6442     onClick : function(e)
6443     {
6444         if (e.getTarget('.dropdown-menu-item')) {
6445             // did you click on a menu itemm.... - then don't trigger onclick..
6446             return;
6447         }
6448         
6449         if(
6450                 this.preventDefault || 
6451                 this.href == '#' 
6452         ){
6453             Roo.log("NavItem - prevent Default?");
6454             e.preventDefault();
6455         }
6456         
6457         if (this.disabled) {
6458             return;
6459         }
6460         
6461         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6462         if (tg && tg.transition) {
6463             Roo.log("waiting for the transitionend");
6464             return;
6465         }
6466         
6467         
6468         
6469         //Roo.log("fire event clicked");
6470         if(this.fireEvent('click', this, e) === false){
6471             return;
6472         };
6473         
6474         if(this.tagtype == 'span'){
6475             return;
6476         }
6477         
6478         //Roo.log(this.href);
6479         var ael = this.el.select('a',true).first();
6480         //Roo.log(ael);
6481         
6482         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6483             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6484             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6485                 return; // ignore... - it's a 'hash' to another page.
6486             }
6487             Roo.log("NavItem - prevent Default?");
6488             e.preventDefault();
6489             this.scrollToElement(e);
6490         }
6491         
6492         
6493         var p =  this.parent();
6494    
6495         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6496             if (typeof(p.setActiveItem) !== 'undefined') {
6497                 p.setActiveItem(this);
6498             }
6499         }
6500         
6501         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6502         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6503             // remove the collapsed menu expand...
6504             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6505         }
6506     },
6507     
6508     isActive: function () {
6509         return this.active
6510     },
6511     setActive : function(state, fire, is_was_active)
6512     {
6513         if (this.active && !state && this.navId) {
6514             this.was_active = true;
6515             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6516             if (nv) {
6517                 nv.clearWasActive(this);
6518             }
6519             
6520         }
6521         this.active = state;
6522         
6523         if (!state ) {
6524             this.el.removeClass('active');
6525             this.navLink ? this.navLink.removeClass('active') : false;
6526         } else if (!this.el.hasClass('active')) {
6527             
6528             this.el.addClass('active');
6529             if (Roo.bootstrap.version == 4 && this.navLink ) {
6530                 this.navLink.addClass('active');
6531             }
6532             
6533         }
6534         if (fire) {
6535             this.fireEvent('changed', this, state);
6536         }
6537         
6538         // show a panel if it's registered and related..
6539         
6540         if (!this.navId || !this.tabId || !state || is_was_active) {
6541             return;
6542         }
6543         
6544         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6545         if (!tg) {
6546             return;
6547         }
6548         var pan = tg.getPanelByName(this.tabId);
6549         if (!pan) {
6550             return;
6551         }
6552         // if we can not flip to new panel - go back to old nav highlight..
6553         if (false == tg.showPanel(pan)) {
6554             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6555             if (nv) {
6556                 var onav = nv.getWasActive();
6557                 if (onav) {
6558                     onav.setActive(true, false, true);
6559                 }
6560             }
6561             
6562         }
6563         
6564         
6565         
6566     },
6567      // this should not be here...
6568     setDisabled : function(state)
6569     {
6570         this.disabled = state;
6571         if (!state ) {
6572             this.el.removeClass('disabled');
6573         } else if (!this.el.hasClass('disabled')) {
6574             this.el.addClass('disabled');
6575         }
6576         
6577     },
6578     
6579     /**
6580      * Fetch the element to display the tooltip on.
6581      * @return {Roo.Element} defaults to this.el
6582      */
6583     tooltipEl : function()
6584     {
6585         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6586     },
6587     
6588     scrollToElement : function(e)
6589     {
6590         var c = document.body;
6591         
6592         /*
6593          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6594          */
6595         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6596             c = document.documentElement;
6597         }
6598         
6599         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6600         
6601         if(!target){
6602             return;
6603         }
6604
6605         var o = target.calcOffsetsTo(c);
6606         
6607         var options = {
6608             target : target,
6609             value : o[1]
6610         };
6611         
6612         this.fireEvent('scrollto', this, options, e);
6613         
6614         Roo.get(c).scrollTo('top', options.value, true);
6615         
6616         return;
6617     },
6618     /**
6619      * Set the HTML (text content) of the item
6620      * @param {string} html  content for the nav item
6621      */
6622     setHtml : function(html)
6623     {
6624         this.html = html;
6625         this.htmlEl.dom.innerHTML = html;
6626         
6627     } 
6628 });
6629  
6630
6631  /*
6632  * - LGPL
6633  *
6634  * sidebar item
6635  *
6636  *  li
6637  *    <span> icon </span>
6638  *    <span> text </span>
6639  *    <span>badge </span>
6640  */
6641
6642 /**
6643  * @class Roo.bootstrap.NavSidebarItem
6644  * @extends Roo.bootstrap.NavItem
6645  * Bootstrap Navbar.NavSidebarItem class
6646  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6647  * {Boolean} open is the menu open
6648  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6649  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6650  * {String} buttonSize (sm|md|lg)the extra classes for the button
6651  * {Boolean} showArrow show arrow next to the text (default true)
6652  * @constructor
6653  * Create a new Navbar Button
6654  * @param {Object} config The config object
6655  */
6656 Roo.bootstrap.NavSidebarItem = function(config){
6657     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6658     this.addEvents({
6659         // raw events
6660         /**
6661          * @event click
6662          * The raw click event for the entire grid.
6663          * @param {Roo.EventObject} e
6664          */
6665         "click" : true,
6666          /**
6667             * @event changed
6668             * Fires when the active item active state changes
6669             * @param {Roo.bootstrap.NavSidebarItem} this
6670             * @param {boolean} state the new state
6671              
6672          */
6673         'changed': true
6674     });
6675    
6676 };
6677
6678 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6679     
6680     badgeWeight : 'default',
6681     
6682     open: false,
6683     
6684     buttonView : false,
6685     
6686     buttonWeight : 'default',
6687     
6688     buttonSize : 'md',
6689     
6690     showArrow : true,
6691     
6692     getAutoCreate : function(){
6693         
6694         
6695         var a = {
6696                 tag: 'a',
6697                 href : this.href || '#',
6698                 cls: '',
6699                 html : '',
6700                 cn : []
6701         };
6702         
6703         if(this.buttonView){
6704             a = {
6705                 tag: 'button',
6706                 href : this.href || '#',
6707                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6708                 html : this.html,
6709                 cn : []
6710             };
6711         }
6712         
6713         var cfg = {
6714             tag: 'li',
6715             cls: '',
6716             cn: [ a ]
6717         };
6718         
6719         if (this.active) {
6720             cfg.cls += ' active';
6721         }
6722         
6723         if (this.disabled) {
6724             cfg.cls += ' disabled';
6725         }
6726         if (this.open) {
6727             cfg.cls += ' open x-open';
6728         }
6729         // left icon..
6730         if (this.glyphicon || this.icon) {
6731             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6732             a.cn.push({ tag : 'i', cls : c }) ;
6733         }
6734         
6735         if(!this.buttonView){
6736             var span = {
6737                 tag: 'span',
6738                 html : this.html || ''
6739             };
6740
6741             a.cn.push(span);
6742             
6743         }
6744         
6745         if (this.badge !== '') {
6746             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6747         }
6748         
6749         if (this.menu) {
6750             
6751             if(this.showArrow){
6752                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6753             }
6754             
6755             a.cls += ' dropdown-toggle treeview' ;
6756         }
6757         
6758         return cfg;
6759     },
6760     
6761     initEvents : function()
6762     { 
6763         if (typeof (this.menu) != 'undefined') {
6764             this.menu.parentType = this.xtype;
6765             this.menu.triggerEl = this.el;
6766             this.menu = this.addxtype(Roo.apply({}, this.menu));
6767         }
6768         
6769         this.el.on('click', this.onClick, this);
6770         
6771         if(this.badge !== ''){
6772             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6773         }
6774         
6775     },
6776     
6777     onClick : function(e)
6778     {
6779         if(this.disabled){
6780             e.preventDefault();
6781             return;
6782         }
6783         
6784         if(this.preventDefault){
6785             e.preventDefault();
6786         }
6787         
6788         this.fireEvent('click', this, e);
6789     },
6790     
6791     disable : function()
6792     {
6793         this.setDisabled(true);
6794     },
6795     
6796     enable : function()
6797     {
6798         this.setDisabled(false);
6799     },
6800     
6801     setDisabled : function(state)
6802     {
6803         if(this.disabled == state){
6804             return;
6805         }
6806         
6807         this.disabled = state;
6808         
6809         if (state) {
6810             this.el.addClass('disabled');
6811             return;
6812         }
6813         
6814         this.el.removeClass('disabled');
6815         
6816         return;
6817     },
6818     
6819     setActive : function(state)
6820     {
6821         if(this.active == state){
6822             return;
6823         }
6824         
6825         this.active = state;
6826         
6827         if (state) {
6828             this.el.addClass('active');
6829             return;
6830         }
6831         
6832         this.el.removeClass('active');
6833         
6834         return;
6835     },
6836     
6837     isActive: function () 
6838     {
6839         return this.active;
6840     },
6841     
6842     setBadge : function(str)
6843     {
6844         if(!this.badgeEl){
6845             return;
6846         }
6847         
6848         this.badgeEl.dom.innerHTML = str;
6849     }
6850     
6851    
6852      
6853  
6854 });
6855  
6856
6857  /*
6858  * - LGPL
6859  *
6860  *  Breadcrumb Nav
6861  * 
6862  */
6863 Roo.namespace('Roo.bootstrap.breadcrumb');
6864
6865
6866 /**
6867  * @class Roo.bootstrap.breadcrumb.Nav
6868  * @extends Roo.bootstrap.Component
6869  * Bootstrap Breadcrumb Nav Class
6870  *  
6871  * @children Roo.bootstrap.breadcrumb.Item
6872  * 
6873  * @constructor
6874  * Create a new breadcrumb.Nav
6875  * @param {Object} config The config object
6876  */
6877
6878
6879 Roo.bootstrap.breadcrumb.Nav = function(config){
6880     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6881     
6882     
6883 };
6884
6885 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6886     
6887     getAutoCreate : function()
6888     {
6889
6890         var cfg = {
6891             tag: 'nav',
6892             cn : [
6893                 {
6894                     tag : 'ol',
6895                     cls : 'breadcrumb'
6896                 }
6897             ]
6898             
6899         };
6900           
6901         return cfg;
6902     },
6903     
6904     initEvents: function()
6905     {
6906         this.olEl = this.el.select('ol',true).first();    
6907     },
6908     getChildContainer : function()
6909     {
6910         return this.olEl;  
6911     }
6912     
6913 });
6914
6915  /*
6916  * - LGPL
6917  *
6918  *  Breadcrumb Item
6919  * 
6920  */
6921
6922
6923 /**
6924  * @class Roo.bootstrap.breadcrumb.Nav
6925  * @extends Roo.bootstrap.Component
6926  * Bootstrap Breadcrumb Nav Class
6927  *  
6928  * @children Roo.bootstrap.breadcrumb.Component
6929  * @cfg {String} html the content of the link.
6930  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6931  * @cfg {Boolean} active is it active
6932
6933  * 
6934  * @constructor
6935  * Create a new breadcrumb.Nav
6936  * @param {Object} config The config object
6937  */
6938
6939 Roo.bootstrap.breadcrumb.Item = function(config){
6940     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6941     this.addEvents({
6942         // img events
6943         /**
6944          * @event click
6945          * The img click event for the img.
6946          * @param {Roo.EventObject} e
6947          */
6948         "click" : true
6949     });
6950     
6951 };
6952
6953 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6954     
6955     href: false,
6956     html : '',
6957     
6958     getAutoCreate : function()
6959     {
6960
6961         var cfg = {
6962             tag: 'li',
6963             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6964         };
6965         if (this.href !== false) {
6966             cfg.cn = [{
6967                 tag : 'a',
6968                 href : this.href,
6969                 html : this.html
6970             }];
6971         } else {
6972             cfg.html = this.html;
6973         }
6974         
6975         return cfg;
6976     },
6977     
6978     initEvents: function()
6979     {
6980         if (this.href) {
6981             this.el.select('a', true).first().on('click',this.onClick, this)
6982         }
6983         
6984     },
6985     onClick : function(e)
6986     {
6987         e.preventDefault();
6988         this.fireEvent('click',this,  e);
6989     }
6990     
6991 });
6992
6993  /*
6994  * - LGPL
6995  *
6996  * row
6997  * 
6998  */
6999
7000 /**
7001  * @class Roo.bootstrap.Row
7002  * @extends Roo.bootstrap.Component
7003  * Bootstrap Row class (contains columns...)
7004  * 
7005  * @constructor
7006  * Create a new Row
7007  * @param {Object} config The config object
7008  */
7009
7010 Roo.bootstrap.Row = function(config){
7011     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7012 };
7013
7014 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7015     
7016     getAutoCreate : function(){
7017        return {
7018             cls: 'row clearfix'
7019        };
7020     }
7021     
7022     
7023 });
7024
7025  
7026
7027  /*
7028  * - LGPL
7029  *
7030  * pagination
7031  * 
7032  */
7033
7034 /**
7035  * @class Roo.bootstrap.Pagination
7036  * @extends Roo.bootstrap.Component
7037  * Bootstrap Pagination class
7038  * @cfg {String} size xs | sm | md | lg
7039  * @cfg {Boolean} inverse false | true
7040  * 
7041  * @constructor
7042  * Create a new Pagination
7043  * @param {Object} config The config object
7044  */
7045
7046 Roo.bootstrap.Pagination = function(config){
7047     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7048 };
7049
7050 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7051     
7052     cls: false,
7053     size: false,
7054     inverse: false,
7055     
7056     getAutoCreate : function(){
7057         var cfg = {
7058             tag: 'ul',
7059                 cls: 'pagination'
7060         };
7061         if (this.inverse) {
7062             cfg.cls += ' inverse';
7063         }
7064         if (this.html) {
7065             cfg.html=this.html;
7066         }
7067         if (this.cls) {
7068             cfg.cls += " " + this.cls;
7069         }
7070         return cfg;
7071     }
7072    
7073 });
7074
7075  
7076
7077  /*
7078  * - LGPL
7079  *
7080  * Pagination item
7081  * 
7082  */
7083
7084
7085 /**
7086  * @class Roo.bootstrap.PaginationItem
7087  * @extends Roo.bootstrap.Component
7088  * Bootstrap PaginationItem class
7089  * @cfg {String} html text
7090  * @cfg {String} href the link
7091  * @cfg {Boolean} preventDefault (true | false) default true
7092  * @cfg {Boolean} active (true | false) default false
7093  * @cfg {Boolean} disabled default false
7094  * 
7095  * 
7096  * @constructor
7097  * Create a new PaginationItem
7098  * @param {Object} config The config object
7099  */
7100
7101
7102 Roo.bootstrap.PaginationItem = function(config){
7103     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7104     this.addEvents({
7105         // raw events
7106         /**
7107          * @event click
7108          * The raw click event for the entire grid.
7109          * @param {Roo.EventObject} e
7110          */
7111         "click" : true
7112     });
7113 };
7114
7115 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7116     
7117     href : false,
7118     html : false,
7119     preventDefault: true,
7120     active : false,
7121     cls : false,
7122     disabled: false,
7123     
7124     getAutoCreate : function(){
7125         var cfg= {
7126             tag: 'li',
7127             cn: [
7128                 {
7129                     tag : 'a',
7130                     href : this.href ? this.href : '#',
7131                     html : this.html ? this.html : ''
7132                 }
7133             ]
7134         };
7135         
7136         if(this.cls){
7137             cfg.cls = this.cls;
7138         }
7139         
7140         if(this.disabled){
7141             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7142         }
7143         
7144         if(this.active){
7145             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7146         }
7147         
7148         return cfg;
7149     },
7150     
7151     initEvents: function() {
7152         
7153         this.el.on('click', this.onClick, this);
7154         
7155     },
7156     onClick : function(e)
7157     {
7158         Roo.log('PaginationItem on click ');
7159         if(this.preventDefault){
7160             e.preventDefault();
7161         }
7162         
7163         if(this.disabled){
7164             return;
7165         }
7166         
7167         this.fireEvent('click', this, e);
7168     }
7169    
7170 });
7171
7172  
7173
7174  /*
7175  * - LGPL
7176  *
7177  * slider
7178  * 
7179  */
7180
7181
7182 /**
7183  * @class Roo.bootstrap.Slider
7184  * @extends Roo.bootstrap.Component
7185  * Bootstrap Slider class
7186  *    
7187  * @constructor
7188  * Create a new Slider
7189  * @param {Object} config The config object
7190  */
7191
7192 Roo.bootstrap.Slider = function(config){
7193     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7194 };
7195
7196 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7197     
7198     getAutoCreate : function(){
7199         
7200         var cfg = {
7201             tag: 'div',
7202             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7203             cn: [
7204                 {
7205                     tag: 'a',
7206                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7207                 }
7208             ]
7209         };
7210         
7211         return cfg;
7212     }
7213    
7214 });
7215
7216  /*
7217  * Based on:
7218  * Ext JS Library 1.1.1
7219  * Copyright(c) 2006-2007, Ext JS, LLC.
7220  *
7221  * Originally Released Under LGPL - original licence link has changed is not relivant.
7222  *
7223  * Fork - LGPL
7224  * <script type="text/javascript">
7225  */
7226  
7227
7228 /**
7229  * @class Roo.grid.ColumnModel
7230  * @extends Roo.util.Observable
7231  * This is the default implementation of a ColumnModel used by the Grid. It defines
7232  * the columns in the grid.
7233  * <br>Usage:<br>
7234  <pre><code>
7235  var colModel = new Roo.grid.ColumnModel([
7236         {header: "Ticker", width: 60, sortable: true, locked: true},
7237         {header: "Company Name", width: 150, sortable: true},
7238         {header: "Market Cap.", width: 100, sortable: true},
7239         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7240         {header: "Employees", width: 100, sortable: true, resizable: false}
7241  ]);
7242  </code></pre>
7243  * <p>
7244  
7245  * The config options listed for this class are options which may appear in each
7246  * individual column definition.
7247  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7248  * @constructor
7249  * @param {Object} config An Array of column config objects. See this class's
7250  * config objects for details.
7251 */
7252 Roo.grid.ColumnModel = function(config){
7253         /**
7254      * The config passed into the constructor
7255      */
7256     this.config = config;
7257     this.lookup = {};
7258
7259     // if no id, create one
7260     // if the column does not have a dataIndex mapping,
7261     // map it to the order it is in the config
7262     for(var i = 0, len = config.length; i < len; i++){
7263         var c = config[i];
7264         if(typeof c.dataIndex == "undefined"){
7265             c.dataIndex = i;
7266         }
7267         if(typeof c.renderer == "string"){
7268             c.renderer = Roo.util.Format[c.renderer];
7269         }
7270         if(typeof c.id == "undefined"){
7271             c.id = Roo.id();
7272         }
7273         if(c.editor && c.editor.xtype){
7274             c.editor  = Roo.factory(c.editor, Roo.grid);
7275         }
7276         if(c.editor && c.editor.isFormField){
7277             c.editor = new Roo.grid.GridEditor(c.editor);
7278         }
7279         this.lookup[c.id] = c;
7280     }
7281
7282     /**
7283      * The width of columns which have no width specified (defaults to 100)
7284      * @type Number
7285      */
7286     this.defaultWidth = 100;
7287
7288     /**
7289      * Default sortable of columns which have no sortable specified (defaults to false)
7290      * @type Boolean
7291      */
7292     this.defaultSortable = false;
7293
7294     this.addEvents({
7295         /**
7296              * @event widthchange
7297              * Fires when the width of a column changes.
7298              * @param {ColumnModel} this
7299              * @param {Number} columnIndex The column index
7300              * @param {Number} newWidth The new width
7301              */
7302             "widthchange": true,
7303         /**
7304              * @event headerchange
7305              * Fires when the text of a header changes.
7306              * @param {ColumnModel} this
7307              * @param {Number} columnIndex The column index
7308              * @param {Number} newText The new header text
7309              */
7310             "headerchange": true,
7311         /**
7312              * @event hiddenchange
7313              * Fires when a column is hidden or "unhidden".
7314              * @param {ColumnModel} this
7315              * @param {Number} columnIndex The column index
7316              * @param {Boolean} hidden true if hidden, false otherwise
7317              */
7318             "hiddenchange": true,
7319             /**
7320          * @event columnmoved
7321          * Fires when a column is moved.
7322          * @param {ColumnModel} this
7323          * @param {Number} oldIndex
7324          * @param {Number} newIndex
7325          */
7326         "columnmoved" : true,
7327         /**
7328          * @event columlockchange
7329          * Fires when a column's locked state is changed
7330          * @param {ColumnModel} this
7331          * @param {Number} colIndex
7332          * @param {Boolean} locked true if locked
7333          */
7334         "columnlockchange" : true
7335     });
7336     Roo.grid.ColumnModel.superclass.constructor.call(this);
7337 };
7338 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7339     /**
7340      * @cfg {String} header The header text to display in the Grid view.
7341      */
7342     /**
7343      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7344      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7345      * specified, the column's index is used as an index into the Record's data Array.
7346      */
7347     /**
7348      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7349      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7350      */
7351     /**
7352      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7353      * Defaults to the value of the {@link #defaultSortable} property.
7354      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7355      */
7356     /**
7357      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7358      */
7359     /**
7360      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7361      */
7362     /**
7363      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7364      */
7365     /**
7366      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7367      */
7368     /**
7369      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7370      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7371      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7372      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7373      */
7374        /**
7375      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7376      */
7377     /**
7378      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7379      */
7380     /**
7381      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7382      */
7383     /**
7384      * @cfg {String} cursor (Optional)
7385      */
7386     /**
7387      * @cfg {String} tooltip (Optional)
7388      */
7389     /**
7390      * @cfg {Number} xs (Optional)
7391      */
7392     /**
7393      * @cfg {Number} sm (Optional)
7394      */
7395     /**
7396      * @cfg {Number} md (Optional)
7397      */
7398     /**
7399      * @cfg {Number} lg (Optional)
7400      */
7401     /**
7402      * Returns the id of the column at the specified index.
7403      * @param {Number} index The column index
7404      * @return {String} the id
7405      */
7406     getColumnId : function(index){
7407         return this.config[index].id;
7408     },
7409
7410     /**
7411      * Returns the column for a specified id.
7412      * @param {String} id The column id
7413      * @return {Object} the column
7414      */
7415     getColumnById : function(id){
7416         return this.lookup[id];
7417     },
7418
7419     
7420     /**
7421      * Returns the column for a specified dataIndex.
7422      * @param {String} dataIndex The column dataIndex
7423      * @return {Object|Boolean} the column or false if not found
7424      */
7425     getColumnByDataIndex: function(dataIndex){
7426         var index = this.findColumnIndex(dataIndex);
7427         return index > -1 ? this.config[index] : false;
7428     },
7429     
7430     /**
7431      * Returns the index for a specified column id.
7432      * @param {String} id The column id
7433      * @return {Number} the index, or -1 if not found
7434      */
7435     getIndexById : function(id){
7436         for(var i = 0, len = this.config.length; i < len; i++){
7437             if(this.config[i].id == id){
7438                 return i;
7439             }
7440         }
7441         return -1;
7442     },
7443     
7444     /**
7445      * Returns the index for a specified column dataIndex.
7446      * @param {String} dataIndex The column dataIndex
7447      * @return {Number} the index, or -1 if not found
7448      */
7449     
7450     findColumnIndex : function(dataIndex){
7451         for(var i = 0, len = this.config.length; i < len; i++){
7452             if(this.config[i].dataIndex == dataIndex){
7453                 return i;
7454             }
7455         }
7456         return -1;
7457     },
7458     
7459     
7460     moveColumn : function(oldIndex, newIndex){
7461         var c = this.config[oldIndex];
7462         this.config.splice(oldIndex, 1);
7463         this.config.splice(newIndex, 0, c);
7464         this.dataMap = null;
7465         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7466     },
7467
7468     isLocked : function(colIndex){
7469         return this.config[colIndex].locked === true;
7470     },
7471
7472     setLocked : function(colIndex, value, suppressEvent){
7473         if(this.isLocked(colIndex) == value){
7474             return;
7475         }
7476         this.config[colIndex].locked = value;
7477         if(!suppressEvent){
7478             this.fireEvent("columnlockchange", this, colIndex, value);
7479         }
7480     },
7481
7482     getTotalLockedWidth : function(){
7483         var totalWidth = 0;
7484         for(var i = 0; i < this.config.length; i++){
7485             if(this.isLocked(i) && !this.isHidden(i)){
7486                 this.totalWidth += this.getColumnWidth(i);
7487             }
7488         }
7489         return totalWidth;
7490     },
7491
7492     getLockedCount : function(){
7493         for(var i = 0, len = this.config.length; i < len; i++){
7494             if(!this.isLocked(i)){
7495                 return i;
7496             }
7497         }
7498         
7499         return this.config.length;
7500     },
7501
7502     /**
7503      * Returns the number of columns.
7504      * @return {Number}
7505      */
7506     getColumnCount : function(visibleOnly){
7507         if(visibleOnly === true){
7508             var c = 0;
7509             for(var i = 0, len = this.config.length; i < len; i++){
7510                 if(!this.isHidden(i)){
7511                     c++;
7512                 }
7513             }
7514             return c;
7515         }
7516         return this.config.length;
7517     },
7518
7519     /**
7520      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7521      * @param {Function} fn
7522      * @param {Object} scope (optional)
7523      * @return {Array} result
7524      */
7525     getColumnsBy : function(fn, scope){
7526         var r = [];
7527         for(var i = 0, len = this.config.length; i < len; i++){
7528             var c = this.config[i];
7529             if(fn.call(scope||this, c, i) === true){
7530                 r[r.length] = c;
7531             }
7532         }
7533         return r;
7534     },
7535
7536     /**
7537      * Returns true if the specified column is sortable.
7538      * @param {Number} col The column index
7539      * @return {Boolean}
7540      */
7541     isSortable : function(col){
7542         if(typeof this.config[col].sortable == "undefined"){
7543             return this.defaultSortable;
7544         }
7545         return this.config[col].sortable;
7546     },
7547
7548     /**
7549      * Returns the rendering (formatting) function defined for the column.
7550      * @param {Number} col The column index.
7551      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7552      */
7553     getRenderer : function(col){
7554         if(!this.config[col].renderer){
7555             return Roo.grid.ColumnModel.defaultRenderer;
7556         }
7557         return this.config[col].renderer;
7558     },
7559
7560     /**
7561      * Sets the rendering (formatting) function for a column.
7562      * @param {Number} col The column index
7563      * @param {Function} fn The function to use to process the cell's raw data
7564      * to return HTML markup for the grid view. The render function is called with
7565      * the following parameters:<ul>
7566      * <li>Data value.</li>
7567      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7568      * <li>css A CSS style string to apply to the table cell.</li>
7569      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7570      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7571      * <li>Row index</li>
7572      * <li>Column index</li>
7573      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7574      */
7575     setRenderer : function(col, fn){
7576         this.config[col].renderer = fn;
7577     },
7578
7579     /**
7580      * Returns the width for the specified column.
7581      * @param {Number} col The column index
7582      * @return {Number}
7583      */
7584     getColumnWidth : function(col){
7585         return this.config[col].width * 1 || this.defaultWidth;
7586     },
7587
7588     /**
7589      * Sets the width for a column.
7590      * @param {Number} col The column index
7591      * @param {Number} width The new width
7592      */
7593     setColumnWidth : function(col, width, suppressEvent){
7594         this.config[col].width = width;
7595         this.totalWidth = null;
7596         if(!suppressEvent){
7597              this.fireEvent("widthchange", this, col, width);
7598         }
7599     },
7600
7601     /**
7602      * Returns the total width of all columns.
7603      * @param {Boolean} includeHidden True to include hidden column widths
7604      * @return {Number}
7605      */
7606     getTotalWidth : function(includeHidden){
7607         if(!this.totalWidth){
7608             this.totalWidth = 0;
7609             for(var i = 0, len = this.config.length; i < len; i++){
7610                 if(includeHidden || !this.isHidden(i)){
7611                     this.totalWidth += this.getColumnWidth(i);
7612                 }
7613             }
7614         }
7615         return this.totalWidth;
7616     },
7617
7618     /**
7619      * Returns the header for the specified column.
7620      * @param {Number} col The column index
7621      * @return {String}
7622      */
7623     getColumnHeader : function(col){
7624         return this.config[col].header;
7625     },
7626
7627     /**
7628      * Sets the header for a column.
7629      * @param {Number} col The column index
7630      * @param {String} header The new header
7631      */
7632     setColumnHeader : function(col, header){
7633         this.config[col].header = header;
7634         this.fireEvent("headerchange", this, col, header);
7635     },
7636
7637     /**
7638      * Returns the tooltip for the specified column.
7639      * @param {Number} col The column index
7640      * @return {String}
7641      */
7642     getColumnTooltip : function(col){
7643             return this.config[col].tooltip;
7644     },
7645     /**
7646      * Sets the tooltip for a column.
7647      * @param {Number} col The column index
7648      * @param {String} tooltip The new tooltip
7649      */
7650     setColumnTooltip : function(col, tooltip){
7651             this.config[col].tooltip = tooltip;
7652     },
7653
7654     /**
7655      * Returns the dataIndex for the specified column.
7656      * @param {Number} col The column index
7657      * @return {Number}
7658      */
7659     getDataIndex : function(col){
7660         return this.config[col].dataIndex;
7661     },
7662
7663     /**
7664      * Sets the dataIndex for a column.
7665      * @param {Number} col The column index
7666      * @param {Number} dataIndex The new dataIndex
7667      */
7668     setDataIndex : function(col, dataIndex){
7669         this.config[col].dataIndex = dataIndex;
7670     },
7671
7672     
7673     
7674     /**
7675      * Returns true if the cell is editable.
7676      * @param {Number} colIndex The column index
7677      * @param {Number} rowIndex The row index - this is nto actually used..?
7678      * @return {Boolean}
7679      */
7680     isCellEditable : function(colIndex, rowIndex){
7681         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7682     },
7683
7684     /**
7685      * Returns the editor defined for the cell/column.
7686      * return false or null to disable editing.
7687      * @param {Number} colIndex The column index
7688      * @param {Number} rowIndex The row index
7689      * @return {Object}
7690      */
7691     getCellEditor : function(colIndex, rowIndex){
7692         return this.config[colIndex].editor;
7693     },
7694
7695     /**
7696      * Sets if a column is editable.
7697      * @param {Number} col The column index
7698      * @param {Boolean} editable True if the column is editable
7699      */
7700     setEditable : function(col, editable){
7701         this.config[col].editable = editable;
7702     },
7703
7704
7705     /**
7706      * Returns true if the column is hidden.
7707      * @param {Number} colIndex The column index
7708      * @return {Boolean}
7709      */
7710     isHidden : function(colIndex){
7711         return this.config[colIndex].hidden;
7712     },
7713
7714
7715     /**
7716      * Returns true if the column width cannot be changed
7717      */
7718     isFixed : function(colIndex){
7719         return this.config[colIndex].fixed;
7720     },
7721
7722     /**
7723      * Returns true if the column can be resized
7724      * @return {Boolean}
7725      */
7726     isResizable : function(colIndex){
7727         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7728     },
7729     /**
7730      * Sets if a column is hidden.
7731      * @param {Number} colIndex The column index
7732      * @param {Boolean} hidden True if the column is hidden
7733      */
7734     setHidden : function(colIndex, hidden){
7735         this.config[colIndex].hidden = hidden;
7736         this.totalWidth = null;
7737         this.fireEvent("hiddenchange", this, colIndex, hidden);
7738     },
7739
7740     /**
7741      * Sets the editor for a column.
7742      * @param {Number} col The column index
7743      * @param {Object} editor The editor object
7744      */
7745     setEditor : function(col, editor){
7746         this.config[col].editor = editor;
7747     }
7748 });
7749
7750 Roo.grid.ColumnModel.defaultRenderer = function(value)
7751 {
7752     if(typeof value == "object") {
7753         return value;
7754     }
7755         if(typeof value == "string" && value.length < 1){
7756             return "&#160;";
7757         }
7758     
7759         return String.format("{0}", value);
7760 };
7761
7762 // Alias for backwards compatibility
7763 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7764 /*
7765  * Based on:
7766  * Ext JS Library 1.1.1
7767  * Copyright(c) 2006-2007, Ext JS, LLC.
7768  *
7769  * Originally Released Under LGPL - original licence link has changed is not relivant.
7770  *
7771  * Fork - LGPL
7772  * <script type="text/javascript">
7773  */
7774  
7775 /**
7776  * @class Roo.LoadMask
7777  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7778  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7779  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7780  * element's UpdateManager load indicator and will be destroyed after the initial load.
7781  * @constructor
7782  * Create a new LoadMask
7783  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7784  * @param {Object} config The config object
7785  */
7786 Roo.LoadMask = function(el, config){
7787     this.el = Roo.get(el);
7788     Roo.apply(this, config);
7789     if(this.store){
7790         this.store.on('beforeload', this.onBeforeLoad, this);
7791         this.store.on('load', this.onLoad, this);
7792         this.store.on('loadexception', this.onLoadException, this);
7793         this.removeMask = false;
7794     }else{
7795         var um = this.el.getUpdateManager();
7796         um.showLoadIndicator = false; // disable the default indicator
7797         um.on('beforeupdate', this.onBeforeLoad, this);
7798         um.on('update', this.onLoad, this);
7799         um.on('failure', this.onLoad, this);
7800         this.removeMask = true;
7801     }
7802 };
7803
7804 Roo.LoadMask.prototype = {
7805     /**
7806      * @cfg {Boolean} removeMask
7807      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7808      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7809      */
7810     /**
7811      * @cfg {String} msg
7812      * The text to display in a centered loading message box (defaults to 'Loading...')
7813      */
7814     msg : 'Loading...',
7815     /**
7816      * @cfg {String} msgCls
7817      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7818      */
7819     msgCls : 'x-mask-loading',
7820
7821     /**
7822      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7823      * @type Boolean
7824      */
7825     disabled: false,
7826
7827     /**
7828      * Disables the mask to prevent it from being displayed
7829      */
7830     disable : function(){
7831        this.disabled = true;
7832     },
7833
7834     /**
7835      * Enables the mask so that it can be displayed
7836      */
7837     enable : function(){
7838         this.disabled = false;
7839     },
7840     
7841     onLoadException : function()
7842     {
7843         Roo.log(arguments);
7844         
7845         if (typeof(arguments[3]) != 'undefined') {
7846             Roo.MessageBox.alert("Error loading",arguments[3]);
7847         } 
7848         /*
7849         try {
7850             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7851                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7852             }   
7853         } catch(e) {
7854             
7855         }
7856         */
7857     
7858         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7859     },
7860     // private
7861     onLoad : function()
7862     {
7863         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7864     },
7865
7866     // private
7867     onBeforeLoad : function(){
7868         if(!this.disabled){
7869             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7870         }
7871     },
7872
7873     // private
7874     destroy : function(){
7875         if(this.store){
7876             this.store.un('beforeload', this.onBeforeLoad, this);
7877             this.store.un('load', this.onLoad, this);
7878             this.store.un('loadexception', this.onLoadException, this);
7879         }else{
7880             var um = this.el.getUpdateManager();
7881             um.un('beforeupdate', this.onBeforeLoad, this);
7882             um.un('update', this.onLoad, this);
7883             um.un('failure', this.onLoad, this);
7884         }
7885     }
7886 };/*
7887  * - LGPL
7888  *
7889  * table
7890  * 
7891  */
7892
7893 /**
7894  * @class Roo.bootstrap.Table
7895  * @extends Roo.bootstrap.Component
7896  * Bootstrap Table class
7897  * @cfg {String} cls table class
7898  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7899  * @cfg {String} bgcolor Specifies the background color for a table
7900  * @cfg {Number} border Specifies whether the table cells should have borders or not
7901  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7902  * @cfg {Number} cellspacing Specifies the space between cells
7903  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7904  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7905  * @cfg {String} sortable Specifies that the table should be sortable
7906  * @cfg {String} summary Specifies a summary of the content of a table
7907  * @cfg {Number} width Specifies the width of a table
7908  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7909  * 
7910  * @cfg {boolean} striped Should the rows be alternative striped
7911  * @cfg {boolean} bordered Add borders to the table
7912  * @cfg {boolean} hover Add hover highlighting
7913  * @cfg {boolean} condensed Format condensed
7914  * @cfg {boolean} responsive Format condensed
7915  * @cfg {Boolean} loadMask (true|false) default false
7916  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7917  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7918  * @cfg {Boolean} rowSelection (true|false) default false
7919  * @cfg {Boolean} cellSelection (true|false) default false
7920  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7921  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7922  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7923  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7924  
7925  * 
7926  * @constructor
7927  * Create a new Table
7928  * @param {Object} config The config object
7929  */
7930
7931 Roo.bootstrap.Table = function(config){
7932     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7933     
7934   
7935     
7936     // BC...
7937     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7938     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7939     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7940     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7941     
7942     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7943     if (this.sm) {
7944         this.sm.grid = this;
7945         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7946         this.sm = this.selModel;
7947         this.sm.xmodule = this.xmodule || false;
7948     }
7949     
7950     if (this.cm && typeof(this.cm.config) == 'undefined') {
7951         this.colModel = new Roo.grid.ColumnModel(this.cm);
7952         this.cm = this.colModel;
7953         this.cm.xmodule = this.xmodule || false;
7954     }
7955     if (this.store) {
7956         this.store= Roo.factory(this.store, Roo.data);
7957         this.ds = this.store;
7958         this.ds.xmodule = this.xmodule || false;
7959          
7960     }
7961     if (this.footer && this.store) {
7962         this.footer.dataSource = this.ds;
7963         this.footer = Roo.factory(this.footer);
7964     }
7965     
7966     /** @private */
7967     this.addEvents({
7968         /**
7969          * @event cellclick
7970          * Fires when a cell is clicked
7971          * @param {Roo.bootstrap.Table} this
7972          * @param {Roo.Element} el
7973          * @param {Number} rowIndex
7974          * @param {Number} columnIndex
7975          * @param {Roo.EventObject} e
7976          */
7977         "cellclick" : true,
7978         /**
7979          * @event celldblclick
7980          * Fires when a cell is double clicked
7981          * @param {Roo.bootstrap.Table} this
7982          * @param {Roo.Element} el
7983          * @param {Number} rowIndex
7984          * @param {Number} columnIndex
7985          * @param {Roo.EventObject} e
7986          */
7987         "celldblclick" : true,
7988         /**
7989          * @event rowclick
7990          * Fires when a row is clicked
7991          * @param {Roo.bootstrap.Table} this
7992          * @param {Roo.Element} el
7993          * @param {Number} rowIndex
7994          * @param {Roo.EventObject} e
7995          */
7996         "rowclick" : true,
7997         /**
7998          * @event rowdblclick
7999          * Fires when a row is double clicked
8000          * @param {Roo.bootstrap.Table} this
8001          * @param {Roo.Element} el
8002          * @param {Number} rowIndex
8003          * @param {Roo.EventObject} e
8004          */
8005         "rowdblclick" : true,
8006         /**
8007          * @event mouseover
8008          * Fires when a mouseover occur
8009          * @param {Roo.bootstrap.Table} this
8010          * @param {Roo.Element} el
8011          * @param {Number} rowIndex
8012          * @param {Number} columnIndex
8013          * @param {Roo.EventObject} e
8014          */
8015         "mouseover" : true,
8016         /**
8017          * @event mouseout
8018          * Fires when a mouseout occur
8019          * @param {Roo.bootstrap.Table} this
8020          * @param {Roo.Element} el
8021          * @param {Number} rowIndex
8022          * @param {Number} columnIndex
8023          * @param {Roo.EventObject} e
8024          */
8025         "mouseout" : true,
8026         /**
8027          * @event rowclass
8028          * Fires when a row is rendered, so you can change add a style to it.
8029          * @param {Roo.bootstrap.Table} this
8030          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8031          */
8032         'rowclass' : true,
8033           /**
8034          * @event rowsrendered
8035          * Fires when all the  rows have been rendered
8036          * @param {Roo.bootstrap.Table} this
8037          */
8038         'rowsrendered' : true,
8039         /**
8040          * @event contextmenu
8041          * The raw contextmenu event for the entire grid.
8042          * @param {Roo.EventObject} e
8043          */
8044         "contextmenu" : true,
8045         /**
8046          * @event rowcontextmenu
8047          * Fires when a row is right clicked
8048          * @param {Roo.bootstrap.Table} this
8049          * @param {Number} rowIndex
8050          * @param {Roo.EventObject} e
8051          */
8052         "rowcontextmenu" : true,
8053         /**
8054          * @event cellcontextmenu
8055          * Fires when a cell is right clicked
8056          * @param {Roo.bootstrap.Table} this
8057          * @param {Number} rowIndex
8058          * @param {Number} cellIndex
8059          * @param {Roo.EventObject} e
8060          */
8061          "cellcontextmenu" : true,
8062          /**
8063          * @event headercontextmenu
8064          * Fires when a header is right clicked
8065          * @param {Roo.bootstrap.Table} this
8066          * @param {Number} columnIndex
8067          * @param {Roo.EventObject} e
8068          */
8069         "headercontextmenu" : true
8070     });
8071 };
8072
8073 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8074     
8075     cls: false,
8076     align: false,
8077     bgcolor: false,
8078     border: false,
8079     cellpadding: false,
8080     cellspacing: false,
8081     frame: false,
8082     rules: false,
8083     sortable: false,
8084     summary: false,
8085     width: false,
8086     striped : false,
8087     scrollBody : false,
8088     bordered: false,
8089     hover:  false,
8090     condensed : false,
8091     responsive : false,
8092     sm : false,
8093     cm : false,
8094     store : false,
8095     loadMask : false,
8096     footerShow : true,
8097     headerShow : true,
8098   
8099     rowSelection : false,
8100     cellSelection : false,
8101     layout : false,
8102     
8103     // Roo.Element - the tbody
8104     mainBody: false,
8105     // Roo.Element - thead element
8106     mainHead: false,
8107     
8108     container: false, // used by gridpanel...
8109     
8110     lazyLoad : false,
8111     
8112     CSS : Roo.util.CSS,
8113     
8114     auto_hide_footer : false,
8115     
8116     getAutoCreate : function()
8117     {
8118         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8119         
8120         cfg = {
8121             tag: 'table',
8122             cls : 'table',
8123             cn : []
8124         };
8125         if (this.scrollBody) {
8126             cfg.cls += ' table-body-fixed';
8127         }    
8128         if (this.striped) {
8129             cfg.cls += ' table-striped';
8130         }
8131         
8132         if (this.hover) {
8133             cfg.cls += ' table-hover';
8134         }
8135         if (this.bordered) {
8136             cfg.cls += ' table-bordered';
8137         }
8138         if (this.condensed) {
8139             cfg.cls += ' table-condensed';
8140         }
8141         if (this.responsive) {
8142             cfg.cls += ' table-responsive';
8143         }
8144         
8145         if (this.cls) {
8146             cfg.cls+=  ' ' +this.cls;
8147         }
8148         
8149         // this lot should be simplifed...
8150         var _t = this;
8151         var cp = [
8152             'align',
8153             'bgcolor',
8154             'border',
8155             'cellpadding',
8156             'cellspacing',
8157             'frame',
8158             'rules',
8159             'sortable',
8160             'summary',
8161             'width'
8162         ].forEach(function(k) {
8163             if (_t[k]) {
8164                 cfg[k] = _t[k];
8165             }
8166         });
8167         
8168         
8169         if (this.layout) {
8170             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8171         }
8172         
8173         if(this.store || this.cm){
8174             if(this.headerShow){
8175                 cfg.cn.push(this.renderHeader());
8176             }
8177             
8178             cfg.cn.push(this.renderBody());
8179             
8180             if(this.footerShow){
8181                 cfg.cn.push(this.renderFooter());
8182             }
8183             // where does this come from?
8184             //cfg.cls+=  ' TableGrid';
8185         }
8186         
8187         return { cn : [ cfg ] };
8188     },
8189     
8190     initEvents : function()
8191     {   
8192         if(!this.store || !this.cm){
8193             return;
8194         }
8195         if (this.selModel) {
8196             this.selModel.initEvents();
8197         }
8198         
8199         
8200         //Roo.log('initEvents with ds!!!!');
8201         
8202         this.mainBody = this.el.select('tbody', true).first();
8203         this.mainHead = this.el.select('thead', true).first();
8204         this.mainFoot = this.el.select('tfoot', true).first();
8205         
8206         
8207         
8208         var _this = this;
8209         
8210         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8211             e.on('click', _this.sort, _this);
8212         });
8213         
8214         this.mainBody.on("click", this.onClick, this);
8215         this.mainBody.on("dblclick", this.onDblClick, this);
8216         
8217         // why is this done????? = it breaks dialogs??
8218         //this.parent().el.setStyle('position', 'relative');
8219         
8220         
8221         if (this.footer) {
8222             this.footer.parentId = this.id;
8223             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8224             
8225             if(this.lazyLoad){
8226                 this.el.select('tfoot tr td').first().addClass('hide');
8227             }
8228         } 
8229         
8230         if(this.loadMask) {
8231             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8232         }
8233         
8234         this.store.on('load', this.onLoad, this);
8235         this.store.on('beforeload', this.onBeforeLoad, this);
8236         this.store.on('update', this.onUpdate, this);
8237         this.store.on('add', this.onAdd, this);
8238         this.store.on("clear", this.clear, this);
8239         
8240         this.el.on("contextmenu", this.onContextMenu, this);
8241         
8242         this.mainBody.on('scroll', this.onBodyScroll, this);
8243         
8244         this.cm.on("headerchange", this.onHeaderChange, this);
8245         
8246         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8247         
8248     },
8249     
8250     onContextMenu : function(e, t)
8251     {
8252         this.processEvent("contextmenu", e);
8253     },
8254     
8255     processEvent : function(name, e)
8256     {
8257         if (name != 'touchstart' ) {
8258             this.fireEvent(name, e);    
8259         }
8260         
8261         var t = e.getTarget();
8262         
8263         var cell = Roo.get(t);
8264         
8265         if(!cell){
8266             return;
8267         }
8268         
8269         if(cell.findParent('tfoot', false, true)){
8270             return;
8271         }
8272         
8273         if(cell.findParent('thead', false, true)){
8274             
8275             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8276                 cell = Roo.get(t).findParent('th', false, true);
8277                 if (!cell) {
8278                     Roo.log("failed to find th in thead?");
8279                     Roo.log(e.getTarget());
8280                     return;
8281                 }
8282             }
8283             
8284             var cellIndex = cell.dom.cellIndex;
8285             
8286             var ename = name == 'touchstart' ? 'click' : name;
8287             this.fireEvent("header" + ename, this, cellIndex, e);
8288             
8289             return;
8290         }
8291         
8292         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8293             cell = Roo.get(t).findParent('td', false, true);
8294             if (!cell) {
8295                 Roo.log("failed to find th in tbody?");
8296                 Roo.log(e.getTarget());
8297                 return;
8298             }
8299         }
8300         
8301         var row = cell.findParent('tr', false, true);
8302         var cellIndex = cell.dom.cellIndex;
8303         var rowIndex = row.dom.rowIndex - 1;
8304         
8305         if(row !== false){
8306             
8307             this.fireEvent("row" + name, this, rowIndex, e);
8308             
8309             if(cell !== false){
8310             
8311                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8312             }
8313         }
8314         
8315     },
8316     
8317     onMouseover : function(e, el)
8318     {
8319         var cell = Roo.get(el);
8320         
8321         if(!cell){
8322             return;
8323         }
8324         
8325         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8326             cell = cell.findParent('td', false, true);
8327         }
8328         
8329         var row = cell.findParent('tr', false, true);
8330         var cellIndex = cell.dom.cellIndex;
8331         var rowIndex = row.dom.rowIndex - 1; // start from 0
8332         
8333         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8334         
8335     },
8336     
8337     onMouseout : function(e, el)
8338     {
8339         var cell = Roo.get(el);
8340         
8341         if(!cell){
8342             return;
8343         }
8344         
8345         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8346             cell = cell.findParent('td', false, true);
8347         }
8348         
8349         var row = cell.findParent('tr', false, true);
8350         var cellIndex = cell.dom.cellIndex;
8351         var rowIndex = row.dom.rowIndex - 1; // start from 0
8352         
8353         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8354         
8355     },
8356     
8357     onClick : function(e, el)
8358     {
8359         var cell = Roo.get(el);
8360         
8361         if(!cell || (!this.cellSelection && !this.rowSelection)){
8362             return;
8363         }
8364         
8365         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8366             cell = cell.findParent('td', false, true);
8367         }
8368         
8369         if(!cell || typeof(cell) == 'undefined'){
8370             return;
8371         }
8372         
8373         var row = cell.findParent('tr', false, true);
8374         
8375         if(!row || typeof(row) == 'undefined'){
8376             return;
8377         }
8378         
8379         var cellIndex = cell.dom.cellIndex;
8380         var rowIndex = this.getRowIndex(row);
8381         
8382         // why??? - should these not be based on SelectionModel?
8383         if(this.cellSelection){
8384             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8385         }
8386         
8387         if(this.rowSelection){
8388             this.fireEvent('rowclick', this, row, rowIndex, e);
8389         }
8390         
8391         
8392     },
8393         
8394     onDblClick : function(e,el)
8395     {
8396         var cell = Roo.get(el);
8397         
8398         if(!cell || (!this.cellSelection && !this.rowSelection)){
8399             return;
8400         }
8401         
8402         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8403             cell = cell.findParent('td', false, true);
8404         }
8405         
8406         if(!cell || typeof(cell) == 'undefined'){
8407             return;
8408         }
8409         
8410         var row = cell.findParent('tr', false, true);
8411         
8412         if(!row || typeof(row) == 'undefined'){
8413             return;
8414         }
8415         
8416         var cellIndex = cell.dom.cellIndex;
8417         var rowIndex = this.getRowIndex(row);
8418         
8419         if(this.cellSelection){
8420             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8421         }
8422         
8423         if(this.rowSelection){
8424             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8425         }
8426     },
8427     
8428     sort : function(e,el)
8429     {
8430         var col = Roo.get(el);
8431         
8432         if(!col.hasClass('sortable')){
8433             return;
8434         }
8435         
8436         var sort = col.attr('sort');
8437         var dir = 'ASC';
8438         
8439         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8440             dir = 'DESC';
8441         }
8442         
8443         this.store.sortInfo = {field : sort, direction : dir};
8444         
8445         if (this.footer) {
8446             Roo.log("calling footer first");
8447             this.footer.onClick('first');
8448         } else {
8449         
8450             this.store.load({ params : { start : 0 } });
8451         }
8452     },
8453     
8454     renderHeader : function()
8455     {
8456         var header = {
8457             tag: 'thead',
8458             cn : []
8459         };
8460         
8461         var cm = this.cm;
8462         this.totalWidth = 0;
8463         
8464         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8465             
8466             var config = cm.config[i];
8467             
8468             var c = {
8469                 tag: 'th',
8470                 cls : 'x-hcol-' + i,
8471                 style : '',
8472                 html: cm.getColumnHeader(i)
8473             };
8474             
8475             var hh = '';
8476             
8477             if(typeof(config.sortable) != 'undefined' && config.sortable){
8478                 c.cls = 'sortable';
8479                 c.html = '<i class="glyphicon"></i>' + c.html;
8480             }
8481             
8482             // could use BS4 hidden-..-down 
8483             
8484             if(typeof(config.lgHeader) != 'undefined'){
8485                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8486             }
8487             
8488             if(typeof(config.mdHeader) != 'undefined'){
8489                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8490             }
8491             
8492             if(typeof(config.smHeader) != 'undefined'){
8493                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8494             }
8495             
8496             if(typeof(config.xsHeader) != 'undefined'){
8497                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8498             }
8499             
8500             if(hh.length){
8501                 c.html = hh;
8502             }
8503             
8504             if(typeof(config.tooltip) != 'undefined'){
8505                 c.tooltip = config.tooltip;
8506             }
8507             
8508             if(typeof(config.colspan) != 'undefined'){
8509                 c.colspan = config.colspan;
8510             }
8511             
8512             if(typeof(config.hidden) != 'undefined' && config.hidden){
8513                 c.style += ' display:none;';
8514             }
8515             
8516             if(typeof(config.dataIndex) != 'undefined'){
8517                 c.sort = config.dataIndex;
8518             }
8519             
8520            
8521             
8522             if(typeof(config.align) != 'undefined' && config.align.length){
8523                 c.style += ' text-align:' + config.align + ';';
8524             }
8525             
8526             if(typeof(config.width) != 'undefined'){
8527                 c.style += ' width:' + config.width + 'px;';
8528                 this.totalWidth += config.width;
8529             } else {
8530                 this.totalWidth += 100; // assume minimum of 100 per column?
8531             }
8532             
8533             if(typeof(config.cls) != 'undefined'){
8534                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8535             }
8536             
8537             ['xs','sm','md','lg'].map(function(size){
8538                 
8539                 if(typeof(config[size]) == 'undefined'){
8540                     return;
8541                 }
8542                  
8543                 if (!config[size]) { // 0 = hidden
8544                     // BS 4 '0' is treated as hide that column and below.
8545                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8546                     return;
8547                 }
8548                 
8549                 c.cls += ' col-' + size + '-' + config[size] + (
8550                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8551                 );
8552                 
8553                 
8554             });
8555             
8556             header.cn.push(c)
8557         }
8558         
8559         return header;
8560     },
8561     
8562     renderBody : function()
8563     {
8564         var body = {
8565             tag: 'tbody',
8566             cn : [
8567                 {
8568                     tag: 'tr',
8569                     cn : [
8570                         {
8571                             tag : 'td',
8572                             colspan :  this.cm.getColumnCount()
8573                         }
8574                     ]
8575                 }
8576             ]
8577         };
8578         
8579         return body;
8580     },
8581     
8582     renderFooter : function()
8583     {
8584         var footer = {
8585             tag: 'tfoot',
8586             cn : [
8587                 {
8588                     tag: 'tr',
8589                     cn : [
8590                         {
8591                             tag : 'td',
8592                             colspan :  this.cm.getColumnCount()
8593                         }
8594                     ]
8595                 }
8596             ]
8597         };
8598         
8599         return footer;
8600     },
8601     
8602     
8603     
8604     onLoad : function()
8605     {
8606 //        Roo.log('ds onload');
8607         this.clear();
8608         
8609         var _this = this;
8610         var cm = this.cm;
8611         var ds = this.store;
8612         
8613         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8614             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8615             if (_this.store.sortInfo) {
8616                     
8617                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8618                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8619                 }
8620                 
8621                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8622                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8623                 }
8624             }
8625         });
8626         
8627         var tbody =  this.mainBody;
8628               
8629         if(ds.getCount() > 0){
8630             ds.data.each(function(d,rowIndex){
8631                 var row =  this.renderRow(cm, ds, rowIndex);
8632                 
8633                 tbody.createChild(row);
8634                 
8635                 var _this = this;
8636                 
8637                 if(row.cellObjects.length){
8638                     Roo.each(row.cellObjects, function(r){
8639                         _this.renderCellObject(r);
8640                     })
8641                 }
8642                 
8643             }, this);
8644         }
8645         
8646         var tfoot = this.el.select('tfoot', true).first();
8647         
8648         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8649             
8650             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8651             
8652             var total = this.ds.getTotalCount();
8653             
8654             if(this.footer.pageSize < total){
8655                 this.mainFoot.show();
8656             }
8657         }
8658         
8659         Roo.each(this.el.select('tbody td', true).elements, function(e){
8660             e.on('mouseover', _this.onMouseover, _this);
8661         });
8662         
8663         Roo.each(this.el.select('tbody td', true).elements, function(e){
8664             e.on('mouseout', _this.onMouseout, _this);
8665         });
8666         this.fireEvent('rowsrendered', this);
8667         
8668         this.autoSize();
8669     },
8670     
8671     
8672     onUpdate : function(ds,record)
8673     {
8674         this.refreshRow(record);
8675         this.autoSize();
8676     },
8677     
8678     onRemove : function(ds, record, index, isUpdate){
8679         if(isUpdate !== true){
8680             this.fireEvent("beforerowremoved", this, index, record);
8681         }
8682         var bt = this.mainBody.dom;
8683         
8684         var rows = this.el.select('tbody > tr', true).elements;
8685         
8686         if(typeof(rows[index]) != 'undefined'){
8687             bt.removeChild(rows[index].dom);
8688         }
8689         
8690 //        if(bt.rows[index]){
8691 //            bt.removeChild(bt.rows[index]);
8692 //        }
8693         
8694         if(isUpdate !== true){
8695             //this.stripeRows(index);
8696             //this.syncRowHeights(index, index);
8697             //this.layout();
8698             this.fireEvent("rowremoved", this, index, record);
8699         }
8700     },
8701     
8702     onAdd : function(ds, records, rowIndex)
8703     {
8704         //Roo.log('on Add called');
8705         // - note this does not handle multiple adding very well..
8706         var bt = this.mainBody.dom;
8707         for (var i =0 ; i < records.length;i++) {
8708             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8709             //Roo.log(records[i]);
8710             //Roo.log(this.store.getAt(rowIndex+i));
8711             this.insertRow(this.store, rowIndex + i, false);
8712             return;
8713         }
8714         
8715     },
8716     
8717     
8718     refreshRow : function(record){
8719         var ds = this.store, index;
8720         if(typeof record == 'number'){
8721             index = record;
8722             record = ds.getAt(index);
8723         }else{
8724             index = ds.indexOf(record);
8725             if (index < 0) {
8726                 return; // should not happen - but seems to 
8727             }
8728         }
8729         this.insertRow(ds, index, true);
8730         this.autoSize();
8731         this.onRemove(ds, record, index+1, true);
8732         this.autoSize();
8733         //this.syncRowHeights(index, index);
8734         //this.layout();
8735         this.fireEvent("rowupdated", this, index, record);
8736     },
8737     
8738     insertRow : function(dm, rowIndex, isUpdate){
8739         
8740         if(!isUpdate){
8741             this.fireEvent("beforerowsinserted", this, rowIndex);
8742         }
8743             //var s = this.getScrollState();
8744         var row = this.renderRow(this.cm, this.store, rowIndex);
8745         // insert before rowIndex..
8746         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8747         
8748         var _this = this;
8749                 
8750         if(row.cellObjects.length){
8751             Roo.each(row.cellObjects, function(r){
8752                 _this.renderCellObject(r);
8753             })
8754         }
8755             
8756         if(!isUpdate){
8757             this.fireEvent("rowsinserted", this, rowIndex);
8758             //this.syncRowHeights(firstRow, lastRow);
8759             //this.stripeRows(firstRow);
8760             //this.layout();
8761         }
8762         
8763     },
8764     
8765     
8766     getRowDom : function(rowIndex)
8767     {
8768         var rows = this.el.select('tbody > tr', true).elements;
8769         
8770         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8771         
8772     },
8773     // returns the object tree for a tr..
8774   
8775     
8776     renderRow : function(cm, ds, rowIndex) 
8777     {
8778         var d = ds.getAt(rowIndex);
8779         
8780         var row = {
8781             tag : 'tr',
8782             cls : 'x-row-' + rowIndex,
8783             cn : []
8784         };
8785             
8786         var cellObjects = [];
8787         
8788         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8789             var config = cm.config[i];
8790             
8791             var renderer = cm.getRenderer(i);
8792             var value = '';
8793             var id = false;
8794             
8795             if(typeof(renderer) !== 'undefined'){
8796                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8797             }
8798             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8799             // and are rendered into the cells after the row is rendered - using the id for the element.
8800             
8801             if(typeof(value) === 'object'){
8802                 id = Roo.id();
8803                 cellObjects.push({
8804                     container : id,
8805                     cfg : value 
8806                 })
8807             }
8808             
8809             var rowcfg = {
8810                 record: d,
8811                 rowIndex : rowIndex,
8812                 colIndex : i,
8813                 rowClass : ''
8814             };
8815
8816             this.fireEvent('rowclass', this, rowcfg);
8817             
8818             var td = {
8819                 tag: 'td',
8820                 cls : rowcfg.rowClass + ' x-col-' + i,
8821                 style: '',
8822                 html: (typeof(value) === 'object') ? '' : value
8823             };
8824             
8825             if (id) {
8826                 td.id = id;
8827             }
8828             
8829             if(typeof(config.colspan) != 'undefined'){
8830                 td.colspan = config.colspan;
8831             }
8832             
8833             if(typeof(config.hidden) != 'undefined' && config.hidden){
8834                 td.style += ' display:none;';
8835             }
8836             
8837             if(typeof(config.align) != 'undefined' && config.align.length){
8838                 td.style += ' text-align:' + config.align + ';';
8839             }
8840             if(typeof(config.valign) != 'undefined' && config.valign.length){
8841                 td.style += ' vertical-align:' + config.valign + ';';
8842             }
8843             
8844             if(typeof(config.width) != 'undefined'){
8845                 td.style += ' width:' +  config.width + 'px;';
8846             }
8847             
8848             if(typeof(config.cursor) != 'undefined'){
8849                 td.style += ' cursor:' +  config.cursor + ';';
8850             }
8851             
8852             if(typeof(config.cls) != 'undefined'){
8853                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8854             }
8855             
8856             ['xs','sm','md','lg'].map(function(size){
8857                 
8858                 if(typeof(config[size]) == 'undefined'){
8859                     return;
8860                 }
8861                 
8862                 
8863                   
8864                 if (!config[size]) { // 0 = hidden
8865                     // BS 4 '0' is treated as hide that column and below.
8866                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8867                     return;
8868                 }
8869                 
8870                 td.cls += ' col-' + size + '-' + config[size] + (
8871                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8872                 );
8873                  
8874
8875             });
8876             
8877             row.cn.push(td);
8878            
8879         }
8880         
8881         row.cellObjects = cellObjects;
8882         
8883         return row;
8884           
8885     },
8886     
8887     
8888     
8889     onBeforeLoad : function()
8890     {
8891         
8892     },
8893      /**
8894      * Remove all rows
8895      */
8896     clear : function()
8897     {
8898         this.el.select('tbody', true).first().dom.innerHTML = '';
8899     },
8900     /**
8901      * Show or hide a row.
8902      * @param {Number} rowIndex to show or hide
8903      * @param {Boolean} state hide
8904      */
8905     setRowVisibility : function(rowIndex, state)
8906     {
8907         var bt = this.mainBody.dom;
8908         
8909         var rows = this.el.select('tbody > tr', true).elements;
8910         
8911         if(typeof(rows[rowIndex]) == 'undefined'){
8912             return;
8913         }
8914         rows[rowIndex].dom.style.display = state ? '' : 'none';
8915     },
8916     
8917     
8918     getSelectionModel : function(){
8919         if(!this.selModel){
8920             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8921         }
8922         return this.selModel;
8923     },
8924     /*
8925      * Render the Roo.bootstrap object from renderder
8926      */
8927     renderCellObject : function(r)
8928     {
8929         var _this = this;
8930         
8931         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8932         
8933         var t = r.cfg.render(r.container);
8934         
8935         if(r.cfg.cn){
8936             Roo.each(r.cfg.cn, function(c){
8937                 var child = {
8938                     container: t.getChildContainer(),
8939                     cfg: c
8940                 };
8941                 _this.renderCellObject(child);
8942             })
8943         }
8944     },
8945     
8946     getRowIndex : function(row)
8947     {
8948         var rowIndex = -1;
8949         
8950         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8951             if(el != row){
8952                 return;
8953             }
8954             
8955             rowIndex = index;
8956         });
8957         
8958         return rowIndex;
8959     },
8960      /**
8961      * Returns the grid's underlying element = used by panel.Grid
8962      * @return {Element} The element
8963      */
8964     getGridEl : function(){
8965         return this.el;
8966     },
8967      /**
8968      * Forces a resize - used by panel.Grid
8969      * @return {Element} The element
8970      */
8971     autoSize : function()
8972     {
8973         //var ctr = Roo.get(this.container.dom.parentElement);
8974         var ctr = Roo.get(this.el.dom);
8975         
8976         var thd = this.getGridEl().select('thead',true).first();
8977         var tbd = this.getGridEl().select('tbody', true).first();
8978         var tfd = this.getGridEl().select('tfoot', true).first();
8979         
8980         var cw = ctr.getWidth();
8981         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8982         
8983         if (tbd) {
8984             
8985             tbd.setWidth(ctr.getWidth());
8986             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8987             // this needs fixing for various usage - currently only hydra job advers I think..
8988             //tdb.setHeight(
8989             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8990             //); 
8991             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8992             cw -= barsize;
8993         }
8994         cw = Math.max(cw, this.totalWidth);
8995         this.getGridEl().select('tbody tr',true).setWidth(cw);
8996         
8997         // resize 'expandable coloumn?
8998         
8999         return; // we doe not have a view in this design..
9000         
9001     },
9002     onBodyScroll: function()
9003     {
9004         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9005         if(this.mainHead){
9006             this.mainHead.setStyle({
9007                 'position' : 'relative',
9008                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9009             });
9010         }
9011         
9012         if(this.lazyLoad){
9013             
9014             var scrollHeight = this.mainBody.dom.scrollHeight;
9015             
9016             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9017             
9018             var height = this.mainBody.getHeight();
9019             
9020             if(scrollHeight - height == scrollTop) {
9021                 
9022                 var total = this.ds.getTotalCount();
9023                 
9024                 if(this.footer.cursor + this.footer.pageSize < total){
9025                     
9026                     this.footer.ds.load({
9027                         params : {
9028                             start : this.footer.cursor + this.footer.pageSize,
9029                             limit : this.footer.pageSize
9030                         },
9031                         add : true
9032                     });
9033                 }
9034             }
9035             
9036         }
9037     },
9038     
9039     onHeaderChange : function()
9040     {
9041         var header = this.renderHeader();
9042         var table = this.el.select('table', true).first();
9043         
9044         this.mainHead.remove();
9045         this.mainHead = table.createChild(header, this.mainBody, false);
9046     },
9047     
9048     onHiddenChange : function(colModel, colIndex, hidden)
9049     {
9050         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9051         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9052         
9053         this.CSS.updateRule(thSelector, "display", "");
9054         this.CSS.updateRule(tdSelector, "display", "");
9055         
9056         if(hidden){
9057             this.CSS.updateRule(thSelector, "display", "none");
9058             this.CSS.updateRule(tdSelector, "display", "none");
9059         }
9060         
9061         this.onHeaderChange();
9062         this.onLoad();
9063     },
9064     
9065     setColumnWidth: function(col_index, width)
9066     {
9067         // width = "md-2 xs-2..."
9068         if(!this.colModel.config[col_index]) {
9069             return;
9070         }
9071         
9072         var w = width.split(" ");
9073         
9074         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9075         
9076         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9077         
9078         
9079         for(var j = 0; j < w.length; j++) {
9080             
9081             if(!w[j]) {
9082                 continue;
9083             }
9084             
9085             var size_cls = w[j].split("-");
9086             
9087             if(!Number.isInteger(size_cls[1] * 1)) {
9088                 continue;
9089             }
9090             
9091             if(!this.colModel.config[col_index][size_cls[0]]) {
9092                 continue;
9093             }
9094             
9095             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9096                 continue;
9097             }
9098             
9099             h_row[0].classList.replace(
9100                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9101                 "col-"+size_cls[0]+"-"+size_cls[1]
9102             );
9103             
9104             for(var i = 0; i < rows.length; i++) {
9105                 
9106                 var size_cls = w[j].split("-");
9107                 
9108                 if(!Number.isInteger(size_cls[1] * 1)) {
9109                     continue;
9110                 }
9111                 
9112                 if(!this.colModel.config[col_index][size_cls[0]]) {
9113                     continue;
9114                 }
9115                 
9116                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9117                     continue;
9118                 }
9119                 
9120                 rows[i].classList.replace(
9121                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9122                     "col-"+size_cls[0]+"-"+size_cls[1]
9123                 );
9124             }
9125             
9126             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9127         }
9128     }
9129 });
9130
9131  
9132
9133  /*
9134  * - LGPL
9135  *
9136  * table cell
9137  * 
9138  */
9139
9140 /**
9141  * @class Roo.bootstrap.TableCell
9142  * @extends Roo.bootstrap.Component
9143  * Bootstrap TableCell class
9144  * @cfg {String} html cell contain text
9145  * @cfg {String} cls cell class
9146  * @cfg {String} tag cell tag (td|th) default td
9147  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9148  * @cfg {String} align Aligns the content in a cell
9149  * @cfg {String} axis Categorizes cells
9150  * @cfg {String} bgcolor Specifies the background color of a cell
9151  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9152  * @cfg {Number} colspan Specifies the number of columns a cell should span
9153  * @cfg {String} headers Specifies one or more header cells a cell is related to
9154  * @cfg {Number} height Sets the height of a cell
9155  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9156  * @cfg {Number} rowspan Sets the number of rows a cell should span
9157  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9158  * @cfg {String} valign Vertical aligns the content in a cell
9159  * @cfg {Number} width Specifies the width of a cell
9160  * 
9161  * @constructor
9162  * Create a new TableCell
9163  * @param {Object} config The config object
9164  */
9165
9166 Roo.bootstrap.TableCell = function(config){
9167     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9168 };
9169
9170 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9171     
9172     html: false,
9173     cls: false,
9174     tag: false,
9175     abbr: false,
9176     align: false,
9177     axis: false,
9178     bgcolor: false,
9179     charoff: false,
9180     colspan: false,
9181     headers: false,
9182     height: false,
9183     nowrap: false,
9184     rowspan: false,
9185     scope: false,
9186     valign: false,
9187     width: false,
9188     
9189     
9190     getAutoCreate : function(){
9191         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9192         
9193         cfg = {
9194             tag: 'td'
9195         };
9196         
9197         if(this.tag){
9198             cfg.tag = this.tag;
9199         }
9200         
9201         if (this.html) {
9202             cfg.html=this.html
9203         }
9204         if (this.cls) {
9205             cfg.cls=this.cls
9206         }
9207         if (this.abbr) {
9208             cfg.abbr=this.abbr
9209         }
9210         if (this.align) {
9211             cfg.align=this.align
9212         }
9213         if (this.axis) {
9214             cfg.axis=this.axis
9215         }
9216         if (this.bgcolor) {
9217             cfg.bgcolor=this.bgcolor
9218         }
9219         if (this.charoff) {
9220             cfg.charoff=this.charoff
9221         }
9222         if (this.colspan) {
9223             cfg.colspan=this.colspan
9224         }
9225         if (this.headers) {
9226             cfg.headers=this.headers
9227         }
9228         if (this.height) {
9229             cfg.height=this.height
9230         }
9231         if (this.nowrap) {
9232             cfg.nowrap=this.nowrap
9233         }
9234         if (this.rowspan) {
9235             cfg.rowspan=this.rowspan
9236         }
9237         if (this.scope) {
9238             cfg.scope=this.scope
9239         }
9240         if (this.valign) {
9241             cfg.valign=this.valign
9242         }
9243         if (this.width) {
9244             cfg.width=this.width
9245         }
9246         
9247         
9248         return cfg;
9249     }
9250    
9251 });
9252
9253  
9254
9255  /*
9256  * - LGPL
9257  *
9258  * table row
9259  * 
9260  */
9261
9262 /**
9263  * @class Roo.bootstrap.TableRow
9264  * @extends Roo.bootstrap.Component
9265  * Bootstrap TableRow class
9266  * @cfg {String} cls row class
9267  * @cfg {String} align Aligns the content in a table row
9268  * @cfg {String} bgcolor Specifies a background color for a table row
9269  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9270  * @cfg {String} valign Vertical aligns the content in a table row
9271  * 
9272  * @constructor
9273  * Create a new TableRow
9274  * @param {Object} config The config object
9275  */
9276
9277 Roo.bootstrap.TableRow = function(config){
9278     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9279 };
9280
9281 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9282     
9283     cls: false,
9284     align: false,
9285     bgcolor: false,
9286     charoff: false,
9287     valign: false,
9288     
9289     getAutoCreate : function(){
9290         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9291         
9292         cfg = {
9293             tag: 'tr'
9294         };
9295             
9296         if(this.cls){
9297             cfg.cls = this.cls;
9298         }
9299         if(this.align){
9300             cfg.align = this.align;
9301         }
9302         if(this.bgcolor){
9303             cfg.bgcolor = this.bgcolor;
9304         }
9305         if(this.charoff){
9306             cfg.charoff = this.charoff;
9307         }
9308         if(this.valign){
9309             cfg.valign = this.valign;
9310         }
9311         
9312         return cfg;
9313     }
9314    
9315 });
9316
9317  
9318
9319  /*
9320  * - LGPL
9321  *
9322  * table body
9323  * 
9324  */
9325
9326 /**
9327  * @class Roo.bootstrap.TableBody
9328  * @extends Roo.bootstrap.Component
9329  * Bootstrap TableBody class
9330  * @cfg {String} cls element class
9331  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9332  * @cfg {String} align Aligns the content inside the element
9333  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9334  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9335  * 
9336  * @constructor
9337  * Create a new TableBody
9338  * @param {Object} config The config object
9339  */
9340
9341 Roo.bootstrap.TableBody = function(config){
9342     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9343 };
9344
9345 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9346     
9347     cls: false,
9348     tag: false,
9349     align: false,
9350     charoff: false,
9351     valign: false,
9352     
9353     getAutoCreate : function(){
9354         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9355         
9356         cfg = {
9357             tag: 'tbody'
9358         };
9359             
9360         if (this.cls) {
9361             cfg.cls=this.cls
9362         }
9363         if(this.tag){
9364             cfg.tag = this.tag;
9365         }
9366         
9367         if(this.align){
9368             cfg.align = this.align;
9369         }
9370         if(this.charoff){
9371             cfg.charoff = this.charoff;
9372         }
9373         if(this.valign){
9374             cfg.valign = this.valign;
9375         }
9376         
9377         return cfg;
9378     }
9379     
9380     
9381 //    initEvents : function()
9382 //    {
9383 //        
9384 //        if(!this.store){
9385 //            return;
9386 //        }
9387 //        
9388 //        this.store = Roo.factory(this.store, Roo.data);
9389 //        this.store.on('load', this.onLoad, this);
9390 //        
9391 //        this.store.load();
9392 //        
9393 //    },
9394 //    
9395 //    onLoad: function () 
9396 //    {   
9397 //        this.fireEvent('load', this);
9398 //    }
9399 //    
9400 //   
9401 });
9402
9403  
9404
9405  /*
9406  * Based on:
9407  * Ext JS Library 1.1.1
9408  * Copyright(c) 2006-2007, Ext JS, LLC.
9409  *
9410  * Originally Released Under LGPL - original licence link has changed is not relivant.
9411  *
9412  * Fork - LGPL
9413  * <script type="text/javascript">
9414  */
9415
9416 // as we use this in bootstrap.
9417 Roo.namespace('Roo.form');
9418  /**
9419  * @class Roo.form.Action
9420  * Internal Class used to handle form actions
9421  * @constructor
9422  * @param {Roo.form.BasicForm} el The form element or its id
9423  * @param {Object} config Configuration options
9424  */
9425
9426  
9427  
9428 // define the action interface
9429 Roo.form.Action = function(form, options){
9430     this.form = form;
9431     this.options = options || {};
9432 };
9433 /**
9434  * Client Validation Failed
9435  * @const 
9436  */
9437 Roo.form.Action.CLIENT_INVALID = 'client';
9438 /**
9439  * Server Validation Failed
9440  * @const 
9441  */
9442 Roo.form.Action.SERVER_INVALID = 'server';
9443  /**
9444  * Connect to Server Failed
9445  * @const 
9446  */
9447 Roo.form.Action.CONNECT_FAILURE = 'connect';
9448 /**
9449  * Reading Data from Server Failed
9450  * @const 
9451  */
9452 Roo.form.Action.LOAD_FAILURE = 'load';
9453
9454 Roo.form.Action.prototype = {
9455     type : 'default',
9456     failureType : undefined,
9457     response : undefined,
9458     result : undefined,
9459
9460     // interface method
9461     run : function(options){
9462
9463     },
9464
9465     // interface method
9466     success : function(response){
9467
9468     },
9469
9470     // interface method
9471     handleResponse : function(response){
9472
9473     },
9474
9475     // default connection failure
9476     failure : function(response){
9477         
9478         this.response = response;
9479         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9480         this.form.afterAction(this, false);
9481     },
9482
9483     processResponse : function(response){
9484         this.response = response;
9485         if(!response.responseText){
9486             return true;
9487         }
9488         this.result = this.handleResponse(response);
9489         return this.result;
9490     },
9491
9492     // utility functions used internally
9493     getUrl : function(appendParams){
9494         var url = this.options.url || this.form.url || this.form.el.dom.action;
9495         if(appendParams){
9496             var p = this.getParams();
9497             if(p){
9498                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9499             }
9500         }
9501         return url;
9502     },
9503
9504     getMethod : function(){
9505         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9506     },
9507
9508     getParams : function(){
9509         var bp = this.form.baseParams;
9510         var p = this.options.params;
9511         if(p){
9512             if(typeof p == "object"){
9513                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9514             }else if(typeof p == 'string' && bp){
9515                 p += '&' + Roo.urlEncode(bp);
9516             }
9517         }else if(bp){
9518             p = Roo.urlEncode(bp);
9519         }
9520         return p;
9521     },
9522
9523     createCallback : function(){
9524         return {
9525             success: this.success,
9526             failure: this.failure,
9527             scope: this,
9528             timeout: (this.form.timeout*1000),
9529             upload: this.form.fileUpload ? this.success : undefined
9530         };
9531     }
9532 };
9533
9534 Roo.form.Action.Submit = function(form, options){
9535     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9536 };
9537
9538 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9539     type : 'submit',
9540
9541     haveProgress : false,
9542     uploadComplete : false,
9543     
9544     // uploadProgress indicator.
9545     uploadProgress : function()
9546     {
9547         if (!this.form.progressUrl) {
9548             return;
9549         }
9550         
9551         if (!this.haveProgress) {
9552             Roo.MessageBox.progress("Uploading", "Uploading");
9553         }
9554         if (this.uploadComplete) {
9555            Roo.MessageBox.hide();
9556            return;
9557         }
9558         
9559         this.haveProgress = true;
9560    
9561         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9562         
9563         var c = new Roo.data.Connection();
9564         c.request({
9565             url : this.form.progressUrl,
9566             params: {
9567                 id : uid
9568             },
9569             method: 'GET',
9570             success : function(req){
9571                //console.log(data);
9572                 var rdata = false;
9573                 var edata;
9574                 try  {
9575                    rdata = Roo.decode(req.responseText)
9576                 } catch (e) {
9577                     Roo.log("Invalid data from server..");
9578                     Roo.log(edata);
9579                     return;
9580                 }
9581                 if (!rdata || !rdata.success) {
9582                     Roo.log(rdata);
9583                     Roo.MessageBox.alert(Roo.encode(rdata));
9584                     return;
9585                 }
9586                 var data = rdata.data;
9587                 
9588                 if (this.uploadComplete) {
9589                    Roo.MessageBox.hide();
9590                    return;
9591                 }
9592                    
9593                 if (data){
9594                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9595                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9596                     );
9597                 }
9598                 this.uploadProgress.defer(2000,this);
9599             },
9600        
9601             failure: function(data) {
9602                 Roo.log('progress url failed ');
9603                 Roo.log(data);
9604             },
9605             scope : this
9606         });
9607            
9608     },
9609     
9610     
9611     run : function()
9612     {
9613         // run get Values on the form, so it syncs any secondary forms.
9614         this.form.getValues();
9615         
9616         var o = this.options;
9617         var method = this.getMethod();
9618         var isPost = method == 'POST';
9619         if(o.clientValidation === false || this.form.isValid()){
9620             
9621             if (this.form.progressUrl) {
9622                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9623                     (new Date() * 1) + '' + Math.random());
9624                     
9625             } 
9626             
9627             
9628             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9629                 form:this.form.el.dom,
9630                 url:this.getUrl(!isPost),
9631                 method: method,
9632                 params:isPost ? this.getParams() : null,
9633                 isUpload: this.form.fileUpload,
9634                 formData : this.form.formData
9635             }));
9636             
9637             this.uploadProgress();
9638
9639         }else if (o.clientValidation !== false){ // client validation failed
9640             this.failureType = Roo.form.Action.CLIENT_INVALID;
9641             this.form.afterAction(this, false);
9642         }
9643     },
9644
9645     success : function(response)
9646     {
9647         this.uploadComplete= true;
9648         if (this.haveProgress) {
9649             Roo.MessageBox.hide();
9650         }
9651         
9652         
9653         var result = this.processResponse(response);
9654         if(result === true || result.success){
9655             this.form.afterAction(this, true);
9656             return;
9657         }
9658         if(result.errors){
9659             this.form.markInvalid(result.errors);
9660             this.failureType = Roo.form.Action.SERVER_INVALID;
9661         }
9662         this.form.afterAction(this, false);
9663     },
9664     failure : function(response)
9665     {
9666         this.uploadComplete= true;
9667         if (this.haveProgress) {
9668             Roo.MessageBox.hide();
9669         }
9670         
9671         this.response = response;
9672         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9673         this.form.afterAction(this, false);
9674     },
9675     
9676     handleResponse : function(response){
9677         if(this.form.errorReader){
9678             var rs = this.form.errorReader.read(response);
9679             var errors = [];
9680             if(rs.records){
9681                 for(var i = 0, len = rs.records.length; i < len; i++) {
9682                     var r = rs.records[i];
9683                     errors[i] = r.data;
9684                 }
9685             }
9686             if(errors.length < 1){
9687                 errors = null;
9688             }
9689             return {
9690                 success : rs.success,
9691                 errors : errors
9692             };
9693         }
9694         var ret = false;
9695         try {
9696             ret = Roo.decode(response.responseText);
9697         } catch (e) {
9698             ret = {
9699                 success: false,
9700                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9701                 errors : []
9702             };
9703         }
9704         return ret;
9705         
9706     }
9707 });
9708
9709
9710 Roo.form.Action.Load = function(form, options){
9711     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9712     this.reader = this.form.reader;
9713 };
9714
9715 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9716     type : 'load',
9717
9718     run : function(){
9719         
9720         Roo.Ajax.request(Roo.apply(
9721                 this.createCallback(), {
9722                     method:this.getMethod(),
9723                     url:this.getUrl(false),
9724                     params:this.getParams()
9725         }));
9726     },
9727
9728     success : function(response){
9729         
9730         var result = this.processResponse(response);
9731         if(result === true || !result.success || !result.data){
9732             this.failureType = Roo.form.Action.LOAD_FAILURE;
9733             this.form.afterAction(this, false);
9734             return;
9735         }
9736         this.form.clearInvalid();
9737         this.form.setValues(result.data);
9738         this.form.afterAction(this, true);
9739     },
9740
9741     handleResponse : function(response){
9742         if(this.form.reader){
9743             var rs = this.form.reader.read(response);
9744             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9745             return {
9746                 success : rs.success,
9747                 data : data
9748             };
9749         }
9750         return Roo.decode(response.responseText);
9751     }
9752 });
9753
9754 Roo.form.Action.ACTION_TYPES = {
9755     'load' : Roo.form.Action.Load,
9756     'submit' : Roo.form.Action.Submit
9757 };/*
9758  * - LGPL
9759  *
9760  * form
9761  *
9762  */
9763
9764 /**
9765  * @class Roo.bootstrap.Form
9766  * @extends Roo.bootstrap.Component
9767  * Bootstrap Form class
9768  * @cfg {String} method  GET | POST (default POST)
9769  * @cfg {String} labelAlign top | left (default top)
9770  * @cfg {String} align left  | right - for navbars
9771  * @cfg {Boolean} loadMask load mask when submit (default true)
9772
9773  *
9774  * @constructor
9775  * Create a new Form
9776  * @param {Object} config The config object
9777  */
9778
9779
9780 Roo.bootstrap.Form = function(config){
9781     
9782     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9783     
9784     Roo.bootstrap.Form.popover.apply();
9785     
9786     this.addEvents({
9787         /**
9788          * @event clientvalidation
9789          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9790          * @param {Form} this
9791          * @param {Boolean} valid true if the form has passed client-side validation
9792          */
9793         clientvalidation: true,
9794         /**
9795          * @event beforeaction
9796          * Fires before any action is performed. Return false to cancel the action.
9797          * @param {Form} this
9798          * @param {Action} action The action to be performed
9799          */
9800         beforeaction: true,
9801         /**
9802          * @event actionfailed
9803          * Fires when an action fails.
9804          * @param {Form} this
9805          * @param {Action} action The action that failed
9806          */
9807         actionfailed : true,
9808         /**
9809          * @event actioncomplete
9810          * Fires when an action is completed.
9811          * @param {Form} this
9812          * @param {Action} action The action that completed
9813          */
9814         actioncomplete : true
9815     });
9816 };
9817
9818 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9819
9820      /**
9821      * @cfg {String} method
9822      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9823      */
9824     method : 'POST',
9825     /**
9826      * @cfg {String} url
9827      * The URL to use for form actions if one isn't supplied in the action options.
9828      */
9829     /**
9830      * @cfg {Boolean} fileUpload
9831      * Set to true if this form is a file upload.
9832      */
9833
9834     /**
9835      * @cfg {Object} baseParams
9836      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9837      */
9838
9839     /**
9840      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9841      */
9842     timeout: 30,
9843     /**
9844      * @cfg {Sting} align (left|right) for navbar forms
9845      */
9846     align : 'left',
9847
9848     // private
9849     activeAction : null,
9850
9851     /**
9852      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9853      * element by passing it or its id or mask the form itself by passing in true.
9854      * @type Mixed
9855      */
9856     waitMsgTarget : false,
9857
9858     loadMask : true,
9859     
9860     /**
9861      * @cfg {Boolean} errorMask (true|false) default false
9862      */
9863     errorMask : false,
9864     
9865     /**
9866      * @cfg {Number} maskOffset Default 100
9867      */
9868     maskOffset : 100,
9869     
9870     /**
9871      * @cfg {Boolean} maskBody
9872      */
9873     maskBody : false,
9874
9875     getAutoCreate : function(){
9876
9877         var cfg = {
9878             tag: 'form',
9879             method : this.method || 'POST',
9880             id : this.id || Roo.id(),
9881             cls : ''
9882         };
9883         if (this.parent().xtype.match(/^Nav/)) {
9884             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9885
9886         }
9887
9888         if (this.labelAlign == 'left' ) {
9889             cfg.cls += ' form-horizontal';
9890         }
9891
9892
9893         return cfg;
9894     },
9895     initEvents : function()
9896     {
9897         this.el.on('submit', this.onSubmit, this);
9898         // this was added as random key presses on the form where triggering form submit.
9899         this.el.on('keypress', function(e) {
9900             if (e.getCharCode() != 13) {
9901                 return true;
9902             }
9903             // we might need to allow it for textareas.. and some other items.
9904             // check e.getTarget().
9905
9906             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9907                 return true;
9908             }
9909
9910             Roo.log("keypress blocked");
9911
9912             e.preventDefault();
9913             return false;
9914         });
9915         
9916     },
9917     // private
9918     onSubmit : function(e){
9919         e.stopEvent();
9920     },
9921
9922      /**
9923      * Returns true if client-side validation on the form is successful.
9924      * @return Boolean
9925      */
9926     isValid : function(){
9927         var items = this.getItems();
9928         var valid = true;
9929         var target = false;
9930         
9931         items.each(function(f){
9932             
9933             if(f.validate()){
9934                 return;
9935             }
9936             
9937             Roo.log('invalid field: ' + f.name);
9938             
9939             valid = false;
9940
9941             if(!target && f.el.isVisible(true)){
9942                 target = f;
9943             }
9944            
9945         });
9946         
9947         if(this.errorMask && !valid){
9948             Roo.bootstrap.Form.popover.mask(this, target);
9949         }
9950         
9951         return valid;
9952     },
9953     
9954     /**
9955      * Returns true if any fields in this form have changed since their original load.
9956      * @return Boolean
9957      */
9958     isDirty : function(){
9959         var dirty = false;
9960         var items = this.getItems();
9961         items.each(function(f){
9962            if(f.isDirty()){
9963                dirty = true;
9964                return false;
9965            }
9966            return true;
9967         });
9968         return dirty;
9969     },
9970      /**
9971      * Performs a predefined action (submit or load) or custom actions you define on this form.
9972      * @param {String} actionName The name of the action type
9973      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9974      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9975      * accept other config options):
9976      * <pre>
9977 Property          Type             Description
9978 ----------------  ---------------  ----------------------------------------------------------------------------------
9979 url               String           The url for the action (defaults to the form's url)
9980 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9981 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9982 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9983                                    validate the form on the client (defaults to false)
9984      * </pre>
9985      * @return {BasicForm} this
9986      */
9987     doAction : function(action, options){
9988         if(typeof action == 'string'){
9989             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9990         }
9991         if(this.fireEvent('beforeaction', this, action) !== false){
9992             this.beforeAction(action);
9993             action.run.defer(100, action);
9994         }
9995         return this;
9996     },
9997
9998     // private
9999     beforeAction : function(action){
10000         var o = action.options;
10001         
10002         if(this.loadMask){
10003             
10004             if(this.maskBody){
10005                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10006             } else {
10007                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10008             }
10009         }
10010         // not really supported yet.. ??
10011
10012         //if(this.waitMsgTarget === true){
10013         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10014         //}else if(this.waitMsgTarget){
10015         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10016         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10017         //}else {
10018         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10019        // }
10020
10021     },
10022
10023     // private
10024     afterAction : function(action, success){
10025         this.activeAction = null;
10026         var o = action.options;
10027
10028         if(this.loadMask){
10029             
10030             if(this.maskBody){
10031                 Roo.get(document.body).unmask();
10032             } else {
10033                 this.el.unmask();
10034             }
10035         }
10036         
10037         //if(this.waitMsgTarget === true){
10038 //            this.el.unmask();
10039         //}else if(this.waitMsgTarget){
10040         //    this.waitMsgTarget.unmask();
10041         //}else{
10042         //    Roo.MessageBox.updateProgress(1);
10043         //    Roo.MessageBox.hide();
10044        // }
10045         //
10046         if(success){
10047             if(o.reset){
10048                 this.reset();
10049             }
10050             Roo.callback(o.success, o.scope, [this, action]);
10051             this.fireEvent('actioncomplete', this, action);
10052
10053         }else{
10054
10055             // failure condition..
10056             // we have a scenario where updates need confirming.
10057             // eg. if a locking scenario exists..
10058             // we look for { errors : { needs_confirm : true }} in the response.
10059             if (
10060                 (typeof(action.result) != 'undefined')  &&
10061                 (typeof(action.result.errors) != 'undefined')  &&
10062                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10063            ){
10064                 var _t = this;
10065                 Roo.log("not supported yet");
10066                  /*
10067
10068                 Roo.MessageBox.confirm(
10069                     "Change requires confirmation",
10070                     action.result.errorMsg,
10071                     function(r) {
10072                         if (r != 'yes') {
10073                             return;
10074                         }
10075                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10076                     }
10077
10078                 );
10079                 */
10080
10081
10082                 return;
10083             }
10084
10085             Roo.callback(o.failure, o.scope, [this, action]);
10086             // show an error message if no failed handler is set..
10087             if (!this.hasListener('actionfailed')) {
10088                 Roo.log("need to add dialog support");
10089                 /*
10090                 Roo.MessageBox.alert("Error",
10091                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10092                         action.result.errorMsg :
10093                         "Saving Failed, please check your entries or try again"
10094                 );
10095                 */
10096             }
10097
10098             this.fireEvent('actionfailed', this, action);
10099         }
10100
10101     },
10102     /**
10103      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10104      * @param {String} id The value to search for
10105      * @return Field
10106      */
10107     findField : function(id){
10108         var items = this.getItems();
10109         var field = items.get(id);
10110         if(!field){
10111              items.each(function(f){
10112                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10113                     field = f;
10114                     return false;
10115                 }
10116                 return true;
10117             });
10118         }
10119         return field || null;
10120     },
10121      /**
10122      * Mark fields in this form invalid in bulk.
10123      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10124      * @return {BasicForm} this
10125      */
10126     markInvalid : function(errors){
10127         if(errors instanceof Array){
10128             for(var i = 0, len = errors.length; i < len; i++){
10129                 var fieldError = errors[i];
10130                 var f = this.findField(fieldError.id);
10131                 if(f){
10132                     f.markInvalid(fieldError.msg);
10133                 }
10134             }
10135         }else{
10136             var field, id;
10137             for(id in errors){
10138                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10139                     field.markInvalid(errors[id]);
10140                 }
10141             }
10142         }
10143         //Roo.each(this.childForms || [], function (f) {
10144         //    f.markInvalid(errors);
10145         //});
10146
10147         return this;
10148     },
10149
10150     /**
10151      * Set values for fields in this form in bulk.
10152      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10153      * @return {BasicForm} this
10154      */
10155     setValues : function(values){
10156         if(values instanceof Array){ // array of objects
10157             for(var i = 0, len = values.length; i < len; i++){
10158                 var v = values[i];
10159                 var f = this.findField(v.id);
10160                 if(f){
10161                     f.setValue(v.value);
10162                     if(this.trackResetOnLoad){
10163                         f.originalValue = f.getValue();
10164                     }
10165                 }
10166             }
10167         }else{ // object hash
10168             var field, id;
10169             for(id in values){
10170                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10171
10172                     if (field.setFromData &&
10173                         field.valueField &&
10174                         field.displayField &&
10175                         // combos' with local stores can
10176                         // be queried via setValue()
10177                         // to set their value..
10178                         (field.store && !field.store.isLocal)
10179                         ) {
10180                         // it's a combo
10181                         var sd = { };
10182                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10183                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10184                         field.setFromData(sd);
10185
10186                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10187                         
10188                         field.setFromData(values);
10189                         
10190                     } else {
10191                         field.setValue(values[id]);
10192                     }
10193
10194
10195                     if(this.trackResetOnLoad){
10196                         field.originalValue = field.getValue();
10197                     }
10198                 }
10199             }
10200         }
10201
10202         //Roo.each(this.childForms || [], function (f) {
10203         //    f.setValues(values);
10204         //});
10205
10206         return this;
10207     },
10208
10209     /**
10210      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10211      * they are returned as an array.
10212      * @param {Boolean} asString
10213      * @return {Object}
10214      */
10215     getValues : function(asString){
10216         //if (this.childForms) {
10217             // copy values from the child forms
10218         //    Roo.each(this.childForms, function (f) {
10219         //        this.setValues(f.getValues());
10220         //    }, this);
10221         //}
10222
10223
10224
10225         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10226         if(asString === true){
10227             return fs;
10228         }
10229         return Roo.urlDecode(fs);
10230     },
10231
10232     /**
10233      * Returns the fields in this form as an object with key/value pairs.
10234      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10235      * @return {Object}
10236      */
10237     getFieldValues : function(with_hidden)
10238     {
10239         var items = this.getItems();
10240         var ret = {};
10241         items.each(function(f){
10242             
10243             if (!f.getName()) {
10244                 return;
10245             }
10246             
10247             var v = f.getValue();
10248             
10249             if (f.inputType =='radio') {
10250                 if (typeof(ret[f.getName()]) == 'undefined') {
10251                     ret[f.getName()] = ''; // empty..
10252                 }
10253
10254                 if (!f.el.dom.checked) {
10255                     return;
10256
10257                 }
10258                 v = f.el.dom.value;
10259
10260             }
10261             
10262             if(f.xtype == 'MoneyField'){
10263                 ret[f.currencyName] = f.getCurrency();
10264             }
10265
10266             // not sure if this supported any more..
10267             if ((typeof(v) == 'object') && f.getRawValue) {
10268                 v = f.getRawValue() ; // dates..
10269             }
10270             // combo boxes where name != hiddenName...
10271             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10272                 ret[f.name] = f.getRawValue();
10273             }
10274             ret[f.getName()] = v;
10275         });
10276
10277         return ret;
10278     },
10279
10280     /**
10281      * Clears all invalid messages in this form.
10282      * @return {BasicForm} this
10283      */
10284     clearInvalid : function(){
10285         var items = this.getItems();
10286
10287         items.each(function(f){
10288            f.clearInvalid();
10289         });
10290
10291         return this;
10292     },
10293
10294     /**
10295      * Resets this form.
10296      * @return {BasicForm} this
10297      */
10298     reset : function(){
10299         var items = this.getItems();
10300         items.each(function(f){
10301             f.reset();
10302         });
10303
10304         Roo.each(this.childForms || [], function (f) {
10305             f.reset();
10306         });
10307
10308
10309         return this;
10310     },
10311     
10312     getItems : function()
10313     {
10314         var r=new Roo.util.MixedCollection(false, function(o){
10315             return o.id || (o.id = Roo.id());
10316         });
10317         var iter = function(el) {
10318             if (el.inputEl) {
10319                 r.add(el);
10320             }
10321             if (!el.items) {
10322                 return;
10323             }
10324             Roo.each(el.items,function(e) {
10325                 iter(e);
10326             });
10327         };
10328
10329         iter(this);
10330         return r;
10331     },
10332     
10333     hideFields : function(items)
10334     {
10335         Roo.each(items, function(i){
10336             
10337             var f = this.findField(i);
10338             
10339             if(!f){
10340                 return;
10341             }
10342             
10343             f.hide();
10344             
10345         }, this);
10346     },
10347     
10348     showFields : function(items)
10349     {
10350         Roo.each(items, function(i){
10351             
10352             var f = this.findField(i);
10353             
10354             if(!f){
10355                 return;
10356             }
10357             
10358             f.show();
10359             
10360         }, this);
10361     }
10362
10363 });
10364
10365 Roo.apply(Roo.bootstrap.Form, {
10366     
10367     popover : {
10368         
10369         padding : 5,
10370         
10371         isApplied : false,
10372         
10373         isMasked : false,
10374         
10375         form : false,
10376         
10377         target : false,
10378         
10379         toolTip : false,
10380         
10381         intervalID : false,
10382         
10383         maskEl : false,
10384         
10385         apply : function()
10386         {
10387             if(this.isApplied){
10388                 return;
10389             }
10390             
10391             this.maskEl = {
10392                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10393                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10394                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10395                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10396             };
10397             
10398             this.maskEl.top.enableDisplayMode("block");
10399             this.maskEl.left.enableDisplayMode("block");
10400             this.maskEl.bottom.enableDisplayMode("block");
10401             this.maskEl.right.enableDisplayMode("block");
10402             
10403             this.toolTip = new Roo.bootstrap.Tooltip({
10404                 cls : 'roo-form-error-popover',
10405                 alignment : {
10406                     'left' : ['r-l', [-2,0], 'right'],
10407                     'right' : ['l-r', [2,0], 'left'],
10408                     'bottom' : ['tl-bl', [0,2], 'top'],
10409                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10410                 }
10411             });
10412             
10413             this.toolTip.render(Roo.get(document.body));
10414
10415             this.toolTip.el.enableDisplayMode("block");
10416             
10417             Roo.get(document.body).on('click', function(){
10418                 this.unmask();
10419             }, this);
10420             
10421             Roo.get(document.body).on('touchstart', function(){
10422                 this.unmask();
10423             }, this);
10424             
10425             this.isApplied = true
10426         },
10427         
10428         mask : function(form, target)
10429         {
10430             this.form = form;
10431             
10432             this.target = target;
10433             
10434             if(!this.form.errorMask || !target.el){
10435                 return;
10436             }
10437             
10438             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10439             
10440             Roo.log(scrollable);
10441             
10442             var ot = this.target.el.calcOffsetsTo(scrollable);
10443             
10444             var scrollTo = ot[1] - this.form.maskOffset;
10445             
10446             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10447             
10448             scrollable.scrollTo('top', scrollTo);
10449             
10450             var box = this.target.el.getBox();
10451             Roo.log(box);
10452             var zIndex = Roo.bootstrap.Modal.zIndex++;
10453
10454             
10455             this.maskEl.top.setStyle('position', 'absolute');
10456             this.maskEl.top.setStyle('z-index', zIndex);
10457             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10458             this.maskEl.top.setLeft(0);
10459             this.maskEl.top.setTop(0);
10460             this.maskEl.top.show();
10461             
10462             this.maskEl.left.setStyle('position', 'absolute');
10463             this.maskEl.left.setStyle('z-index', zIndex);
10464             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10465             this.maskEl.left.setLeft(0);
10466             this.maskEl.left.setTop(box.y - this.padding);
10467             this.maskEl.left.show();
10468
10469             this.maskEl.bottom.setStyle('position', 'absolute');
10470             this.maskEl.bottom.setStyle('z-index', zIndex);
10471             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10472             this.maskEl.bottom.setLeft(0);
10473             this.maskEl.bottom.setTop(box.bottom + this.padding);
10474             this.maskEl.bottom.show();
10475
10476             this.maskEl.right.setStyle('position', 'absolute');
10477             this.maskEl.right.setStyle('z-index', zIndex);
10478             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10479             this.maskEl.right.setLeft(box.right + this.padding);
10480             this.maskEl.right.setTop(box.y - this.padding);
10481             this.maskEl.right.show();
10482
10483             this.toolTip.bindEl = this.target.el;
10484
10485             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10486
10487             var tip = this.target.blankText;
10488
10489             if(this.target.getValue() !== '' ) {
10490                 
10491                 if (this.target.invalidText.length) {
10492                     tip = this.target.invalidText;
10493                 } else if (this.target.regexText.length){
10494                     tip = this.target.regexText;
10495                 }
10496             }
10497
10498             this.toolTip.show(tip);
10499
10500             this.intervalID = window.setInterval(function() {
10501                 Roo.bootstrap.Form.popover.unmask();
10502             }, 10000);
10503
10504             window.onwheel = function(){ return false;};
10505             
10506             (function(){ this.isMasked = true; }).defer(500, this);
10507             
10508         },
10509         
10510         unmask : function()
10511         {
10512             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10513                 return;
10514             }
10515             
10516             this.maskEl.top.setStyle('position', 'absolute');
10517             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10518             this.maskEl.top.hide();
10519
10520             this.maskEl.left.setStyle('position', 'absolute');
10521             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10522             this.maskEl.left.hide();
10523
10524             this.maskEl.bottom.setStyle('position', 'absolute');
10525             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10526             this.maskEl.bottom.hide();
10527
10528             this.maskEl.right.setStyle('position', 'absolute');
10529             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10530             this.maskEl.right.hide();
10531             
10532             this.toolTip.hide();
10533             
10534             this.toolTip.el.hide();
10535             
10536             window.onwheel = function(){ return true;};
10537             
10538             if(this.intervalID){
10539                 window.clearInterval(this.intervalID);
10540                 this.intervalID = false;
10541             }
10542             
10543             this.isMasked = false;
10544             
10545         }
10546         
10547     }
10548     
10549 });
10550
10551 /*
10552  * Based on:
10553  * Ext JS Library 1.1.1
10554  * Copyright(c) 2006-2007, Ext JS, LLC.
10555  *
10556  * Originally Released Under LGPL - original licence link has changed is not relivant.
10557  *
10558  * Fork - LGPL
10559  * <script type="text/javascript">
10560  */
10561 /**
10562  * @class Roo.form.VTypes
10563  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10564  * @singleton
10565  */
10566 Roo.form.VTypes = function(){
10567     // closure these in so they are only created once.
10568     var alpha = /^[a-zA-Z_]+$/;
10569     var alphanum = /^[a-zA-Z0-9_]+$/;
10570     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10571     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10572
10573     // All these messages and functions are configurable
10574     return {
10575         /**
10576          * The function used to validate email addresses
10577          * @param {String} value The email address
10578          */
10579         'email' : function(v){
10580             return email.test(v);
10581         },
10582         /**
10583          * The error text to display when the email validation function returns false
10584          * @type String
10585          */
10586         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10587         /**
10588          * The keystroke filter mask to be applied on email input
10589          * @type RegExp
10590          */
10591         'emailMask' : /[a-z0-9_\.\-@]/i,
10592
10593         /**
10594          * The function used to validate URLs
10595          * @param {String} value The URL
10596          */
10597         'url' : function(v){
10598             return url.test(v);
10599         },
10600         /**
10601          * The error text to display when the url validation function returns false
10602          * @type String
10603          */
10604         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10605         
10606         /**
10607          * The function used to validate alpha values
10608          * @param {String} value The value
10609          */
10610         'alpha' : function(v){
10611             return alpha.test(v);
10612         },
10613         /**
10614          * The error text to display when the alpha validation function returns false
10615          * @type String
10616          */
10617         'alphaText' : 'This field should only contain letters and _',
10618         /**
10619          * The keystroke filter mask to be applied on alpha input
10620          * @type RegExp
10621          */
10622         'alphaMask' : /[a-z_]/i,
10623
10624         /**
10625          * The function used to validate alphanumeric values
10626          * @param {String} value The value
10627          */
10628         'alphanum' : function(v){
10629             return alphanum.test(v);
10630         },
10631         /**
10632          * The error text to display when the alphanumeric validation function returns false
10633          * @type String
10634          */
10635         'alphanumText' : 'This field should only contain letters, numbers and _',
10636         /**
10637          * The keystroke filter mask to be applied on alphanumeric input
10638          * @type RegExp
10639          */
10640         'alphanumMask' : /[a-z0-9_]/i
10641     };
10642 }();/*
10643  * - LGPL
10644  *
10645  * Input
10646  * 
10647  */
10648
10649 /**
10650  * @class Roo.bootstrap.Input
10651  * @extends Roo.bootstrap.Component
10652  * Bootstrap Input class
10653  * @cfg {Boolean} disabled is it disabled
10654  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10655  * @cfg {String} name name of the input
10656  * @cfg {string} fieldLabel - the label associated
10657  * @cfg {string} placeholder - placeholder to put in text.
10658  * @cfg {string}  before - input group add on before
10659  * @cfg {string} after - input group add on after
10660  * @cfg {string} size - (lg|sm) or leave empty..
10661  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10662  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10663  * @cfg {Number} md colspan out of 12 for computer-sized screens
10664  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10665  * @cfg {string} value default value of the input
10666  * @cfg {Number} labelWidth set the width of label 
10667  * @cfg {Number} labellg set the width of label (1-12)
10668  * @cfg {Number} labelmd set the width of label (1-12)
10669  * @cfg {Number} labelsm set the width of label (1-12)
10670  * @cfg {Number} labelxs set the width of label (1-12)
10671  * @cfg {String} labelAlign (top|left)
10672  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10673  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10674  * @cfg {String} indicatorpos (left|right) default left
10675  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10676  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10677  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10678
10679  * @cfg {String} align (left|center|right) Default left
10680  * @cfg {Boolean} forceFeedback (true|false) Default false
10681  * 
10682  * @constructor
10683  * Create a new Input
10684  * @param {Object} config The config object
10685  */
10686
10687 Roo.bootstrap.Input = function(config){
10688     
10689     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10690     
10691     this.addEvents({
10692         /**
10693          * @event focus
10694          * Fires when this field receives input focus.
10695          * @param {Roo.form.Field} this
10696          */
10697         focus : true,
10698         /**
10699          * @event blur
10700          * Fires when this field loses input focus.
10701          * @param {Roo.form.Field} this
10702          */
10703         blur : true,
10704         /**
10705          * @event specialkey
10706          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10707          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10708          * @param {Roo.form.Field} this
10709          * @param {Roo.EventObject} e The event object
10710          */
10711         specialkey : true,
10712         /**
10713          * @event change
10714          * Fires just before the field blurs if the field value has changed.
10715          * @param {Roo.form.Field} this
10716          * @param {Mixed} newValue The new value
10717          * @param {Mixed} oldValue The original value
10718          */
10719         change : true,
10720         /**
10721          * @event invalid
10722          * Fires after the field has been marked as invalid.
10723          * @param {Roo.form.Field} this
10724          * @param {String} msg The validation message
10725          */
10726         invalid : true,
10727         /**
10728          * @event valid
10729          * Fires after the field has been validated with no errors.
10730          * @param {Roo.form.Field} this
10731          */
10732         valid : true,
10733          /**
10734          * @event keyup
10735          * Fires after the key up
10736          * @param {Roo.form.Field} this
10737          * @param {Roo.EventObject}  e The event Object
10738          */
10739         keyup : true,
10740         /**
10741          * @event paste
10742          * Fires after the user pastes into input
10743          * @param {Roo.form.Field} this
10744          * @param {Roo.EventObject}  e The event Object
10745          */
10746         paste : true
10747     });
10748 };
10749
10750 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10751      /**
10752      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10753       automatic validation (defaults to "keyup").
10754      */
10755     validationEvent : "keyup",
10756      /**
10757      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10758      */
10759     validateOnBlur : true,
10760     /**
10761      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10762      */
10763     validationDelay : 250,
10764      /**
10765      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10766      */
10767     focusClass : "x-form-focus",  // not needed???
10768     
10769        
10770     /**
10771      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10772      */
10773     invalidClass : "has-warning",
10774     
10775     /**
10776      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10777      */
10778     validClass : "has-success",
10779     
10780     /**
10781      * @cfg {Boolean} hasFeedback (true|false) default true
10782      */
10783     hasFeedback : true,
10784     
10785     /**
10786      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10787      */
10788     invalidFeedbackClass : "glyphicon-warning-sign",
10789     
10790     /**
10791      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10792      */
10793     validFeedbackClass : "glyphicon-ok",
10794     
10795     /**
10796      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10797      */
10798     selectOnFocus : false,
10799     
10800      /**
10801      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10802      */
10803     maskRe : null,
10804        /**
10805      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10806      */
10807     vtype : null,
10808     
10809       /**
10810      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10811      */
10812     disableKeyFilter : false,
10813     
10814        /**
10815      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10816      */
10817     disabled : false,
10818      /**
10819      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10820      */
10821     allowBlank : true,
10822     /**
10823      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10824      */
10825     blankText : "Please complete this mandatory field",
10826     
10827      /**
10828      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10829      */
10830     minLength : 0,
10831     /**
10832      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10833      */
10834     maxLength : Number.MAX_VALUE,
10835     /**
10836      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10837      */
10838     minLengthText : "The minimum length for this field is {0}",
10839     /**
10840      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10841      */
10842     maxLengthText : "The maximum length for this field is {0}",
10843   
10844     
10845     /**
10846      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10847      * If available, this function will be called only after the basic validators all return true, and will be passed the
10848      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10849      */
10850     validator : null,
10851     /**
10852      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10853      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10854      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10855      */
10856     regex : null,
10857     /**
10858      * @cfg {String} regexText -- Depricated - use Invalid Text
10859      */
10860     regexText : "",
10861     
10862     /**
10863      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10864      */
10865     invalidText : "",
10866     
10867     
10868     
10869     autocomplete: false,
10870     
10871     
10872     fieldLabel : '',
10873     inputType : 'text',
10874     
10875     name : false,
10876     placeholder: false,
10877     before : false,
10878     after : false,
10879     size : false,
10880     hasFocus : false,
10881     preventMark: false,
10882     isFormField : true,
10883     value : '',
10884     labelWidth : 2,
10885     labelAlign : false,
10886     readOnly : false,
10887     align : false,
10888     formatedValue : false,
10889     forceFeedback : false,
10890     
10891     indicatorpos : 'left',
10892     
10893     labellg : 0,
10894     labelmd : 0,
10895     labelsm : 0,
10896     labelxs : 0,
10897     
10898     capture : '',
10899     accept : '',
10900     
10901     parentLabelAlign : function()
10902     {
10903         var parent = this;
10904         while (parent.parent()) {
10905             parent = parent.parent();
10906             if (typeof(parent.labelAlign) !='undefined') {
10907                 return parent.labelAlign;
10908             }
10909         }
10910         return 'left';
10911         
10912     },
10913     
10914     getAutoCreate : function()
10915     {
10916         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10917         
10918         var id = Roo.id();
10919         
10920         var cfg = {};
10921         
10922         if(this.inputType != 'hidden'){
10923             cfg.cls = 'form-group' //input-group
10924         }
10925         
10926         var input =  {
10927             tag: 'input',
10928             id : id,
10929             type : this.inputType,
10930             value : this.value,
10931             cls : 'form-control',
10932             placeholder : this.placeholder || '',
10933             autocomplete : this.autocomplete || 'new-password'
10934         };
10935         if (this.inputType == 'file') {
10936             input.style = 'overflow:hidden'; // why not in CSS?
10937         }
10938         
10939         if(this.capture.length){
10940             input.capture = this.capture;
10941         }
10942         
10943         if(this.accept.length){
10944             input.accept = this.accept + "/*";
10945         }
10946         
10947         if(this.align){
10948             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10949         }
10950         
10951         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10952             input.maxLength = this.maxLength;
10953         }
10954         
10955         if (this.disabled) {
10956             input.disabled=true;
10957         }
10958         
10959         if (this.readOnly) {
10960             input.readonly=true;
10961         }
10962         
10963         if (this.name) {
10964             input.name = this.name;
10965         }
10966         
10967         if (this.size) {
10968             input.cls += ' input-' + this.size;
10969         }
10970         
10971         var settings=this;
10972         ['xs','sm','md','lg'].map(function(size){
10973             if (settings[size]) {
10974                 cfg.cls += ' col-' + size + '-' + settings[size];
10975             }
10976         });
10977         
10978         var inputblock = input;
10979         
10980         var feedback = {
10981             tag: 'span',
10982             cls: 'glyphicon form-control-feedback'
10983         };
10984             
10985         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10986             
10987             inputblock = {
10988                 cls : 'has-feedback',
10989                 cn :  [
10990                     input,
10991                     feedback
10992                 ] 
10993             };  
10994         }
10995         
10996         if (this.before || this.after) {
10997             
10998             inputblock = {
10999                 cls : 'input-group',
11000                 cn :  [] 
11001             };
11002             
11003             if (this.before && typeof(this.before) == 'string') {
11004                 
11005                 inputblock.cn.push({
11006                     tag :'span',
11007                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11008                     html : this.before
11009                 });
11010             }
11011             if (this.before && typeof(this.before) == 'object') {
11012                 this.before = Roo.factory(this.before);
11013                 
11014                 inputblock.cn.push({
11015                     tag :'span',
11016                     cls : 'roo-input-before input-group-prepend   input-group-' +
11017                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11018                 });
11019             }
11020             
11021             inputblock.cn.push(input);
11022             
11023             if (this.after && typeof(this.after) == 'string') {
11024                 inputblock.cn.push({
11025                     tag :'span',
11026                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11027                     html : this.after
11028                 });
11029             }
11030             if (this.after && typeof(this.after) == 'object') {
11031                 this.after = Roo.factory(this.after);
11032                 
11033                 inputblock.cn.push({
11034                     tag :'span',
11035                     cls : 'roo-input-after input-group-append  input-group-' +
11036                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11037                 });
11038             }
11039             
11040             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11041                 inputblock.cls += ' has-feedback';
11042                 inputblock.cn.push(feedback);
11043             }
11044         };
11045         var indicator = {
11046             tag : 'i',
11047             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11048             tooltip : 'This field is required'
11049         };
11050         if (this.allowBlank ) {
11051             indicator.style = this.allowBlank ? ' display:none' : '';
11052         }
11053         if (align ==='left' && this.fieldLabel.length) {
11054             
11055             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11056             
11057             cfg.cn = [
11058                 indicator,
11059                 {
11060                     tag: 'label',
11061                     'for' :  id,
11062                     cls : 'control-label col-form-label',
11063                     html : this.fieldLabel
11064
11065                 },
11066                 {
11067                     cls : "", 
11068                     cn: [
11069                         inputblock
11070                     ]
11071                 }
11072             ];
11073             
11074             var labelCfg = cfg.cn[1];
11075             var contentCfg = cfg.cn[2];
11076             
11077             if(this.indicatorpos == 'right'){
11078                 cfg.cn = [
11079                     {
11080                         tag: 'label',
11081                         'for' :  id,
11082                         cls : 'control-label col-form-label',
11083                         cn : [
11084                             {
11085                                 tag : 'span',
11086                                 html : this.fieldLabel
11087                             },
11088                             indicator
11089                         ]
11090                     },
11091                     {
11092                         cls : "",
11093                         cn: [
11094                             inputblock
11095                         ]
11096                     }
11097
11098                 ];
11099                 
11100                 labelCfg = cfg.cn[0];
11101                 contentCfg = cfg.cn[1];
11102             
11103             }
11104             
11105             if(this.labelWidth > 12){
11106                 labelCfg.style = "width: " + this.labelWidth + 'px';
11107             }
11108             
11109             if(this.labelWidth < 13 && this.labelmd == 0){
11110                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11111             }
11112             
11113             if(this.labellg > 0){
11114                 labelCfg.cls += ' col-lg-' + this.labellg;
11115                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11116             }
11117             
11118             if(this.labelmd > 0){
11119                 labelCfg.cls += ' col-md-' + this.labelmd;
11120                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11121             }
11122             
11123             if(this.labelsm > 0){
11124                 labelCfg.cls += ' col-sm-' + this.labelsm;
11125                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11126             }
11127             
11128             if(this.labelxs > 0){
11129                 labelCfg.cls += ' col-xs-' + this.labelxs;
11130                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11131             }
11132             
11133             
11134         } else if ( this.fieldLabel.length) {
11135                 
11136             
11137             
11138             cfg.cn = [
11139                 {
11140                     tag : 'i',
11141                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11142                     tooltip : 'This field is required',
11143                     style : this.allowBlank ? ' display:none' : '' 
11144                 },
11145                 {
11146                     tag: 'label',
11147                    //cls : 'input-group-addon',
11148                     html : this.fieldLabel
11149
11150                 },
11151
11152                inputblock
11153
11154            ];
11155            
11156            if(this.indicatorpos == 'right'){
11157        
11158                 cfg.cn = [
11159                     {
11160                         tag: 'label',
11161                        //cls : 'input-group-addon',
11162                         html : this.fieldLabel
11163
11164                     },
11165                     {
11166                         tag : 'i',
11167                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11168                         tooltip : 'This field is required',
11169                         style : this.allowBlank ? ' display:none' : '' 
11170                     },
11171
11172                    inputblock
11173
11174                ];
11175
11176             }
11177
11178         } else {
11179             
11180             cfg.cn = [
11181
11182                     inputblock
11183
11184             ];
11185                 
11186                 
11187         };
11188         
11189         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11190            cfg.cls += ' navbar-form';
11191         }
11192         
11193         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11194             // on BS4 we do this only if not form 
11195             cfg.cls += ' navbar-form';
11196             cfg.tag = 'li';
11197         }
11198         
11199         return cfg;
11200         
11201     },
11202     /**
11203      * return the real input element.
11204      */
11205     inputEl: function ()
11206     {
11207         return this.el.select('input.form-control',true).first();
11208     },
11209     
11210     tooltipEl : function()
11211     {
11212         return this.inputEl();
11213     },
11214     
11215     indicatorEl : function()
11216     {
11217         if (Roo.bootstrap.version == 4) {
11218             return false; // not enabled in v4 yet.
11219         }
11220         
11221         var indicator = this.el.select('i.roo-required-indicator',true).first();
11222         
11223         if(!indicator){
11224             return false;
11225         }
11226         
11227         return indicator;
11228         
11229     },
11230     
11231     setDisabled : function(v)
11232     {
11233         var i  = this.inputEl().dom;
11234         if (!v) {
11235             i.removeAttribute('disabled');
11236             return;
11237             
11238         }
11239         i.setAttribute('disabled','true');
11240     },
11241     initEvents : function()
11242     {
11243           
11244         this.inputEl().on("keydown" , this.fireKey,  this);
11245         this.inputEl().on("focus", this.onFocus,  this);
11246         this.inputEl().on("blur", this.onBlur,  this);
11247         
11248         this.inputEl().relayEvent('keyup', this);
11249         this.inputEl().relayEvent('paste', this);
11250         
11251         this.indicator = this.indicatorEl();
11252         
11253         if(this.indicator){
11254             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11255         }
11256  
11257         // reference to original value for reset
11258         this.originalValue = this.getValue();
11259         //Roo.form.TextField.superclass.initEvents.call(this);
11260         if(this.validationEvent == 'keyup'){
11261             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11262             this.inputEl().on('keyup', this.filterValidation, this);
11263         }
11264         else if(this.validationEvent !== false){
11265             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11266         }
11267         
11268         if(this.selectOnFocus){
11269             this.on("focus", this.preFocus, this);
11270             
11271         }
11272         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11273             this.inputEl().on("keypress", this.filterKeys, this);
11274         } else {
11275             this.inputEl().relayEvent('keypress', this);
11276         }
11277        /* if(this.grow){
11278             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11279             this.el.on("click", this.autoSize,  this);
11280         }
11281         */
11282         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11283             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11284         }
11285         
11286         if (typeof(this.before) == 'object') {
11287             this.before.render(this.el.select('.roo-input-before',true).first());
11288         }
11289         if (typeof(this.after) == 'object') {
11290             this.after.render(this.el.select('.roo-input-after',true).first());
11291         }
11292         
11293         this.inputEl().on('change', this.onChange, this);
11294         
11295     },
11296     filterValidation : function(e){
11297         if(!e.isNavKeyPress()){
11298             this.validationTask.delay(this.validationDelay);
11299         }
11300     },
11301      /**
11302      * Validates the field value
11303      * @return {Boolean} True if the value is valid, else false
11304      */
11305     validate : function(){
11306         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11307         if(this.disabled || this.validateValue(this.getRawValue())){
11308             this.markValid();
11309             return true;
11310         }
11311         
11312         this.markInvalid();
11313         return false;
11314     },
11315     
11316     
11317     /**
11318      * Validates a value according to the field's validation rules and marks the field as invalid
11319      * if the validation fails
11320      * @param {Mixed} value The value to validate
11321      * @return {Boolean} True if the value is valid, else false
11322      */
11323     validateValue : function(value)
11324     {
11325         if(this.getVisibilityEl().hasClass('hidden')){
11326             return true;
11327         }
11328         
11329         if(value.length < 1)  { // if it's blank
11330             if(this.allowBlank){
11331                 return true;
11332             }
11333             return false;
11334         }
11335         
11336         if(value.length < this.minLength){
11337             return false;
11338         }
11339         if(value.length > this.maxLength){
11340             return false;
11341         }
11342         if(this.vtype){
11343             var vt = Roo.form.VTypes;
11344             if(!vt[this.vtype](value, this)){
11345                 return false;
11346             }
11347         }
11348         if(typeof this.validator == "function"){
11349             var msg = this.validator(value);
11350             if(msg !== true){
11351                 return false;
11352             }
11353             if (typeof(msg) == 'string') {
11354                 this.invalidText = msg;
11355             }
11356         }
11357         
11358         if(this.regex && !this.regex.test(value)){
11359             return false;
11360         }
11361         
11362         return true;
11363     },
11364     
11365      // private
11366     fireKey : function(e){
11367         //Roo.log('field ' + e.getKey());
11368         if(e.isNavKeyPress()){
11369             this.fireEvent("specialkey", this, e);
11370         }
11371     },
11372     focus : function (selectText){
11373         if(this.rendered){
11374             this.inputEl().focus();
11375             if(selectText === true){
11376                 this.inputEl().dom.select();
11377             }
11378         }
11379         return this;
11380     } ,
11381     
11382     onFocus : function(){
11383         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11384            // this.el.addClass(this.focusClass);
11385         }
11386         if(!this.hasFocus){
11387             this.hasFocus = true;
11388             this.startValue = this.getValue();
11389             this.fireEvent("focus", this);
11390         }
11391     },
11392     
11393     beforeBlur : Roo.emptyFn,
11394
11395     
11396     // private
11397     onBlur : function(){
11398         this.beforeBlur();
11399         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11400             //this.el.removeClass(this.focusClass);
11401         }
11402         this.hasFocus = false;
11403         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11404             this.validate();
11405         }
11406         var v = this.getValue();
11407         if(String(v) !== String(this.startValue)){
11408             this.fireEvent('change', this, v, this.startValue);
11409         }
11410         this.fireEvent("blur", this);
11411     },
11412     
11413     onChange : function(e)
11414     {
11415         var v = this.getValue();
11416         if(String(v) !== String(this.startValue)){
11417             this.fireEvent('change', this, v, this.startValue);
11418         }
11419         
11420     },
11421     
11422     /**
11423      * Resets the current field value to the originally loaded value and clears any validation messages
11424      */
11425     reset : function(){
11426         this.setValue(this.originalValue);
11427         this.validate();
11428     },
11429      /**
11430      * Returns the name of the field
11431      * @return {Mixed} name The name field
11432      */
11433     getName: function(){
11434         return this.name;
11435     },
11436      /**
11437      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11438      * @return {Mixed} value The field value
11439      */
11440     getValue : function(){
11441         
11442         var v = this.inputEl().getValue();
11443         
11444         return v;
11445     },
11446     /**
11447      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11448      * @return {Mixed} value The field value
11449      */
11450     getRawValue : function(){
11451         var v = this.inputEl().getValue();
11452         
11453         return v;
11454     },
11455     
11456     /**
11457      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11458      * @param {Mixed} value The value to set
11459      */
11460     setRawValue : function(v){
11461         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11462     },
11463     
11464     selectText : function(start, end){
11465         var v = this.getRawValue();
11466         if(v.length > 0){
11467             start = start === undefined ? 0 : start;
11468             end = end === undefined ? v.length : end;
11469             var d = this.inputEl().dom;
11470             if(d.setSelectionRange){
11471                 d.setSelectionRange(start, end);
11472             }else if(d.createTextRange){
11473                 var range = d.createTextRange();
11474                 range.moveStart("character", start);
11475                 range.moveEnd("character", v.length-end);
11476                 range.select();
11477             }
11478         }
11479     },
11480     
11481     /**
11482      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11483      * @param {Mixed} value The value to set
11484      */
11485     setValue : function(v){
11486         this.value = v;
11487         if(this.rendered){
11488             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11489             this.validate();
11490         }
11491     },
11492     
11493     /*
11494     processValue : function(value){
11495         if(this.stripCharsRe){
11496             var newValue = value.replace(this.stripCharsRe, '');
11497             if(newValue !== value){
11498                 this.setRawValue(newValue);
11499                 return newValue;
11500             }
11501         }
11502         return value;
11503     },
11504   */
11505     preFocus : function(){
11506         
11507         if(this.selectOnFocus){
11508             this.inputEl().dom.select();
11509         }
11510     },
11511     filterKeys : function(e){
11512         var k = e.getKey();
11513         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11514             return;
11515         }
11516         var c = e.getCharCode(), cc = String.fromCharCode(c);
11517         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11518             return;
11519         }
11520         if(!this.maskRe.test(cc)){
11521             e.stopEvent();
11522         }
11523     },
11524      /**
11525      * Clear any invalid styles/messages for this field
11526      */
11527     clearInvalid : function(){
11528         
11529         if(!this.el || this.preventMark){ // not rendered
11530             return;
11531         }
11532         
11533         
11534         this.el.removeClass([this.invalidClass, 'is-invalid']);
11535         
11536         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11537             
11538             var feedback = this.el.select('.form-control-feedback', true).first();
11539             
11540             if(feedback){
11541                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11542             }
11543             
11544         }
11545         
11546         if(this.indicator){
11547             this.indicator.removeClass('visible');
11548             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11549         }
11550         
11551         this.fireEvent('valid', this);
11552     },
11553     
11554      /**
11555      * Mark this field as valid
11556      */
11557     markValid : function()
11558     {
11559         if(!this.el  || this.preventMark){ // not rendered...
11560             return;
11561         }
11562         
11563         this.el.removeClass([this.invalidClass, this.validClass]);
11564         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11565
11566         var feedback = this.el.select('.form-control-feedback', true).first();
11567             
11568         if(feedback){
11569             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11570         }
11571         
11572         if(this.indicator){
11573             this.indicator.removeClass('visible');
11574             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11575         }
11576         
11577         if(this.disabled){
11578             return;
11579         }
11580         
11581            
11582         if(this.allowBlank && !this.getRawValue().length){
11583             return;
11584         }
11585         if (Roo.bootstrap.version == 3) {
11586             this.el.addClass(this.validClass);
11587         } else {
11588             this.inputEl().addClass('is-valid');
11589         }
11590
11591         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11592             
11593             var feedback = this.el.select('.form-control-feedback', true).first();
11594             
11595             if(feedback){
11596                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11597                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11598             }
11599             
11600         }
11601         
11602         this.fireEvent('valid', this);
11603     },
11604     
11605      /**
11606      * Mark this field as invalid
11607      * @param {String} msg The validation message
11608      */
11609     markInvalid : function(msg)
11610     {
11611         if(!this.el  || this.preventMark){ // not rendered
11612             return;
11613         }
11614         
11615         this.el.removeClass([this.invalidClass, this.validClass]);
11616         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11617         
11618         var feedback = this.el.select('.form-control-feedback', true).first();
11619             
11620         if(feedback){
11621             this.el.select('.form-control-feedback', true).first().removeClass(
11622                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11623         }
11624
11625         if(this.disabled){
11626             return;
11627         }
11628         
11629         if(this.allowBlank && !this.getRawValue().length){
11630             return;
11631         }
11632         
11633         if(this.indicator){
11634             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11635             this.indicator.addClass('visible');
11636         }
11637         if (Roo.bootstrap.version == 3) {
11638             this.el.addClass(this.invalidClass);
11639         } else {
11640             this.inputEl().addClass('is-invalid');
11641         }
11642         
11643         
11644         
11645         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11646             
11647             var feedback = this.el.select('.form-control-feedback', true).first();
11648             
11649             if(feedback){
11650                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11651                 
11652                 if(this.getValue().length || this.forceFeedback){
11653                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11654                 }
11655                 
11656             }
11657             
11658         }
11659         
11660         this.fireEvent('invalid', this, msg);
11661     },
11662     // private
11663     SafariOnKeyDown : function(event)
11664     {
11665         // this is a workaround for a password hang bug on chrome/ webkit.
11666         if (this.inputEl().dom.type != 'password') {
11667             return;
11668         }
11669         
11670         var isSelectAll = false;
11671         
11672         if(this.inputEl().dom.selectionEnd > 0){
11673             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11674         }
11675         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11676             event.preventDefault();
11677             this.setValue('');
11678             return;
11679         }
11680         
11681         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11682             
11683             event.preventDefault();
11684             // this is very hacky as keydown always get's upper case.
11685             //
11686             var cc = String.fromCharCode(event.getCharCode());
11687             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11688             
11689         }
11690     },
11691     adjustWidth : function(tag, w){
11692         tag = tag.toLowerCase();
11693         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11694             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11695                 if(tag == 'input'){
11696                     return w + 2;
11697                 }
11698                 if(tag == 'textarea'){
11699                     return w-2;
11700                 }
11701             }else if(Roo.isOpera){
11702                 if(tag == 'input'){
11703                     return w + 2;
11704                 }
11705                 if(tag == 'textarea'){
11706                     return w-2;
11707                 }
11708             }
11709         }
11710         return w;
11711     },
11712     
11713     setFieldLabel : function(v)
11714     {
11715         if(!this.rendered){
11716             return;
11717         }
11718         
11719         if(this.indicatorEl()){
11720             var ar = this.el.select('label > span',true);
11721             
11722             if (ar.elements.length) {
11723                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11724                 this.fieldLabel = v;
11725                 return;
11726             }
11727             
11728             var br = this.el.select('label',true);
11729             
11730             if(br.elements.length) {
11731                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11732                 this.fieldLabel = v;
11733                 return;
11734             }
11735             
11736             Roo.log('Cannot Found any of label > span || label in input');
11737             return;
11738         }
11739         
11740         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11741         this.fieldLabel = v;
11742         
11743         
11744     }
11745 });
11746
11747  
11748 /*
11749  * - LGPL
11750  *
11751  * Input
11752  * 
11753  */
11754
11755 /**
11756  * @class Roo.bootstrap.TextArea
11757  * @extends Roo.bootstrap.Input
11758  * Bootstrap TextArea class
11759  * @cfg {Number} cols Specifies the visible width of a text area
11760  * @cfg {Number} rows Specifies the visible number of lines in a text area
11761  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11762  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11763  * @cfg {string} html text
11764  * 
11765  * @constructor
11766  * Create a new TextArea
11767  * @param {Object} config The config object
11768  */
11769
11770 Roo.bootstrap.TextArea = function(config){
11771     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11772    
11773 };
11774
11775 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11776      
11777     cols : false,
11778     rows : 5,
11779     readOnly : false,
11780     warp : 'soft',
11781     resize : false,
11782     value: false,
11783     html: false,
11784     
11785     getAutoCreate : function(){
11786         
11787         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11788         
11789         var id = Roo.id();
11790         
11791         var cfg = {};
11792         
11793         if(this.inputType != 'hidden'){
11794             cfg.cls = 'form-group' //input-group
11795         }
11796         
11797         var input =  {
11798             tag: 'textarea',
11799             id : id,
11800             warp : this.warp,
11801             rows : this.rows,
11802             value : this.value || '',
11803             html: this.html || '',
11804             cls : 'form-control',
11805             placeholder : this.placeholder || '' 
11806             
11807         };
11808         
11809         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11810             input.maxLength = this.maxLength;
11811         }
11812         
11813         if(this.resize){
11814             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11815         }
11816         
11817         if(this.cols){
11818             input.cols = this.cols;
11819         }
11820         
11821         if (this.readOnly) {
11822             input.readonly = true;
11823         }
11824         
11825         if (this.name) {
11826             input.name = this.name;
11827         }
11828         
11829         if (this.size) {
11830             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11831         }
11832         
11833         var settings=this;
11834         ['xs','sm','md','lg'].map(function(size){
11835             if (settings[size]) {
11836                 cfg.cls += ' col-' + size + '-' + settings[size];
11837             }
11838         });
11839         
11840         var inputblock = input;
11841         
11842         if(this.hasFeedback && !this.allowBlank){
11843             
11844             var feedback = {
11845                 tag: 'span',
11846                 cls: 'glyphicon form-control-feedback'
11847             };
11848
11849             inputblock = {
11850                 cls : 'has-feedback',
11851                 cn :  [
11852                     input,
11853                     feedback
11854                 ] 
11855             };  
11856         }
11857         
11858         
11859         if (this.before || this.after) {
11860             
11861             inputblock = {
11862                 cls : 'input-group',
11863                 cn :  [] 
11864             };
11865             if (this.before) {
11866                 inputblock.cn.push({
11867                     tag :'span',
11868                     cls : 'input-group-addon',
11869                     html : this.before
11870                 });
11871             }
11872             
11873             inputblock.cn.push(input);
11874             
11875             if(this.hasFeedback && !this.allowBlank){
11876                 inputblock.cls += ' has-feedback';
11877                 inputblock.cn.push(feedback);
11878             }
11879             
11880             if (this.after) {
11881                 inputblock.cn.push({
11882                     tag :'span',
11883                     cls : 'input-group-addon',
11884                     html : this.after
11885                 });
11886             }
11887             
11888         }
11889         
11890         if (align ==='left' && this.fieldLabel.length) {
11891             cfg.cn = [
11892                 {
11893                     tag: 'label',
11894                     'for' :  id,
11895                     cls : 'control-label',
11896                     html : this.fieldLabel
11897                 },
11898                 {
11899                     cls : "",
11900                     cn: [
11901                         inputblock
11902                     ]
11903                 }
11904
11905             ];
11906             
11907             if(this.labelWidth > 12){
11908                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11909             }
11910
11911             if(this.labelWidth < 13 && this.labelmd == 0){
11912                 this.labelmd = this.labelWidth;
11913             }
11914
11915             if(this.labellg > 0){
11916                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11917                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11918             }
11919
11920             if(this.labelmd > 0){
11921                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11922                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11923             }
11924
11925             if(this.labelsm > 0){
11926                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11927                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11928             }
11929
11930             if(this.labelxs > 0){
11931                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11932                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11933             }
11934             
11935         } else if ( this.fieldLabel.length) {
11936             cfg.cn = [
11937
11938                {
11939                    tag: 'label',
11940                    //cls : 'input-group-addon',
11941                    html : this.fieldLabel
11942
11943                },
11944
11945                inputblock
11946
11947            ];
11948
11949         } else {
11950
11951             cfg.cn = [
11952
11953                 inputblock
11954
11955             ];
11956                 
11957         }
11958         
11959         if (this.disabled) {
11960             input.disabled=true;
11961         }
11962         
11963         return cfg;
11964         
11965     },
11966     /**
11967      * return the real textarea element.
11968      */
11969     inputEl: function ()
11970     {
11971         return this.el.select('textarea.form-control',true).first();
11972     },
11973     
11974     /**
11975      * Clear any invalid styles/messages for this field
11976      */
11977     clearInvalid : function()
11978     {
11979         
11980         if(!this.el || this.preventMark){ // not rendered
11981             return;
11982         }
11983         
11984         var label = this.el.select('label', true).first();
11985         var icon = this.el.select('i.fa-star', true).first();
11986         
11987         if(label && icon){
11988             icon.remove();
11989         }
11990         this.el.removeClass( this.validClass);
11991         this.inputEl().removeClass('is-invalid');
11992          
11993         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11994             
11995             var feedback = this.el.select('.form-control-feedback', true).first();
11996             
11997             if(feedback){
11998                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11999             }
12000             
12001         }
12002         
12003         this.fireEvent('valid', this);
12004     },
12005     
12006      /**
12007      * Mark this field as valid
12008      */
12009     markValid : function()
12010     {
12011         if(!this.el  || this.preventMark){ // not rendered
12012             return;
12013         }
12014         
12015         this.el.removeClass([this.invalidClass, this.validClass]);
12016         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12017         
12018         var feedback = this.el.select('.form-control-feedback', true).first();
12019             
12020         if(feedback){
12021             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12022         }
12023
12024         if(this.disabled || this.allowBlank){
12025             return;
12026         }
12027         
12028         var label = this.el.select('label', true).first();
12029         var icon = this.el.select('i.fa-star', true).first();
12030         
12031         if(label && icon){
12032             icon.remove();
12033         }
12034         if (Roo.bootstrap.version == 3) {
12035             this.el.addClass(this.validClass);
12036         } else {
12037             this.inputEl().addClass('is-valid');
12038         }
12039         
12040         
12041         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12042             
12043             var feedback = this.el.select('.form-control-feedback', true).first();
12044             
12045             if(feedback){
12046                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12047                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12048             }
12049             
12050         }
12051         
12052         this.fireEvent('valid', this);
12053     },
12054     
12055      /**
12056      * Mark this field as invalid
12057      * @param {String} msg The validation message
12058      */
12059     markInvalid : function(msg)
12060     {
12061         if(!this.el  || this.preventMark){ // not rendered
12062             return;
12063         }
12064         
12065         this.el.removeClass([this.invalidClass, this.validClass]);
12066         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12067         
12068         var feedback = this.el.select('.form-control-feedback', true).first();
12069             
12070         if(feedback){
12071             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12072         }
12073
12074         if(this.disabled || this.allowBlank){
12075             return;
12076         }
12077         
12078         var label = this.el.select('label', true).first();
12079         var icon = this.el.select('i.fa-star', true).first();
12080         
12081         if(!this.getValue().length && label && !icon){
12082             this.el.createChild({
12083                 tag : 'i',
12084                 cls : 'text-danger fa fa-lg fa-star',
12085                 tooltip : 'This field is required',
12086                 style : 'margin-right:5px;'
12087             }, label, true);
12088         }
12089         
12090         if (Roo.bootstrap.version == 3) {
12091             this.el.addClass(this.invalidClass);
12092         } else {
12093             this.inputEl().addClass('is-invalid');
12094         }
12095         
12096         // fixme ... this may be depricated need to test..
12097         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12098             
12099             var feedback = this.el.select('.form-control-feedback', true).first();
12100             
12101             if(feedback){
12102                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12103                 
12104                 if(this.getValue().length || this.forceFeedback){
12105                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12106                 }
12107                 
12108             }
12109             
12110         }
12111         
12112         this.fireEvent('invalid', this, msg);
12113     }
12114 });
12115
12116  
12117 /*
12118  * - LGPL
12119  *
12120  * trigger field - base class for combo..
12121  * 
12122  */
12123  
12124 /**
12125  * @class Roo.bootstrap.TriggerField
12126  * @extends Roo.bootstrap.Input
12127  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12128  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12129  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12130  * for which you can provide a custom implementation.  For example:
12131  * <pre><code>
12132 var trigger = new Roo.bootstrap.TriggerField();
12133 trigger.onTriggerClick = myTriggerFn;
12134 trigger.applyTo('my-field');
12135 </code></pre>
12136  *
12137  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12138  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12139  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12140  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12141  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12142
12143  * @constructor
12144  * Create a new TriggerField.
12145  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12146  * to the base TextField)
12147  */
12148 Roo.bootstrap.TriggerField = function(config){
12149     this.mimicing = false;
12150     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12151 };
12152
12153 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12154     /**
12155      * @cfg {String} triggerClass A CSS class to apply to the trigger
12156      */
12157      /**
12158      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12159      */
12160     hideTrigger:false,
12161
12162     /**
12163      * @cfg {Boolean} removable (true|false) special filter default false
12164      */
12165     removable : false,
12166     
12167     /** @cfg {Boolean} grow @hide */
12168     /** @cfg {Number} growMin @hide */
12169     /** @cfg {Number} growMax @hide */
12170
12171     /**
12172      * @hide 
12173      * @method
12174      */
12175     autoSize: Roo.emptyFn,
12176     // private
12177     monitorTab : true,
12178     // private
12179     deferHeight : true,
12180
12181     
12182     actionMode : 'wrap',
12183     
12184     caret : false,
12185     
12186     
12187     getAutoCreate : function(){
12188        
12189         var align = this.labelAlign || this.parentLabelAlign();
12190         
12191         var id = Roo.id();
12192         
12193         var cfg = {
12194             cls: 'form-group' //input-group
12195         };
12196         
12197         
12198         var input =  {
12199             tag: 'input',
12200             id : id,
12201             type : this.inputType,
12202             cls : 'form-control',
12203             autocomplete: 'new-password',
12204             placeholder : this.placeholder || '' 
12205             
12206         };
12207         if (this.name) {
12208             input.name = this.name;
12209         }
12210         if (this.size) {
12211             input.cls += ' input-' + this.size;
12212         }
12213         
12214         if (this.disabled) {
12215             input.disabled=true;
12216         }
12217         
12218         var inputblock = input;
12219         
12220         if(this.hasFeedback && !this.allowBlank){
12221             
12222             var feedback = {
12223                 tag: 'span',
12224                 cls: 'glyphicon form-control-feedback'
12225             };
12226             
12227             if(this.removable && !this.editable  ){
12228                 inputblock = {
12229                     cls : 'has-feedback',
12230                     cn :  [
12231                         inputblock,
12232                         {
12233                             tag: 'button',
12234                             html : 'x',
12235                             cls : 'roo-combo-removable-btn close'
12236                         },
12237                         feedback
12238                     ] 
12239                 };
12240             } else {
12241                 inputblock = {
12242                     cls : 'has-feedback',
12243                     cn :  [
12244                         inputblock,
12245                         feedback
12246                     ] 
12247                 };
12248             }
12249
12250         } else {
12251             if(this.removable && !this.editable ){
12252                 inputblock = {
12253                     cls : 'roo-removable',
12254                     cn :  [
12255                         inputblock,
12256                         {
12257                             tag: 'button',
12258                             html : 'x',
12259                             cls : 'roo-combo-removable-btn close'
12260                         }
12261                     ] 
12262                 };
12263             }
12264         }
12265         
12266         if (this.before || this.after) {
12267             
12268             inputblock = {
12269                 cls : 'input-group',
12270                 cn :  [] 
12271             };
12272             if (this.before) {
12273                 inputblock.cn.push({
12274                     tag :'span',
12275                     cls : 'input-group-addon input-group-prepend input-group-text',
12276                     html : this.before
12277                 });
12278             }
12279             
12280             inputblock.cn.push(input);
12281             
12282             if(this.hasFeedback && !this.allowBlank){
12283                 inputblock.cls += ' has-feedback';
12284                 inputblock.cn.push(feedback);
12285             }
12286             
12287             if (this.after) {
12288                 inputblock.cn.push({
12289                     tag :'span',
12290                     cls : 'input-group-addon input-group-append input-group-text',
12291                     html : this.after
12292                 });
12293             }
12294             
12295         };
12296         
12297       
12298         
12299         var ibwrap = inputblock;
12300         
12301         if(this.multiple){
12302             ibwrap = {
12303                 tag: 'ul',
12304                 cls: 'roo-select2-choices',
12305                 cn:[
12306                     {
12307                         tag: 'li',
12308                         cls: 'roo-select2-search-field',
12309                         cn: [
12310
12311                             inputblock
12312                         ]
12313                     }
12314                 ]
12315             };
12316                 
12317         }
12318         
12319         var combobox = {
12320             cls: 'roo-select2-container input-group',
12321             cn: [
12322                  {
12323                     tag: 'input',
12324                     type : 'hidden',
12325                     cls: 'form-hidden-field'
12326                 },
12327                 ibwrap
12328             ]
12329         };
12330         
12331         if(!this.multiple && this.showToggleBtn){
12332             
12333             var caret = {
12334                         tag: 'span',
12335                         cls: 'caret'
12336              };
12337             if (this.caret != false) {
12338                 caret = {
12339                      tag: 'i',
12340                      cls: 'fa fa-' + this.caret
12341                 };
12342                 
12343             }
12344             
12345             combobox.cn.push({
12346                 tag :'span',
12347                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12348                 cn : [
12349                     Roo.bootstrap.version == 3 ? caret : '',
12350                     {
12351                         tag: 'span',
12352                         cls: 'combobox-clear',
12353                         cn  : [
12354                             {
12355                                 tag : 'i',
12356                                 cls: 'icon-remove'
12357                             }
12358                         ]
12359                     }
12360                 ]
12361
12362             })
12363         }
12364         
12365         if(this.multiple){
12366             combobox.cls += ' roo-select2-container-multi';
12367         }
12368          var indicator = {
12369             tag : 'i',
12370             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12371             tooltip : 'This field is required'
12372         };
12373         if (Roo.bootstrap.version == 4) {
12374             indicator = {
12375                 tag : 'i',
12376                 style : 'display:none'
12377             };
12378         }
12379         
12380         
12381         if (align ==='left' && this.fieldLabel.length) {
12382             
12383             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12384
12385             cfg.cn = [
12386                 indicator,
12387                 {
12388                     tag: 'label',
12389                     'for' :  id,
12390                     cls : 'control-label',
12391                     html : this.fieldLabel
12392
12393                 },
12394                 {
12395                     cls : "", 
12396                     cn: [
12397                         combobox
12398                     ]
12399                 }
12400
12401             ];
12402             
12403             var labelCfg = cfg.cn[1];
12404             var contentCfg = cfg.cn[2];
12405             
12406             if(this.indicatorpos == 'right'){
12407                 cfg.cn = [
12408                     {
12409                         tag: 'label',
12410                         'for' :  id,
12411                         cls : 'control-label',
12412                         cn : [
12413                             {
12414                                 tag : 'span',
12415                                 html : this.fieldLabel
12416                             },
12417                             indicator
12418                         ]
12419                     },
12420                     {
12421                         cls : "", 
12422                         cn: [
12423                             combobox
12424                         ]
12425                     }
12426
12427                 ];
12428                 
12429                 labelCfg = cfg.cn[0];
12430                 contentCfg = cfg.cn[1];
12431             }
12432             
12433             if(this.labelWidth > 12){
12434                 labelCfg.style = "width: " + this.labelWidth + 'px';
12435             }
12436             
12437             if(this.labelWidth < 13 && this.labelmd == 0){
12438                 this.labelmd = this.labelWidth;
12439             }
12440             
12441             if(this.labellg > 0){
12442                 labelCfg.cls += ' col-lg-' + this.labellg;
12443                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12444             }
12445             
12446             if(this.labelmd > 0){
12447                 labelCfg.cls += ' col-md-' + this.labelmd;
12448                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12449             }
12450             
12451             if(this.labelsm > 0){
12452                 labelCfg.cls += ' col-sm-' + this.labelsm;
12453                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12454             }
12455             
12456             if(this.labelxs > 0){
12457                 labelCfg.cls += ' col-xs-' + this.labelxs;
12458                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12459             }
12460             
12461         } else if ( this.fieldLabel.length) {
12462 //                Roo.log(" label");
12463             cfg.cn = [
12464                 indicator,
12465                {
12466                    tag: 'label',
12467                    //cls : 'input-group-addon',
12468                    html : this.fieldLabel
12469
12470                },
12471
12472                combobox
12473
12474             ];
12475             
12476             if(this.indicatorpos == 'right'){
12477                 
12478                 cfg.cn = [
12479                     {
12480                        tag: 'label',
12481                        cn : [
12482                            {
12483                                tag : 'span',
12484                                html : this.fieldLabel
12485                            },
12486                            indicator
12487                        ]
12488
12489                     },
12490                     combobox
12491
12492                 ];
12493
12494             }
12495
12496         } else {
12497             
12498 //                Roo.log(" no label && no align");
12499                 cfg = combobox
12500                      
12501                 
12502         }
12503         
12504         var settings=this;
12505         ['xs','sm','md','lg'].map(function(size){
12506             if (settings[size]) {
12507                 cfg.cls += ' col-' + size + '-' + settings[size];
12508             }
12509         });
12510         
12511         return cfg;
12512         
12513     },
12514     
12515     
12516     
12517     // private
12518     onResize : function(w, h){
12519 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12520 //        if(typeof w == 'number'){
12521 //            var x = w - this.trigger.getWidth();
12522 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12523 //            this.trigger.setStyle('left', x+'px');
12524 //        }
12525     },
12526
12527     // private
12528     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12529
12530     // private
12531     getResizeEl : function(){
12532         return this.inputEl();
12533     },
12534
12535     // private
12536     getPositionEl : function(){
12537         return this.inputEl();
12538     },
12539
12540     // private
12541     alignErrorIcon : function(){
12542         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12543     },
12544
12545     // private
12546     initEvents : function(){
12547         
12548         this.createList();
12549         
12550         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12551         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12552         if(!this.multiple && this.showToggleBtn){
12553             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12554             if(this.hideTrigger){
12555                 this.trigger.setDisplayed(false);
12556             }
12557             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12558         }
12559         
12560         if(this.multiple){
12561             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12562         }
12563         
12564         if(this.removable && !this.editable && !this.tickable){
12565             var close = this.closeTriggerEl();
12566             
12567             if(close){
12568                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12569                 close.on('click', this.removeBtnClick, this, close);
12570             }
12571         }
12572         
12573         //this.trigger.addClassOnOver('x-form-trigger-over');
12574         //this.trigger.addClassOnClick('x-form-trigger-click');
12575         
12576         //if(!this.width){
12577         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12578         //}
12579     },
12580     
12581     closeTriggerEl : function()
12582     {
12583         var close = this.el.select('.roo-combo-removable-btn', true).first();
12584         return close ? close : false;
12585     },
12586     
12587     removeBtnClick : function(e, h, el)
12588     {
12589         e.preventDefault();
12590         
12591         if(this.fireEvent("remove", this) !== false){
12592             this.reset();
12593             this.fireEvent("afterremove", this)
12594         }
12595     },
12596     
12597     createList : function()
12598     {
12599         this.list = Roo.get(document.body).createChild({
12600             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12601             cls: 'typeahead typeahead-long dropdown-menu shadow',
12602             style: 'display:none'
12603         });
12604         
12605         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12606         
12607     },
12608
12609     // private
12610     initTrigger : function(){
12611        
12612     },
12613
12614     // private
12615     onDestroy : function(){
12616         if(this.trigger){
12617             this.trigger.removeAllListeners();
12618           //  this.trigger.remove();
12619         }
12620         //if(this.wrap){
12621         //    this.wrap.remove();
12622         //}
12623         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12624     },
12625
12626     // private
12627     onFocus : function(){
12628         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12629         /*
12630         if(!this.mimicing){
12631             this.wrap.addClass('x-trigger-wrap-focus');
12632             this.mimicing = true;
12633             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12634             if(this.monitorTab){
12635                 this.el.on("keydown", this.checkTab, this);
12636             }
12637         }
12638         */
12639     },
12640
12641     // private
12642     checkTab : function(e){
12643         if(e.getKey() == e.TAB){
12644             this.triggerBlur();
12645         }
12646     },
12647
12648     // private
12649     onBlur : function(){
12650         // do nothing
12651     },
12652
12653     // private
12654     mimicBlur : function(e, t){
12655         /*
12656         if(!this.wrap.contains(t) && this.validateBlur()){
12657             this.triggerBlur();
12658         }
12659         */
12660     },
12661
12662     // private
12663     triggerBlur : function(){
12664         this.mimicing = false;
12665         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12666         if(this.monitorTab){
12667             this.el.un("keydown", this.checkTab, this);
12668         }
12669         //this.wrap.removeClass('x-trigger-wrap-focus');
12670         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12671     },
12672
12673     // private
12674     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12675     validateBlur : function(e, t){
12676         return true;
12677     },
12678
12679     // private
12680     onDisable : function(){
12681         this.inputEl().dom.disabled = true;
12682         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12683         //if(this.wrap){
12684         //    this.wrap.addClass('x-item-disabled');
12685         //}
12686     },
12687
12688     // private
12689     onEnable : function(){
12690         this.inputEl().dom.disabled = false;
12691         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12692         //if(this.wrap){
12693         //    this.el.removeClass('x-item-disabled');
12694         //}
12695     },
12696
12697     // private
12698     onShow : function(){
12699         var ae = this.getActionEl();
12700         
12701         if(ae){
12702             ae.dom.style.display = '';
12703             ae.dom.style.visibility = 'visible';
12704         }
12705     },
12706
12707     // private
12708     
12709     onHide : function(){
12710         var ae = this.getActionEl();
12711         ae.dom.style.display = 'none';
12712     },
12713
12714     /**
12715      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12716      * by an implementing function.
12717      * @method
12718      * @param {EventObject} e
12719      */
12720     onTriggerClick : Roo.emptyFn
12721 });
12722  
12723 /*
12724 * Licence: LGPL
12725 */
12726
12727 /**
12728  * @class Roo.bootstrap.CardUploader
12729  * @extends Roo.bootstrap.Button
12730  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12731  * @cfg {Number} errorTimeout default 3000
12732  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12733  * @cfg {Array}  html The button text.
12734
12735  *
12736  * @constructor
12737  * Create a new CardUploader
12738  * @param {Object} config The config object
12739  */
12740
12741 Roo.bootstrap.CardUploader = function(config){
12742     
12743  
12744     
12745     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12746     
12747     
12748     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12749         return r.data.id
12750      });
12751     
12752      this.addEvents({
12753          // raw events
12754         /**
12755          * @event preview
12756          * When a image is clicked on - and needs to display a slideshow or similar..
12757          * @param {Roo.bootstrap.Card} this
12758          * @param {Object} The image information data 
12759          *
12760          */
12761         'preview' : true,
12762          /**
12763          * @event download
12764          * When a the download link is clicked
12765          * @param {Roo.bootstrap.Card} this
12766          * @param {Object} The image information data  contains 
12767          */
12768         'download' : true
12769         
12770     });
12771 };
12772  
12773 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12774     
12775      
12776     errorTimeout : 3000,
12777      
12778     images : false,
12779    
12780     fileCollection : false,
12781     allowBlank : true,
12782     
12783     getAutoCreate : function()
12784     {
12785         
12786         var cfg =  {
12787             cls :'form-group' ,
12788             cn : [
12789                
12790                 {
12791                     tag: 'label',
12792                    //cls : 'input-group-addon',
12793                     html : this.fieldLabel
12794
12795                 },
12796
12797                 {
12798                     tag: 'input',
12799                     type : 'hidden',
12800                     name : this.name,
12801                     value : this.value,
12802                     cls : 'd-none  form-control'
12803                 },
12804                 
12805                 {
12806                     tag: 'input',
12807                     multiple : 'multiple',
12808                     type : 'file',
12809                     cls : 'd-none  roo-card-upload-selector'
12810                 },
12811                 
12812                 {
12813                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12814                 },
12815                 {
12816                     cls : 'card-columns roo-card-uploader-container'
12817                 }
12818
12819             ]
12820         };
12821            
12822          
12823         return cfg;
12824     },
12825     
12826     getChildContainer : function() /// what children are added to.
12827     {
12828         return this.containerEl;
12829     },
12830    
12831     getButtonContainer : function() /// what children are added to.
12832     {
12833         return this.el.select(".roo-card-uploader-button-container").first();
12834     },
12835    
12836     initEvents : function()
12837     {
12838         
12839         Roo.bootstrap.Input.prototype.initEvents.call(this);
12840         
12841         var t = this;
12842         this.addxtype({
12843             xns: Roo.bootstrap,
12844
12845             xtype : 'Button',
12846             container_method : 'getButtonContainer' ,            
12847             html :  this.html, // fix changable?
12848             cls : 'w-100 ',
12849             listeners : {
12850                 'click' : function(btn, e) {
12851                     t.onClick(e);
12852                 }
12853             }
12854         });
12855         
12856         
12857         
12858         
12859         this.urlAPI = (window.createObjectURL && window) || 
12860                                 (window.URL && URL.revokeObjectURL && URL) || 
12861                                 (window.webkitURL && webkitURL);
12862                         
12863          
12864          
12865          
12866         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12867         
12868         this.selectorEl.on('change', this.onFileSelected, this);
12869         if (this.images) {
12870             var t = this;
12871             this.images.forEach(function(img) {
12872                 t.addCard(img)
12873             });
12874             this.images = false;
12875         }
12876         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12877          
12878        
12879     },
12880     
12881    
12882     onClick : function(e)
12883     {
12884         e.preventDefault();
12885          
12886         this.selectorEl.dom.click();
12887          
12888     },
12889     
12890     onFileSelected : function(e)
12891     {
12892         e.preventDefault();
12893         
12894         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12895             return;
12896         }
12897         
12898         Roo.each(this.selectorEl.dom.files, function(file){    
12899             this.addFile(file);
12900         }, this);
12901          
12902     },
12903     
12904       
12905     
12906       
12907     
12908     addFile : function(file)
12909     {
12910            
12911         if(typeof(file) === 'string'){
12912             throw "Add file by name?"; // should not happen
12913             return;
12914         }
12915         
12916         if(!file || !this.urlAPI){
12917             return;
12918         }
12919         
12920         // file;
12921         // file.type;
12922         
12923         var _this = this;
12924         
12925         
12926         var url = _this.urlAPI.createObjectURL( file);
12927            
12928         this.addCard({
12929             id : Roo.bootstrap.CardUploader.ID--,
12930             is_uploaded : false,
12931             src : url,
12932             srcfile : file,
12933             title : file.name,
12934             mimetype : file.type,
12935             preview : false,
12936             is_deleted : 0
12937         });
12938         
12939     },
12940     
12941     /**
12942      * addCard - add an Attachment to the uploader
12943      * @param data - the data about the image to upload
12944      *
12945      * {
12946           id : 123
12947           title : "Title of file",
12948           is_uploaded : false,
12949           src : "http://.....",
12950           srcfile : { the File upload object },
12951           mimetype : file.type,
12952           preview : false,
12953           is_deleted : 0
12954           .. any other data...
12955         }
12956      *
12957      * 
12958     */
12959     
12960     addCard : function (data)
12961     {
12962         // hidden input element?
12963         // if the file is not an image...
12964         //then we need to use something other that and header_image
12965         var t = this;
12966         //   remove.....
12967         var footer = [
12968             {
12969                 xns : Roo.bootstrap,
12970                 xtype : 'CardFooter',
12971                  items: [
12972                     {
12973                         xns : Roo.bootstrap,
12974                         xtype : 'Element',
12975                         cls : 'd-flex',
12976                         items : [
12977                             
12978                             {
12979                                 xns : Roo.bootstrap,
12980                                 xtype : 'Button',
12981                                 html : String.format("<small>{0}</small>", data.title),
12982                                 cls : 'col-10 text-left',
12983                                 size: 'sm',
12984                                 weight: 'link',
12985                                 fa : 'download',
12986                                 listeners : {
12987                                     click : function() {
12988                                      
12989                                         t.fireEvent( "download", t, data );
12990                                     }
12991                                 }
12992                             },
12993                           
12994                             {
12995                                 xns : Roo.bootstrap,
12996                                 xtype : 'Button',
12997                                 style: 'max-height: 28px; ',
12998                                 size : 'sm',
12999                                 weight: 'danger',
13000                                 cls : 'col-2',
13001                                 fa : 'times',
13002                                 listeners : {
13003                                     click : function() {
13004                                         t.removeCard(data.id)
13005                                     }
13006                                 }
13007                             }
13008                         ]
13009                     }
13010                     
13011                 ] 
13012             }
13013             
13014         ];
13015         
13016         var cn = this.addxtype(
13017             {
13018                  
13019                 xns : Roo.bootstrap,
13020                 xtype : 'Card',
13021                 closeable : true,
13022                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13023                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13024                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13025                 data : data,
13026                 html : false,
13027                  
13028                 items : footer,
13029                 initEvents : function() {
13030                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13031                     var card = this;
13032                     this.imgEl = this.el.select('.card-img-top').first();
13033                     if (this.imgEl) {
13034                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13035                         this.imgEl.set({ 'pointer' : 'cursor' });
13036                                   
13037                     }
13038                     this.getCardFooter().addClass('p-1');
13039                     
13040                   
13041                 }
13042                 
13043             }
13044         );
13045         // dont' really need ot update items.
13046         // this.items.push(cn);
13047         this.fileCollection.add(cn);
13048         
13049         if (!data.srcfile) {
13050             this.updateInput();
13051             return;
13052         }
13053             
13054         var _t = this;
13055         var reader = new FileReader();
13056         reader.addEventListener("load", function() {  
13057             data.srcdata =  reader.result;
13058             _t.updateInput();
13059         });
13060         reader.readAsDataURL(data.srcfile);
13061         
13062         
13063         
13064     },
13065     removeCard : function(id)
13066     {
13067         
13068         var card  = this.fileCollection.get(id);
13069         card.data.is_deleted = 1;
13070         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13071         //this.fileCollection.remove(card);
13072         //this.items = this.items.filter(function(e) { return e != card });
13073         // dont' really need ot update items.
13074         card.el.dom.parentNode.removeChild(card.el.dom);
13075         this.updateInput();
13076
13077         
13078     },
13079     reset: function()
13080     {
13081         this.fileCollection.each(function(card) {
13082             if (card.el.dom && card.el.dom.parentNode) {
13083                 card.el.dom.parentNode.removeChild(card.el.dom);
13084             }
13085         });
13086         this.fileCollection.clear();
13087         this.updateInput();
13088     },
13089     
13090     updateInput : function()
13091     {
13092          var data = [];
13093         this.fileCollection.each(function(e) {
13094             data.push(e.data);
13095             
13096         });
13097         this.inputEl().dom.value = JSON.stringify(data);
13098         
13099         
13100         
13101     }
13102     
13103     
13104 });
13105
13106
13107 Roo.bootstrap.CardUploader.ID = -1;/*
13108  * Based on:
13109  * Ext JS Library 1.1.1
13110  * Copyright(c) 2006-2007, Ext JS, LLC.
13111  *
13112  * Originally Released Under LGPL - original licence link has changed is not relivant.
13113  *
13114  * Fork - LGPL
13115  * <script type="text/javascript">
13116  */
13117
13118
13119 /**
13120  * @class Roo.data.SortTypes
13121  * @singleton
13122  * Defines the default sorting (casting?) comparison functions used when sorting data.
13123  */
13124 Roo.data.SortTypes = {
13125     /**
13126      * Default sort that does nothing
13127      * @param {Mixed} s The value being converted
13128      * @return {Mixed} The comparison value
13129      */
13130     none : function(s){
13131         return s;
13132     },
13133     
13134     /**
13135      * The regular expression used to strip tags
13136      * @type {RegExp}
13137      * @property
13138      */
13139     stripTagsRE : /<\/?[^>]+>/gi,
13140     
13141     /**
13142      * Strips all HTML tags to sort on text only
13143      * @param {Mixed} s The value being converted
13144      * @return {String} The comparison value
13145      */
13146     asText : function(s){
13147         return String(s).replace(this.stripTagsRE, "");
13148     },
13149     
13150     /**
13151      * Strips all HTML tags to sort on text only - Case insensitive
13152      * @param {Mixed} s The value being converted
13153      * @return {String} The comparison value
13154      */
13155     asUCText : function(s){
13156         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13157     },
13158     
13159     /**
13160      * Case insensitive string
13161      * @param {Mixed} s The value being converted
13162      * @return {String} The comparison value
13163      */
13164     asUCString : function(s) {
13165         return String(s).toUpperCase();
13166     },
13167     
13168     /**
13169      * Date sorting
13170      * @param {Mixed} s The value being converted
13171      * @return {Number} The comparison value
13172      */
13173     asDate : function(s) {
13174         if(!s){
13175             return 0;
13176         }
13177         if(s instanceof Date){
13178             return s.getTime();
13179         }
13180         return Date.parse(String(s));
13181     },
13182     
13183     /**
13184      * Float sorting
13185      * @param {Mixed} s The value being converted
13186      * @return {Float} The comparison value
13187      */
13188     asFloat : function(s) {
13189         var val = parseFloat(String(s).replace(/,/g, ""));
13190         if(isNaN(val)) {
13191             val = 0;
13192         }
13193         return val;
13194     },
13195     
13196     /**
13197      * Integer sorting
13198      * @param {Mixed} s The value being converted
13199      * @return {Number} The comparison value
13200      */
13201     asInt : function(s) {
13202         var val = parseInt(String(s).replace(/,/g, ""));
13203         if(isNaN(val)) {
13204             val = 0;
13205         }
13206         return val;
13207     }
13208 };/*
13209  * Based on:
13210  * Ext JS Library 1.1.1
13211  * Copyright(c) 2006-2007, Ext JS, LLC.
13212  *
13213  * Originally Released Under LGPL - original licence link has changed is not relivant.
13214  *
13215  * Fork - LGPL
13216  * <script type="text/javascript">
13217  */
13218
13219 /**
13220 * @class Roo.data.Record
13221  * Instances of this class encapsulate both record <em>definition</em> information, and record
13222  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13223  * to access Records cached in an {@link Roo.data.Store} object.<br>
13224  * <p>
13225  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13226  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13227  * objects.<br>
13228  * <p>
13229  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13230  * @constructor
13231  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13232  * {@link #create}. The parameters are the same.
13233  * @param {Array} data An associative Array of data values keyed by the field name.
13234  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13235  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13236  * not specified an integer id is generated.
13237  */
13238 Roo.data.Record = function(data, id){
13239     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13240     this.data = data;
13241 };
13242
13243 /**
13244  * Generate a constructor for a specific record layout.
13245  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13246  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13247  * Each field definition object may contain the following properties: <ul>
13248  * <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,
13249  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13250  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13251  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13252  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13253  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13254  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13255  * this may be omitted.</p></li>
13256  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13257  * <ul><li>auto (Default, implies no conversion)</li>
13258  * <li>string</li>
13259  * <li>int</li>
13260  * <li>float</li>
13261  * <li>boolean</li>
13262  * <li>date</li></ul></p></li>
13263  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13264  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13265  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13266  * by the Reader into an object that will be stored in the Record. It is passed the
13267  * following parameters:<ul>
13268  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13269  * </ul></p></li>
13270  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13271  * </ul>
13272  * <br>usage:<br><pre><code>
13273 var TopicRecord = Roo.data.Record.create(
13274     {name: 'title', mapping: 'topic_title'},
13275     {name: 'author', mapping: 'username'},
13276     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13277     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13278     {name: 'lastPoster', mapping: 'user2'},
13279     {name: 'excerpt', mapping: 'post_text'}
13280 );
13281
13282 var myNewRecord = new TopicRecord({
13283     title: 'Do my job please',
13284     author: 'noobie',
13285     totalPosts: 1,
13286     lastPost: new Date(),
13287     lastPoster: 'Animal',
13288     excerpt: 'No way dude!'
13289 });
13290 myStore.add(myNewRecord);
13291 </code></pre>
13292  * @method create
13293  * @static
13294  */
13295 Roo.data.Record.create = function(o){
13296     var f = function(){
13297         f.superclass.constructor.apply(this, arguments);
13298     };
13299     Roo.extend(f, Roo.data.Record);
13300     var p = f.prototype;
13301     p.fields = new Roo.util.MixedCollection(false, function(field){
13302         return field.name;
13303     });
13304     for(var i = 0, len = o.length; i < len; i++){
13305         p.fields.add(new Roo.data.Field(o[i]));
13306     }
13307     f.getField = function(name){
13308         return p.fields.get(name);  
13309     };
13310     return f;
13311 };
13312
13313 Roo.data.Record.AUTO_ID = 1000;
13314 Roo.data.Record.EDIT = 'edit';
13315 Roo.data.Record.REJECT = 'reject';
13316 Roo.data.Record.COMMIT = 'commit';
13317
13318 Roo.data.Record.prototype = {
13319     /**
13320      * Readonly flag - true if this record has been modified.
13321      * @type Boolean
13322      */
13323     dirty : false,
13324     editing : false,
13325     error: null,
13326     modified: null,
13327
13328     // private
13329     join : function(store){
13330         this.store = store;
13331     },
13332
13333     /**
13334      * Set the named field to the specified value.
13335      * @param {String} name The name of the field to set.
13336      * @param {Object} value The value to set the field to.
13337      */
13338     set : function(name, value){
13339         if(this.data[name] == value){
13340             return;
13341         }
13342         this.dirty = true;
13343         if(!this.modified){
13344             this.modified = {};
13345         }
13346         if(typeof this.modified[name] == 'undefined'){
13347             this.modified[name] = this.data[name];
13348         }
13349         this.data[name] = value;
13350         if(!this.editing && this.store){
13351             this.store.afterEdit(this);
13352         }       
13353     },
13354
13355     /**
13356      * Get the value of the named field.
13357      * @param {String} name The name of the field to get the value of.
13358      * @return {Object} The value of the field.
13359      */
13360     get : function(name){
13361         return this.data[name]; 
13362     },
13363
13364     // private
13365     beginEdit : function(){
13366         this.editing = true;
13367         this.modified = {}; 
13368     },
13369
13370     // private
13371     cancelEdit : function(){
13372         this.editing = false;
13373         delete this.modified;
13374     },
13375
13376     // private
13377     endEdit : function(){
13378         this.editing = false;
13379         if(this.dirty && this.store){
13380             this.store.afterEdit(this);
13381         }
13382     },
13383
13384     /**
13385      * Usually called by the {@link Roo.data.Store} which owns the Record.
13386      * Rejects all changes made to the Record since either creation, or the last commit operation.
13387      * Modified fields are reverted to their original values.
13388      * <p>
13389      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13390      * of reject operations.
13391      */
13392     reject : function(){
13393         var m = this.modified;
13394         for(var n in m){
13395             if(typeof m[n] != "function"){
13396                 this.data[n] = m[n];
13397             }
13398         }
13399         this.dirty = false;
13400         delete this.modified;
13401         this.editing = false;
13402         if(this.store){
13403             this.store.afterReject(this);
13404         }
13405     },
13406
13407     /**
13408      * Usually called by the {@link Roo.data.Store} which owns the Record.
13409      * Commits all changes made to the Record since either creation, or the last commit operation.
13410      * <p>
13411      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13412      * of commit operations.
13413      */
13414     commit : function(){
13415         this.dirty = false;
13416         delete this.modified;
13417         this.editing = false;
13418         if(this.store){
13419             this.store.afterCommit(this);
13420         }
13421     },
13422
13423     // private
13424     hasError : function(){
13425         return this.error != null;
13426     },
13427
13428     // private
13429     clearError : function(){
13430         this.error = null;
13431     },
13432
13433     /**
13434      * Creates a copy of this record.
13435      * @param {String} id (optional) A new record id if you don't want to use this record's id
13436      * @return {Record}
13437      */
13438     copy : function(newId) {
13439         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13440     }
13441 };/*
13442  * Based on:
13443  * Ext JS Library 1.1.1
13444  * Copyright(c) 2006-2007, Ext JS, LLC.
13445  *
13446  * Originally Released Under LGPL - original licence link has changed is not relivant.
13447  *
13448  * Fork - LGPL
13449  * <script type="text/javascript">
13450  */
13451
13452
13453
13454 /**
13455  * @class Roo.data.Store
13456  * @extends Roo.util.Observable
13457  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13458  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13459  * <p>
13460  * 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
13461  * has no knowledge of the format of the data returned by the Proxy.<br>
13462  * <p>
13463  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13464  * instances from the data object. These records are cached and made available through accessor functions.
13465  * @constructor
13466  * Creates a new Store.
13467  * @param {Object} config A config object containing the objects needed for the Store to access data,
13468  * and read the data into Records.
13469  */
13470 Roo.data.Store = function(config){
13471     this.data = new Roo.util.MixedCollection(false);
13472     this.data.getKey = function(o){
13473         return o.id;
13474     };
13475     this.baseParams = {};
13476     // private
13477     this.paramNames = {
13478         "start" : "start",
13479         "limit" : "limit",
13480         "sort" : "sort",
13481         "dir" : "dir",
13482         "multisort" : "_multisort"
13483     };
13484
13485     if(config && config.data){
13486         this.inlineData = config.data;
13487         delete config.data;
13488     }
13489
13490     Roo.apply(this, config);
13491     
13492     if(this.reader){ // reader passed
13493         this.reader = Roo.factory(this.reader, Roo.data);
13494         this.reader.xmodule = this.xmodule || false;
13495         if(!this.recordType){
13496             this.recordType = this.reader.recordType;
13497         }
13498         if(this.reader.onMetaChange){
13499             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13500         }
13501     }
13502
13503     if(this.recordType){
13504         this.fields = this.recordType.prototype.fields;
13505     }
13506     this.modified = [];
13507
13508     this.addEvents({
13509         /**
13510          * @event datachanged
13511          * Fires when the data cache has changed, and a widget which is using this Store
13512          * as a Record cache should refresh its view.
13513          * @param {Store} this
13514          */
13515         datachanged : true,
13516         /**
13517          * @event metachange
13518          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13519          * @param {Store} this
13520          * @param {Object} meta The JSON metadata
13521          */
13522         metachange : true,
13523         /**
13524          * @event add
13525          * Fires when Records have been added to the Store
13526          * @param {Store} this
13527          * @param {Roo.data.Record[]} records The array of Records added
13528          * @param {Number} index The index at which the record(s) were added
13529          */
13530         add : true,
13531         /**
13532          * @event remove
13533          * Fires when a Record has been removed from the Store
13534          * @param {Store} this
13535          * @param {Roo.data.Record} record The Record that was removed
13536          * @param {Number} index The index at which the record was removed
13537          */
13538         remove : true,
13539         /**
13540          * @event update
13541          * Fires when a Record has been updated
13542          * @param {Store} this
13543          * @param {Roo.data.Record} record The Record that was updated
13544          * @param {String} operation The update operation being performed.  Value may be one of:
13545          * <pre><code>
13546  Roo.data.Record.EDIT
13547  Roo.data.Record.REJECT
13548  Roo.data.Record.COMMIT
13549          * </code></pre>
13550          */
13551         update : true,
13552         /**
13553          * @event clear
13554          * Fires when the data cache has been cleared.
13555          * @param {Store} this
13556          */
13557         clear : true,
13558         /**
13559          * @event beforeload
13560          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13561          * the load action will be canceled.
13562          * @param {Store} this
13563          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13564          */
13565         beforeload : true,
13566         /**
13567          * @event beforeloadadd
13568          * Fires after a new set of Records has been loaded.
13569          * @param {Store} this
13570          * @param {Roo.data.Record[]} records The Records that were loaded
13571          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13572          */
13573         beforeloadadd : true,
13574         /**
13575          * @event load
13576          * Fires after a new set of Records has been loaded, before they are added to the store.
13577          * @param {Store} this
13578          * @param {Roo.data.Record[]} records The Records that were loaded
13579          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13580          * @params {Object} return from reader
13581          */
13582         load : true,
13583         /**
13584          * @event loadexception
13585          * Fires if an exception occurs in the Proxy during loading.
13586          * Called with the signature of the Proxy's "loadexception" event.
13587          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13588          * 
13589          * @param {Proxy} 
13590          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13591          * @param {Object} load options 
13592          * @param {Object} jsonData from your request (normally this contains the Exception)
13593          */
13594         loadexception : true
13595     });
13596     
13597     if(this.proxy){
13598         this.proxy = Roo.factory(this.proxy, Roo.data);
13599         this.proxy.xmodule = this.xmodule || false;
13600         this.relayEvents(this.proxy,  ["loadexception"]);
13601     }
13602     this.sortToggle = {};
13603     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13604
13605     Roo.data.Store.superclass.constructor.call(this);
13606
13607     if(this.inlineData){
13608         this.loadData(this.inlineData);
13609         delete this.inlineData;
13610     }
13611 };
13612
13613 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13614      /**
13615     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13616     * without a remote query - used by combo/forms at present.
13617     */
13618     
13619     /**
13620     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13621     */
13622     /**
13623     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13624     */
13625     /**
13626     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13627     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13628     */
13629     /**
13630     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13631     * on any HTTP request
13632     */
13633     /**
13634     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13635     */
13636     /**
13637     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13638     */
13639     multiSort: false,
13640     /**
13641     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13642     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13643     */
13644     remoteSort : false,
13645
13646     /**
13647     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13648      * loaded or when a record is removed. (defaults to false).
13649     */
13650     pruneModifiedRecords : false,
13651
13652     // private
13653     lastOptions : null,
13654
13655     /**
13656      * Add Records to the Store and fires the add event.
13657      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13658      */
13659     add : function(records){
13660         records = [].concat(records);
13661         for(var i = 0, len = records.length; i < len; i++){
13662             records[i].join(this);
13663         }
13664         var index = this.data.length;
13665         this.data.addAll(records);
13666         this.fireEvent("add", this, records, index);
13667     },
13668
13669     /**
13670      * Remove a Record from the Store and fires the remove event.
13671      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13672      */
13673     remove : function(record){
13674         var index = this.data.indexOf(record);
13675         this.data.removeAt(index);
13676  
13677         if(this.pruneModifiedRecords){
13678             this.modified.remove(record);
13679         }
13680         this.fireEvent("remove", this, record, index);
13681     },
13682
13683     /**
13684      * Remove all Records from the Store and fires the clear event.
13685      */
13686     removeAll : function(){
13687         this.data.clear();
13688         if(this.pruneModifiedRecords){
13689             this.modified = [];
13690         }
13691         this.fireEvent("clear", this);
13692     },
13693
13694     /**
13695      * Inserts Records to the Store at the given index and fires the add event.
13696      * @param {Number} index The start index at which to insert the passed Records.
13697      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13698      */
13699     insert : function(index, records){
13700         records = [].concat(records);
13701         for(var i = 0, len = records.length; i < len; i++){
13702             this.data.insert(index, records[i]);
13703             records[i].join(this);
13704         }
13705         this.fireEvent("add", this, records, index);
13706     },
13707
13708     /**
13709      * Get the index within the cache of the passed Record.
13710      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13711      * @return {Number} The index of the passed Record. Returns -1 if not found.
13712      */
13713     indexOf : function(record){
13714         return this.data.indexOf(record);
13715     },
13716
13717     /**
13718      * Get the index within the cache of the Record with the passed id.
13719      * @param {String} id The id of the Record to find.
13720      * @return {Number} The index of the Record. Returns -1 if not found.
13721      */
13722     indexOfId : function(id){
13723         return this.data.indexOfKey(id);
13724     },
13725
13726     /**
13727      * Get the Record with the specified id.
13728      * @param {String} id The id of the Record to find.
13729      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13730      */
13731     getById : function(id){
13732         return this.data.key(id);
13733     },
13734
13735     /**
13736      * Get the Record at the specified index.
13737      * @param {Number} index The index of the Record to find.
13738      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13739      */
13740     getAt : function(index){
13741         return this.data.itemAt(index);
13742     },
13743
13744     /**
13745      * Returns a range of Records between specified indices.
13746      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13747      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13748      * @return {Roo.data.Record[]} An array of Records
13749      */
13750     getRange : function(start, end){
13751         return this.data.getRange(start, end);
13752     },
13753
13754     // private
13755     storeOptions : function(o){
13756         o = Roo.apply({}, o);
13757         delete o.callback;
13758         delete o.scope;
13759         this.lastOptions = o;
13760     },
13761
13762     /**
13763      * Loads the Record cache from the configured Proxy using the configured Reader.
13764      * <p>
13765      * If using remote paging, then the first load call must specify the <em>start</em>
13766      * and <em>limit</em> properties in the options.params property to establish the initial
13767      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13768      * <p>
13769      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13770      * and this call will return before the new data has been loaded. Perform any post-processing
13771      * in a callback function, or in a "load" event handler.</strong>
13772      * <p>
13773      * @param {Object} options An object containing properties which control loading options:<ul>
13774      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13775      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13776      * passed the following arguments:<ul>
13777      * <li>r : Roo.data.Record[]</li>
13778      * <li>options: Options object from the load call</li>
13779      * <li>success: Boolean success indicator</li></ul></li>
13780      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13781      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13782      * </ul>
13783      */
13784     load : function(options){
13785         options = options || {};
13786         if(this.fireEvent("beforeload", this, options) !== false){
13787             this.storeOptions(options);
13788             var p = Roo.apply(options.params || {}, this.baseParams);
13789             // if meta was not loaded from remote source.. try requesting it.
13790             if (!this.reader.metaFromRemote) {
13791                 p._requestMeta = 1;
13792             }
13793             if(this.sortInfo && this.remoteSort){
13794                 var pn = this.paramNames;
13795                 p[pn["sort"]] = this.sortInfo.field;
13796                 p[pn["dir"]] = this.sortInfo.direction;
13797             }
13798             if (this.multiSort) {
13799                 var pn = this.paramNames;
13800                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13801             }
13802             
13803             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13804         }
13805     },
13806
13807     /**
13808      * Reloads the Record cache from the configured Proxy using the configured Reader and
13809      * the options from the last load operation performed.
13810      * @param {Object} options (optional) An object containing properties which may override the options
13811      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13812      * the most recently used options are reused).
13813      */
13814     reload : function(options){
13815         this.load(Roo.applyIf(options||{}, this.lastOptions));
13816     },
13817
13818     // private
13819     // Called as a callback by the Reader during a load operation.
13820     loadRecords : function(o, options, success){
13821         if(!o || success === false){
13822             if(success !== false){
13823                 this.fireEvent("load", this, [], options, o);
13824             }
13825             if(options.callback){
13826                 options.callback.call(options.scope || this, [], options, false);
13827             }
13828             return;
13829         }
13830         // if data returned failure - throw an exception.
13831         if (o.success === false) {
13832             // show a message if no listener is registered.
13833             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13834                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13835             }
13836             // loadmask wil be hooked into this..
13837             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13838             return;
13839         }
13840         var r = o.records, t = o.totalRecords || r.length;
13841         
13842         this.fireEvent("beforeloadadd", this, r, options, o);
13843         
13844         if(!options || options.add !== true){
13845             if(this.pruneModifiedRecords){
13846                 this.modified = [];
13847             }
13848             for(var i = 0, len = r.length; i < len; i++){
13849                 r[i].join(this);
13850             }
13851             if(this.snapshot){
13852                 this.data = this.snapshot;
13853                 delete this.snapshot;
13854             }
13855             this.data.clear();
13856             this.data.addAll(r);
13857             this.totalLength = t;
13858             this.applySort();
13859             this.fireEvent("datachanged", this);
13860         }else{
13861             this.totalLength = Math.max(t, this.data.length+r.length);
13862             this.add(r);
13863         }
13864         
13865         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13866                 
13867             var e = new Roo.data.Record({});
13868
13869             e.set(this.parent.displayField, this.parent.emptyTitle);
13870             e.set(this.parent.valueField, '');
13871
13872             this.insert(0, e);
13873         }
13874             
13875         this.fireEvent("load", this, r, options, o);
13876         if(options.callback){
13877             options.callback.call(options.scope || this, r, options, true);
13878         }
13879     },
13880
13881
13882     /**
13883      * Loads data from a passed data block. A Reader which understands the format of the data
13884      * must have been configured in the constructor.
13885      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13886      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13887      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13888      */
13889     loadData : function(o, append){
13890         var r = this.reader.readRecords(o);
13891         this.loadRecords(r, {add: append}, true);
13892     },
13893     
13894      /**
13895      * using 'cn' the nested child reader read the child array into it's child stores.
13896      * @param {Object} rec The record with a 'children array
13897      */
13898     loadDataFromChildren : function(rec)
13899     {
13900         this.loadData(this.reader.toLoadData(rec));
13901     },
13902     
13903
13904     /**
13905      * Gets the number of cached records.
13906      * <p>
13907      * <em>If using paging, this may not be the total size of the dataset. If the data object
13908      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13909      * the data set size</em>
13910      */
13911     getCount : function(){
13912         return this.data.length || 0;
13913     },
13914
13915     /**
13916      * Gets the total number of records in the dataset as returned by the server.
13917      * <p>
13918      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13919      * the dataset size</em>
13920      */
13921     getTotalCount : function(){
13922         return this.totalLength || 0;
13923     },
13924
13925     /**
13926      * Returns the sort state of the Store as an object with two properties:
13927      * <pre><code>
13928  field {String} The name of the field by which the Records are sorted
13929  direction {String} The sort order, "ASC" or "DESC"
13930      * </code></pre>
13931      */
13932     getSortState : function(){
13933         return this.sortInfo;
13934     },
13935
13936     // private
13937     applySort : function(){
13938         if(this.sortInfo && !this.remoteSort){
13939             var s = this.sortInfo, f = s.field;
13940             var st = this.fields.get(f).sortType;
13941             var fn = function(r1, r2){
13942                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13943                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13944             };
13945             this.data.sort(s.direction, fn);
13946             if(this.snapshot && this.snapshot != this.data){
13947                 this.snapshot.sort(s.direction, fn);
13948             }
13949         }
13950     },
13951
13952     /**
13953      * Sets the default sort column and order to be used by the next load operation.
13954      * @param {String} fieldName The name of the field to sort by.
13955      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13956      */
13957     setDefaultSort : function(field, dir){
13958         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13959     },
13960
13961     /**
13962      * Sort the Records.
13963      * If remote sorting is used, the sort is performed on the server, and the cache is
13964      * reloaded. If local sorting is used, the cache is sorted internally.
13965      * @param {String} fieldName The name of the field to sort by.
13966      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13967      */
13968     sort : function(fieldName, dir){
13969         var f = this.fields.get(fieldName);
13970         if(!dir){
13971             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13972             
13973             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13974                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13975             }else{
13976                 dir = f.sortDir;
13977             }
13978         }
13979         this.sortToggle[f.name] = dir;
13980         this.sortInfo = {field: f.name, direction: dir};
13981         if(!this.remoteSort){
13982             this.applySort();
13983             this.fireEvent("datachanged", this);
13984         }else{
13985             this.load(this.lastOptions);
13986         }
13987     },
13988
13989     /**
13990      * Calls the specified function for each of the Records in the cache.
13991      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13992      * Returning <em>false</em> aborts and exits the iteration.
13993      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13994      */
13995     each : function(fn, scope){
13996         this.data.each(fn, scope);
13997     },
13998
13999     /**
14000      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14001      * (e.g., during paging).
14002      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14003      */
14004     getModifiedRecords : function(){
14005         return this.modified;
14006     },
14007
14008     // private
14009     createFilterFn : function(property, value, anyMatch){
14010         if(!value.exec){ // not a regex
14011             value = String(value);
14012             if(value.length == 0){
14013                 return false;
14014             }
14015             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14016         }
14017         return function(r){
14018             return value.test(r.data[property]);
14019         };
14020     },
14021
14022     /**
14023      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14024      * @param {String} property A field on your records
14025      * @param {Number} start The record index to start at (defaults to 0)
14026      * @param {Number} end The last record index to include (defaults to length - 1)
14027      * @return {Number} The sum
14028      */
14029     sum : function(property, start, end){
14030         var rs = this.data.items, v = 0;
14031         start = start || 0;
14032         end = (end || end === 0) ? end : rs.length-1;
14033
14034         for(var i = start; i <= end; i++){
14035             v += (rs[i].data[property] || 0);
14036         }
14037         return v;
14038     },
14039
14040     /**
14041      * Filter the records by a specified property.
14042      * @param {String} field A field on your records
14043      * @param {String/RegExp} value Either a string that the field
14044      * should start with or a RegExp to test against the field
14045      * @param {Boolean} anyMatch True to match any part not just the beginning
14046      */
14047     filter : function(property, value, anyMatch){
14048         var fn = this.createFilterFn(property, value, anyMatch);
14049         return fn ? this.filterBy(fn) : this.clearFilter();
14050     },
14051
14052     /**
14053      * Filter by a function. The specified function will be called with each
14054      * record in this data source. If the function returns true the record is included,
14055      * otherwise it is filtered.
14056      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14057      * @param {Object} scope (optional) The scope of the function (defaults to this)
14058      */
14059     filterBy : function(fn, scope){
14060         this.snapshot = this.snapshot || this.data;
14061         this.data = this.queryBy(fn, scope||this);
14062         this.fireEvent("datachanged", this);
14063     },
14064
14065     /**
14066      * Query the records by a specified property.
14067      * @param {String} field A field on your records
14068      * @param {String/RegExp} value Either a string that the field
14069      * should start with or a RegExp to test against the field
14070      * @param {Boolean} anyMatch True to match any part not just the beginning
14071      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14072      */
14073     query : function(property, value, anyMatch){
14074         var fn = this.createFilterFn(property, value, anyMatch);
14075         return fn ? this.queryBy(fn) : this.data.clone();
14076     },
14077
14078     /**
14079      * Query by a function. The specified function will be called with each
14080      * record in this data source. If the function returns true the record is included
14081      * in the results.
14082      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14083      * @param {Object} scope (optional) The scope of the function (defaults to this)
14084       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14085      **/
14086     queryBy : function(fn, scope){
14087         var data = this.snapshot || this.data;
14088         return data.filterBy(fn, scope||this);
14089     },
14090
14091     /**
14092      * Collects unique values for a particular dataIndex from this store.
14093      * @param {String} dataIndex The property to collect
14094      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14095      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14096      * @return {Array} An array of the unique values
14097      **/
14098     collect : function(dataIndex, allowNull, bypassFilter){
14099         var d = (bypassFilter === true && this.snapshot) ?
14100                 this.snapshot.items : this.data.items;
14101         var v, sv, r = [], l = {};
14102         for(var i = 0, len = d.length; i < len; i++){
14103             v = d[i].data[dataIndex];
14104             sv = String(v);
14105             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14106                 l[sv] = true;
14107                 r[r.length] = v;
14108             }
14109         }
14110         return r;
14111     },
14112
14113     /**
14114      * Revert to a view of the Record cache with no filtering applied.
14115      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14116      */
14117     clearFilter : function(suppressEvent){
14118         if(this.snapshot && this.snapshot != this.data){
14119             this.data = this.snapshot;
14120             delete this.snapshot;
14121             if(suppressEvent !== true){
14122                 this.fireEvent("datachanged", this);
14123             }
14124         }
14125     },
14126
14127     // private
14128     afterEdit : function(record){
14129         if(this.modified.indexOf(record) == -1){
14130             this.modified.push(record);
14131         }
14132         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14133     },
14134     
14135     // private
14136     afterReject : function(record){
14137         this.modified.remove(record);
14138         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14139     },
14140
14141     // private
14142     afterCommit : function(record){
14143         this.modified.remove(record);
14144         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14145     },
14146
14147     /**
14148      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14149      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14150      */
14151     commitChanges : function(){
14152         var m = this.modified.slice(0);
14153         this.modified = [];
14154         for(var i = 0, len = m.length; i < len; i++){
14155             m[i].commit();
14156         }
14157     },
14158
14159     /**
14160      * Cancel outstanding changes on all changed records.
14161      */
14162     rejectChanges : function(){
14163         var m = this.modified.slice(0);
14164         this.modified = [];
14165         for(var i = 0, len = m.length; i < len; i++){
14166             m[i].reject();
14167         }
14168     },
14169
14170     onMetaChange : function(meta, rtype, o){
14171         this.recordType = rtype;
14172         this.fields = rtype.prototype.fields;
14173         delete this.snapshot;
14174         this.sortInfo = meta.sortInfo || this.sortInfo;
14175         this.modified = [];
14176         this.fireEvent('metachange', this, this.reader.meta);
14177     },
14178     
14179     moveIndex : function(data, type)
14180     {
14181         var index = this.indexOf(data);
14182         
14183         var newIndex = index + type;
14184         
14185         this.remove(data);
14186         
14187         this.insert(newIndex, data);
14188         
14189     }
14190 });/*
14191  * Based on:
14192  * Ext JS Library 1.1.1
14193  * Copyright(c) 2006-2007, Ext JS, LLC.
14194  *
14195  * Originally Released Under LGPL - original licence link has changed is not relivant.
14196  *
14197  * Fork - LGPL
14198  * <script type="text/javascript">
14199  */
14200
14201 /**
14202  * @class Roo.data.SimpleStore
14203  * @extends Roo.data.Store
14204  * Small helper class to make creating Stores from Array data easier.
14205  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14206  * @cfg {Array} fields An array of field definition objects, or field name strings.
14207  * @cfg {Object} an existing reader (eg. copied from another store)
14208  * @cfg {Array} data The multi-dimensional array of data
14209  * @constructor
14210  * @param {Object} config
14211  */
14212 Roo.data.SimpleStore = function(config)
14213 {
14214     Roo.data.SimpleStore.superclass.constructor.call(this, {
14215         isLocal : true,
14216         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14217                 id: config.id
14218             },
14219             Roo.data.Record.create(config.fields)
14220         ),
14221         proxy : new Roo.data.MemoryProxy(config.data)
14222     });
14223     this.load();
14224 };
14225 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14226  * Based on:
14227  * Ext JS Library 1.1.1
14228  * Copyright(c) 2006-2007, Ext JS, LLC.
14229  *
14230  * Originally Released Under LGPL - original licence link has changed is not relivant.
14231  *
14232  * Fork - LGPL
14233  * <script type="text/javascript">
14234  */
14235
14236 /**
14237 /**
14238  * @extends Roo.data.Store
14239  * @class Roo.data.JsonStore
14240  * Small helper class to make creating Stores for JSON data easier. <br/>
14241 <pre><code>
14242 var store = new Roo.data.JsonStore({
14243     url: 'get-images.php',
14244     root: 'images',
14245     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14246 });
14247 </code></pre>
14248  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14249  * JsonReader and HttpProxy (unless inline data is provided).</b>
14250  * @cfg {Array} fields An array of field definition objects, or field name strings.
14251  * @constructor
14252  * @param {Object} config
14253  */
14254 Roo.data.JsonStore = function(c){
14255     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14256         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14257         reader: new Roo.data.JsonReader(c, c.fields)
14258     }));
14259 };
14260 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14261  * Based on:
14262  * Ext JS Library 1.1.1
14263  * Copyright(c) 2006-2007, Ext JS, LLC.
14264  *
14265  * Originally Released Under LGPL - original licence link has changed is not relivant.
14266  *
14267  * Fork - LGPL
14268  * <script type="text/javascript">
14269  */
14270
14271  
14272 Roo.data.Field = function(config){
14273     if(typeof config == "string"){
14274         config = {name: config};
14275     }
14276     Roo.apply(this, config);
14277     
14278     if(!this.type){
14279         this.type = "auto";
14280     }
14281     
14282     var st = Roo.data.SortTypes;
14283     // named sortTypes are supported, here we look them up
14284     if(typeof this.sortType == "string"){
14285         this.sortType = st[this.sortType];
14286     }
14287     
14288     // set default sortType for strings and dates
14289     if(!this.sortType){
14290         switch(this.type){
14291             case "string":
14292                 this.sortType = st.asUCString;
14293                 break;
14294             case "date":
14295                 this.sortType = st.asDate;
14296                 break;
14297             default:
14298                 this.sortType = st.none;
14299         }
14300     }
14301
14302     // define once
14303     var stripRe = /[\$,%]/g;
14304
14305     // prebuilt conversion function for this field, instead of
14306     // switching every time we're reading a value
14307     if(!this.convert){
14308         var cv, dateFormat = this.dateFormat;
14309         switch(this.type){
14310             case "":
14311             case "auto":
14312             case undefined:
14313                 cv = function(v){ return v; };
14314                 break;
14315             case "string":
14316                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14317                 break;
14318             case "int":
14319                 cv = function(v){
14320                     return v !== undefined && v !== null && v !== '' ?
14321                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14322                     };
14323                 break;
14324             case "float":
14325                 cv = function(v){
14326                     return v !== undefined && v !== null && v !== '' ?
14327                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14328                     };
14329                 break;
14330             case "bool":
14331             case "boolean":
14332                 cv = function(v){ return v === true || v === "true" || v == 1; };
14333                 break;
14334             case "date":
14335                 cv = function(v){
14336                     if(!v){
14337                         return '';
14338                     }
14339                     if(v instanceof Date){
14340                         return v;
14341                     }
14342                     if(dateFormat){
14343                         if(dateFormat == "timestamp"){
14344                             return new Date(v*1000);
14345                         }
14346                         return Date.parseDate(v, dateFormat);
14347                     }
14348                     var parsed = Date.parse(v);
14349                     return parsed ? new Date(parsed) : null;
14350                 };
14351              break;
14352             
14353         }
14354         this.convert = cv;
14355     }
14356 };
14357
14358 Roo.data.Field.prototype = {
14359     dateFormat: null,
14360     defaultValue: "",
14361     mapping: null,
14362     sortType : null,
14363     sortDir : "ASC"
14364 };/*
14365  * Based on:
14366  * Ext JS Library 1.1.1
14367  * Copyright(c) 2006-2007, Ext JS, LLC.
14368  *
14369  * Originally Released Under LGPL - original licence link has changed is not relivant.
14370  *
14371  * Fork - LGPL
14372  * <script type="text/javascript">
14373  */
14374  
14375 // Base class for reading structured data from a data source.  This class is intended to be
14376 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14377
14378 /**
14379  * @class Roo.data.DataReader
14380  * Base class for reading structured data from a data source.  This class is intended to be
14381  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14382  */
14383
14384 Roo.data.DataReader = function(meta, recordType){
14385     
14386     this.meta = meta;
14387     
14388     this.recordType = recordType instanceof Array ? 
14389         Roo.data.Record.create(recordType) : recordType;
14390 };
14391
14392 Roo.data.DataReader.prototype = {
14393     
14394     
14395     readerType : 'Data',
14396      /**
14397      * Create an empty record
14398      * @param {Object} data (optional) - overlay some values
14399      * @return {Roo.data.Record} record created.
14400      */
14401     newRow :  function(d) {
14402         var da =  {};
14403         this.recordType.prototype.fields.each(function(c) {
14404             switch( c.type) {
14405                 case 'int' : da[c.name] = 0; break;
14406                 case 'date' : da[c.name] = new Date(); break;
14407                 case 'float' : da[c.name] = 0.0; break;
14408                 case 'boolean' : da[c.name] = false; break;
14409                 default : da[c.name] = ""; break;
14410             }
14411             
14412         });
14413         return new this.recordType(Roo.apply(da, d));
14414     }
14415     
14416     
14417 };/*
14418  * Based on:
14419  * Ext JS Library 1.1.1
14420  * Copyright(c) 2006-2007, Ext JS, LLC.
14421  *
14422  * Originally Released Under LGPL - original licence link has changed is not relivant.
14423  *
14424  * Fork - LGPL
14425  * <script type="text/javascript">
14426  */
14427
14428 /**
14429  * @class Roo.data.DataProxy
14430  * @extends Roo.data.Observable
14431  * This class is an abstract base class for implementations which provide retrieval of
14432  * unformatted data objects.<br>
14433  * <p>
14434  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14435  * (of the appropriate type which knows how to parse the data object) to provide a block of
14436  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14437  * <p>
14438  * Custom implementations must implement the load method as described in
14439  * {@link Roo.data.HttpProxy#load}.
14440  */
14441 Roo.data.DataProxy = function(){
14442     this.addEvents({
14443         /**
14444          * @event beforeload
14445          * Fires before a network request is made to retrieve a data object.
14446          * @param {Object} This DataProxy object.
14447          * @param {Object} params The params parameter to the load function.
14448          */
14449         beforeload : true,
14450         /**
14451          * @event load
14452          * Fires before the load method's callback is called.
14453          * @param {Object} This DataProxy object.
14454          * @param {Object} o The data object.
14455          * @param {Object} arg The callback argument object passed to the load function.
14456          */
14457         load : true,
14458         /**
14459          * @event loadexception
14460          * Fires if an Exception occurs during data retrieval.
14461          * @param {Object} This DataProxy object.
14462          * @param {Object} o The data object.
14463          * @param {Object} arg The callback argument object passed to the load function.
14464          * @param {Object} e The Exception.
14465          */
14466         loadexception : true
14467     });
14468     Roo.data.DataProxy.superclass.constructor.call(this);
14469 };
14470
14471 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14472
14473     /**
14474      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14475      */
14476 /*
14477  * Based on:
14478  * Ext JS Library 1.1.1
14479  * Copyright(c) 2006-2007, Ext JS, LLC.
14480  *
14481  * Originally Released Under LGPL - original licence link has changed is not relivant.
14482  *
14483  * Fork - LGPL
14484  * <script type="text/javascript">
14485  */
14486 /**
14487  * @class Roo.data.MemoryProxy
14488  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14489  * to the Reader when its load method is called.
14490  * @constructor
14491  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14492  */
14493 Roo.data.MemoryProxy = function(data){
14494     if (data.data) {
14495         data = data.data;
14496     }
14497     Roo.data.MemoryProxy.superclass.constructor.call(this);
14498     this.data = data;
14499 };
14500
14501 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14502     
14503     /**
14504      * Load data from the requested source (in this case an in-memory
14505      * data object passed to the constructor), read the data object into
14506      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14507      * process that block using the passed callback.
14508      * @param {Object} params This parameter is not used by the MemoryProxy class.
14509      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14510      * object into a block of Roo.data.Records.
14511      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14512      * The function must be passed <ul>
14513      * <li>The Record block object</li>
14514      * <li>The "arg" argument from the load function</li>
14515      * <li>A boolean success indicator</li>
14516      * </ul>
14517      * @param {Object} scope The scope in which to call the callback
14518      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14519      */
14520     load : function(params, reader, callback, scope, arg){
14521         params = params || {};
14522         var result;
14523         try {
14524             result = reader.readRecords(params.data ? params.data :this.data);
14525         }catch(e){
14526             this.fireEvent("loadexception", this, arg, null, e);
14527             callback.call(scope, null, arg, false);
14528             return;
14529         }
14530         callback.call(scope, result, arg, true);
14531     },
14532     
14533     // private
14534     update : function(params, records){
14535         
14536     }
14537 });/*
14538  * Based on:
14539  * Ext JS Library 1.1.1
14540  * Copyright(c) 2006-2007, Ext JS, LLC.
14541  *
14542  * Originally Released Under LGPL - original licence link has changed is not relivant.
14543  *
14544  * Fork - LGPL
14545  * <script type="text/javascript">
14546  */
14547 /**
14548  * @class Roo.data.HttpProxy
14549  * @extends Roo.data.DataProxy
14550  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14551  * configured to reference a certain URL.<br><br>
14552  * <p>
14553  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14554  * from which the running page was served.<br><br>
14555  * <p>
14556  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14557  * <p>
14558  * Be aware that to enable the browser to parse an XML document, the server must set
14559  * the Content-Type header in the HTTP response to "text/xml".
14560  * @constructor
14561  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14562  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14563  * will be used to make the request.
14564  */
14565 Roo.data.HttpProxy = function(conn){
14566     Roo.data.HttpProxy.superclass.constructor.call(this);
14567     // is conn a conn config or a real conn?
14568     this.conn = conn;
14569     this.useAjax = !conn || !conn.events;
14570   
14571 };
14572
14573 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14574     // thse are take from connection...
14575     
14576     /**
14577      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14578      */
14579     /**
14580      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14581      * extra parameters to each request made by this object. (defaults to undefined)
14582      */
14583     /**
14584      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14585      *  to each request made by this object. (defaults to undefined)
14586      */
14587     /**
14588      * @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)
14589      */
14590     /**
14591      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14592      */
14593      /**
14594      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14595      * @type Boolean
14596      */
14597   
14598
14599     /**
14600      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14601      * @type Boolean
14602      */
14603     /**
14604      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14605      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14606      * a finer-grained basis than the DataProxy events.
14607      */
14608     getConnection : function(){
14609         return this.useAjax ? Roo.Ajax : this.conn;
14610     },
14611
14612     /**
14613      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14614      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14615      * process that block using the passed callback.
14616      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14617      * for the request to the remote server.
14618      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14619      * object into a block of Roo.data.Records.
14620      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14621      * The function must be passed <ul>
14622      * <li>The Record block object</li>
14623      * <li>The "arg" argument from the load function</li>
14624      * <li>A boolean success indicator</li>
14625      * </ul>
14626      * @param {Object} scope The scope in which to call the callback
14627      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14628      */
14629     load : function(params, reader, callback, scope, arg){
14630         if(this.fireEvent("beforeload", this, params) !== false){
14631             var  o = {
14632                 params : params || {},
14633                 request: {
14634                     callback : callback,
14635                     scope : scope,
14636                     arg : arg
14637                 },
14638                 reader: reader,
14639                 callback : this.loadResponse,
14640                 scope: this
14641             };
14642             if(this.useAjax){
14643                 Roo.applyIf(o, this.conn);
14644                 if(this.activeRequest){
14645                     Roo.Ajax.abort(this.activeRequest);
14646                 }
14647                 this.activeRequest = Roo.Ajax.request(o);
14648             }else{
14649                 this.conn.request(o);
14650             }
14651         }else{
14652             callback.call(scope||this, null, arg, false);
14653         }
14654     },
14655
14656     // private
14657     loadResponse : function(o, success, response){
14658         delete this.activeRequest;
14659         if(!success){
14660             this.fireEvent("loadexception", this, o, response);
14661             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14662             return;
14663         }
14664         var result;
14665         try {
14666             result = o.reader.read(response);
14667         }catch(e){
14668             this.fireEvent("loadexception", this, o, response, e);
14669             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14670             return;
14671         }
14672         
14673         this.fireEvent("load", this, o, o.request.arg);
14674         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14675     },
14676
14677     // private
14678     update : function(dataSet){
14679
14680     },
14681
14682     // private
14683     updateResponse : function(dataSet){
14684
14685     }
14686 });/*
14687  * Based on:
14688  * Ext JS Library 1.1.1
14689  * Copyright(c) 2006-2007, Ext JS, LLC.
14690  *
14691  * Originally Released Under LGPL - original licence link has changed is not relivant.
14692  *
14693  * Fork - LGPL
14694  * <script type="text/javascript">
14695  */
14696
14697 /**
14698  * @class Roo.data.ScriptTagProxy
14699  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14700  * other than the originating domain of the running page.<br><br>
14701  * <p>
14702  * <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
14703  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14704  * <p>
14705  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14706  * source code that is used as the source inside a &lt;script> tag.<br><br>
14707  * <p>
14708  * In order for the browser to process the returned data, the server must wrap the data object
14709  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14710  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14711  * depending on whether the callback name was passed:
14712  * <p>
14713  * <pre><code>
14714 boolean scriptTag = false;
14715 String cb = request.getParameter("callback");
14716 if (cb != null) {
14717     scriptTag = true;
14718     response.setContentType("text/javascript");
14719 } else {
14720     response.setContentType("application/x-json");
14721 }
14722 Writer out = response.getWriter();
14723 if (scriptTag) {
14724     out.write(cb + "(");
14725 }
14726 out.print(dataBlock.toJsonString());
14727 if (scriptTag) {
14728     out.write(");");
14729 }
14730 </pre></code>
14731  *
14732  * @constructor
14733  * @param {Object} config A configuration object.
14734  */
14735 Roo.data.ScriptTagProxy = function(config){
14736     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14737     Roo.apply(this, config);
14738     this.head = document.getElementsByTagName("head")[0];
14739 };
14740
14741 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14742
14743 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14744     /**
14745      * @cfg {String} url The URL from which to request the data object.
14746      */
14747     /**
14748      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14749      */
14750     timeout : 30000,
14751     /**
14752      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14753      * the server the name of the callback function set up by the load call to process the returned data object.
14754      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14755      * javascript output which calls this named function passing the data object as its only parameter.
14756      */
14757     callbackParam : "callback",
14758     /**
14759      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14760      * name to the request.
14761      */
14762     nocache : true,
14763
14764     /**
14765      * Load data from the configured URL, read the data object into
14766      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14767      * process that block using the passed callback.
14768      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14769      * for the request to the remote server.
14770      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14771      * object into a block of Roo.data.Records.
14772      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14773      * The function must be passed <ul>
14774      * <li>The Record block object</li>
14775      * <li>The "arg" argument from the load function</li>
14776      * <li>A boolean success indicator</li>
14777      * </ul>
14778      * @param {Object} scope The scope in which to call the callback
14779      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14780      */
14781     load : function(params, reader, callback, scope, arg){
14782         if(this.fireEvent("beforeload", this, params) !== false){
14783
14784             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14785
14786             var url = this.url;
14787             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14788             if(this.nocache){
14789                 url += "&_dc=" + (new Date().getTime());
14790             }
14791             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14792             var trans = {
14793                 id : transId,
14794                 cb : "stcCallback"+transId,
14795                 scriptId : "stcScript"+transId,
14796                 params : params,
14797                 arg : arg,
14798                 url : url,
14799                 callback : callback,
14800                 scope : scope,
14801                 reader : reader
14802             };
14803             var conn = this;
14804
14805             window[trans.cb] = function(o){
14806                 conn.handleResponse(o, trans);
14807             };
14808
14809             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14810
14811             if(this.autoAbort !== false){
14812                 this.abort();
14813             }
14814
14815             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14816
14817             var script = document.createElement("script");
14818             script.setAttribute("src", url);
14819             script.setAttribute("type", "text/javascript");
14820             script.setAttribute("id", trans.scriptId);
14821             this.head.appendChild(script);
14822
14823             this.trans = trans;
14824         }else{
14825             callback.call(scope||this, null, arg, false);
14826         }
14827     },
14828
14829     // private
14830     isLoading : function(){
14831         return this.trans ? true : false;
14832     },
14833
14834     /**
14835      * Abort the current server request.
14836      */
14837     abort : function(){
14838         if(this.isLoading()){
14839             this.destroyTrans(this.trans);
14840         }
14841     },
14842
14843     // private
14844     destroyTrans : function(trans, isLoaded){
14845         this.head.removeChild(document.getElementById(trans.scriptId));
14846         clearTimeout(trans.timeoutId);
14847         if(isLoaded){
14848             window[trans.cb] = undefined;
14849             try{
14850                 delete window[trans.cb];
14851             }catch(e){}
14852         }else{
14853             // if hasn't been loaded, wait for load to remove it to prevent script error
14854             window[trans.cb] = function(){
14855                 window[trans.cb] = undefined;
14856                 try{
14857                     delete window[trans.cb];
14858                 }catch(e){}
14859             };
14860         }
14861     },
14862
14863     // private
14864     handleResponse : function(o, trans){
14865         this.trans = false;
14866         this.destroyTrans(trans, true);
14867         var result;
14868         try {
14869             result = trans.reader.readRecords(o);
14870         }catch(e){
14871             this.fireEvent("loadexception", this, o, trans.arg, e);
14872             trans.callback.call(trans.scope||window, null, trans.arg, false);
14873             return;
14874         }
14875         this.fireEvent("load", this, o, trans.arg);
14876         trans.callback.call(trans.scope||window, result, trans.arg, true);
14877     },
14878
14879     // private
14880     handleFailure : function(trans){
14881         this.trans = false;
14882         this.destroyTrans(trans, false);
14883         this.fireEvent("loadexception", this, null, trans.arg);
14884         trans.callback.call(trans.scope||window, null, trans.arg, false);
14885     }
14886 });/*
14887  * Based on:
14888  * Ext JS Library 1.1.1
14889  * Copyright(c) 2006-2007, Ext JS, LLC.
14890  *
14891  * Originally Released Under LGPL - original licence link has changed is not relivant.
14892  *
14893  * Fork - LGPL
14894  * <script type="text/javascript">
14895  */
14896
14897 /**
14898  * @class Roo.data.JsonReader
14899  * @extends Roo.data.DataReader
14900  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14901  * based on mappings in a provided Roo.data.Record constructor.
14902  * 
14903  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14904  * in the reply previously. 
14905  * 
14906  * <p>
14907  * Example code:
14908  * <pre><code>
14909 var RecordDef = Roo.data.Record.create([
14910     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14911     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14912 ]);
14913 var myReader = new Roo.data.JsonReader({
14914     totalProperty: "results",    // The property which contains the total dataset size (optional)
14915     root: "rows",                // The property which contains an Array of row objects
14916     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14917 }, RecordDef);
14918 </code></pre>
14919  * <p>
14920  * This would consume a JSON file like this:
14921  * <pre><code>
14922 { 'results': 2, 'rows': [
14923     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14924     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14925 }
14926 </code></pre>
14927  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14928  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14929  * paged from the remote server.
14930  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14931  * @cfg {String} root name of the property which contains the Array of row objects.
14932  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14933  * @cfg {Array} fields Array of field definition objects
14934  * @constructor
14935  * Create a new JsonReader
14936  * @param {Object} meta Metadata configuration options
14937  * @param {Object} recordType Either an Array of field definition objects,
14938  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14939  */
14940 Roo.data.JsonReader = function(meta, recordType){
14941     
14942     meta = meta || {};
14943     // set some defaults:
14944     Roo.applyIf(meta, {
14945         totalProperty: 'total',
14946         successProperty : 'success',
14947         root : 'data',
14948         id : 'id'
14949     });
14950     
14951     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14952 };
14953 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14954     
14955     readerType : 'Json',
14956     
14957     /**
14958      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14959      * Used by Store query builder to append _requestMeta to params.
14960      * 
14961      */
14962     metaFromRemote : false,
14963     /**
14964      * This method is only used by a DataProxy which has retrieved data from a remote server.
14965      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14966      * @return {Object} data A data block which is used by an Roo.data.Store object as
14967      * a cache of Roo.data.Records.
14968      */
14969     read : function(response){
14970         var json = response.responseText;
14971        
14972         var o = /* eval:var:o */ eval("("+json+")");
14973         if(!o) {
14974             throw {message: "JsonReader.read: Json object not found"};
14975         }
14976         
14977         if(o.metaData){
14978             
14979             delete this.ef;
14980             this.metaFromRemote = true;
14981             this.meta = o.metaData;
14982             this.recordType = Roo.data.Record.create(o.metaData.fields);
14983             this.onMetaChange(this.meta, this.recordType, o);
14984         }
14985         return this.readRecords(o);
14986     },
14987
14988     // private function a store will implement
14989     onMetaChange : function(meta, recordType, o){
14990
14991     },
14992
14993     /**
14994          * @ignore
14995          */
14996     simpleAccess: function(obj, subsc) {
14997         return obj[subsc];
14998     },
14999
15000         /**
15001          * @ignore
15002          */
15003     getJsonAccessor: function(){
15004         var re = /[\[\.]/;
15005         return function(expr) {
15006             try {
15007                 return(re.test(expr))
15008                     ? new Function("obj", "return obj." + expr)
15009                     : function(obj){
15010                         return obj[expr];
15011                     };
15012             } catch(e){}
15013             return Roo.emptyFn;
15014         };
15015     }(),
15016
15017     /**
15018      * Create a data block containing Roo.data.Records from an XML document.
15019      * @param {Object} o An object which contains an Array of row objects in the property specified
15020      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15021      * which contains the total size of the dataset.
15022      * @return {Object} data A data block which is used by an Roo.data.Store object as
15023      * a cache of Roo.data.Records.
15024      */
15025     readRecords : function(o){
15026         /**
15027          * After any data loads, the raw JSON data is available for further custom processing.
15028          * @type Object
15029          */
15030         this.o = o;
15031         var s = this.meta, Record = this.recordType,
15032             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15033
15034 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15035         if (!this.ef) {
15036             if(s.totalProperty) {
15037                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15038                 }
15039                 if(s.successProperty) {
15040                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15041                 }
15042                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15043                 if (s.id) {
15044                         var g = this.getJsonAccessor(s.id);
15045                         this.getId = function(rec) {
15046                                 var r = g(rec);  
15047                                 return (r === undefined || r === "") ? null : r;
15048                         };
15049                 } else {
15050                         this.getId = function(){return null;};
15051                 }
15052             this.ef = [];
15053             for(var jj = 0; jj < fl; jj++){
15054                 f = fi[jj];
15055                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15056                 this.ef[jj] = this.getJsonAccessor(map);
15057             }
15058         }
15059
15060         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15061         if(s.totalProperty){
15062             var vt = parseInt(this.getTotal(o), 10);
15063             if(!isNaN(vt)){
15064                 totalRecords = vt;
15065             }
15066         }
15067         if(s.successProperty){
15068             var vs = this.getSuccess(o);
15069             if(vs === false || vs === 'false'){
15070                 success = false;
15071             }
15072         }
15073         var records = [];
15074         for(var i = 0; i < c; i++){
15075                 var n = root[i];
15076             var values = {};
15077             var id = this.getId(n);
15078             for(var j = 0; j < fl; j++){
15079                 f = fi[j];
15080             var v = this.ef[j](n);
15081             if (!f.convert) {
15082                 Roo.log('missing convert for ' + f.name);
15083                 Roo.log(f);
15084                 continue;
15085             }
15086             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15087             }
15088             var record = new Record(values, id);
15089             record.json = n;
15090             records[i] = record;
15091         }
15092         return {
15093             raw : o,
15094             success : success,
15095             records : records,
15096             totalRecords : totalRecords
15097         };
15098     },
15099     // used when loading children.. @see loadDataFromChildren
15100     toLoadData: function(rec)
15101     {
15102         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15103         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15104         return { data : data, total : data.length };
15105         
15106     }
15107 });/*
15108  * Based on:
15109  * Ext JS Library 1.1.1
15110  * Copyright(c) 2006-2007, Ext JS, LLC.
15111  *
15112  * Originally Released Under LGPL - original licence link has changed is not relivant.
15113  *
15114  * Fork - LGPL
15115  * <script type="text/javascript">
15116  */
15117
15118 /**
15119  * @class Roo.data.ArrayReader
15120  * @extends Roo.data.DataReader
15121  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15122  * Each element of that Array represents a row of data fields. The
15123  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15124  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15125  * <p>
15126  * Example code:.
15127  * <pre><code>
15128 var RecordDef = Roo.data.Record.create([
15129     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15130     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15131 ]);
15132 var myReader = new Roo.data.ArrayReader({
15133     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15134 }, RecordDef);
15135 </code></pre>
15136  * <p>
15137  * This would consume an Array like this:
15138  * <pre><code>
15139 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15140   </code></pre>
15141  
15142  * @constructor
15143  * Create a new JsonReader
15144  * @param {Object} meta Metadata configuration options.
15145  * @param {Object|Array} recordType Either an Array of field definition objects
15146  * 
15147  * @cfg {Array} fields Array of field definition objects
15148  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15149  * as specified to {@link Roo.data.Record#create},
15150  * or an {@link Roo.data.Record} object
15151  *
15152  * 
15153  * created using {@link Roo.data.Record#create}.
15154  */
15155 Roo.data.ArrayReader = function(meta, recordType)
15156 {    
15157     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15158 };
15159
15160 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15161     
15162       /**
15163      * Create a data block containing Roo.data.Records from an XML document.
15164      * @param {Object} o An Array of row objects which represents the dataset.
15165      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15166      * a cache of Roo.data.Records.
15167      */
15168     readRecords : function(o)
15169     {
15170         var sid = this.meta ? this.meta.id : null;
15171         var recordType = this.recordType, fields = recordType.prototype.fields;
15172         var records = [];
15173         var root = o;
15174         for(var i = 0; i < root.length; i++){
15175             var n = root[i];
15176             var values = {};
15177             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15178             for(var j = 0, jlen = fields.length; j < jlen; j++){
15179                 var f = fields.items[j];
15180                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15181                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15182                 v = f.convert(v);
15183                 values[f.name] = v;
15184             }
15185             var record = new recordType(values, id);
15186             record.json = n;
15187             records[records.length] = record;
15188         }
15189         return {
15190             records : records,
15191             totalRecords : records.length
15192         };
15193     },
15194     // used when loading children.. @see loadDataFromChildren
15195     toLoadData: function(rec)
15196     {
15197         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15198         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15199         
15200     }
15201     
15202     
15203 });/*
15204  * - LGPL
15205  * * 
15206  */
15207
15208 /**
15209  * @class Roo.bootstrap.ComboBox
15210  * @extends Roo.bootstrap.TriggerField
15211  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15212  * @cfg {Boolean} append (true|false) default false
15213  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15214  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15215  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15216  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15217  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15218  * @cfg {Boolean} animate default true
15219  * @cfg {Boolean} emptyResultText only for touch device
15220  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15221  * @cfg {String} emptyTitle default ''
15222  * @cfg {Number} width fixed with? experimental
15223  * @constructor
15224  * Create a new ComboBox.
15225  * @param {Object} config Configuration options
15226  */
15227 Roo.bootstrap.ComboBox = function(config){
15228     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15229     this.addEvents({
15230         /**
15231          * @event expand
15232          * Fires when the dropdown list is expanded
15233         * @param {Roo.bootstrap.ComboBox} combo This combo box
15234         */
15235         'expand' : true,
15236         /**
15237          * @event collapse
15238          * Fires when the dropdown list is collapsed
15239         * @param {Roo.bootstrap.ComboBox} combo This combo box
15240         */
15241         'collapse' : true,
15242         /**
15243          * @event beforeselect
15244          * Fires before a list item is selected. Return false to cancel the selection.
15245         * @param {Roo.bootstrap.ComboBox} combo This combo box
15246         * @param {Roo.data.Record} record The data record returned from the underlying store
15247         * @param {Number} index The index of the selected item in the dropdown list
15248         */
15249         'beforeselect' : true,
15250         /**
15251          * @event select
15252          * Fires when a list item is selected
15253         * @param {Roo.bootstrap.ComboBox} combo This combo box
15254         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15255         * @param {Number} index The index of the selected item in the dropdown list
15256         */
15257         'select' : true,
15258         /**
15259          * @event beforequery
15260          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15261          * The event object passed has these properties:
15262         * @param {Roo.bootstrap.ComboBox} combo This combo box
15263         * @param {String} query The query
15264         * @param {Boolean} forceAll true to force "all" query
15265         * @param {Boolean} cancel true to cancel the query
15266         * @param {Object} e The query event object
15267         */
15268         'beforequery': true,
15269          /**
15270          * @event add
15271          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15272         * @param {Roo.bootstrap.ComboBox} combo This combo box
15273         */
15274         'add' : true,
15275         /**
15276          * @event edit
15277          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15278         * @param {Roo.bootstrap.ComboBox} combo This combo box
15279         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15280         */
15281         'edit' : true,
15282         /**
15283          * @event remove
15284          * Fires when the remove value from the combobox array
15285         * @param {Roo.bootstrap.ComboBox} combo This combo box
15286         */
15287         'remove' : true,
15288         /**
15289          * @event afterremove
15290          * Fires when the remove value from the combobox array
15291         * @param {Roo.bootstrap.ComboBox} combo This combo box
15292         */
15293         'afterremove' : true,
15294         /**
15295          * @event specialfilter
15296          * Fires when specialfilter
15297             * @param {Roo.bootstrap.ComboBox} combo This combo box
15298             */
15299         'specialfilter' : true,
15300         /**
15301          * @event tick
15302          * Fires when tick the element
15303             * @param {Roo.bootstrap.ComboBox} combo This combo box
15304             */
15305         'tick' : true,
15306         /**
15307          * @event touchviewdisplay
15308          * Fires when touch view require special display (default is using displayField)
15309             * @param {Roo.bootstrap.ComboBox} combo This combo box
15310             * @param {Object} cfg set html .
15311             */
15312         'touchviewdisplay' : true
15313         
15314     });
15315     
15316     this.item = [];
15317     this.tickItems = [];
15318     
15319     this.selectedIndex = -1;
15320     if(this.mode == 'local'){
15321         if(config.queryDelay === undefined){
15322             this.queryDelay = 10;
15323         }
15324         if(config.minChars === undefined){
15325             this.minChars = 0;
15326         }
15327     }
15328 };
15329
15330 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15331      
15332     /**
15333      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15334      * rendering into an Roo.Editor, defaults to false)
15335      */
15336     /**
15337      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15338      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15339      */
15340     /**
15341      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15342      */
15343     /**
15344      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15345      * the dropdown list (defaults to undefined, with no header element)
15346      */
15347
15348      /**
15349      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15350      */
15351      
15352      /**
15353      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15354      */
15355     listWidth: undefined,
15356     /**
15357      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15358      * mode = 'remote' or 'text' if mode = 'local')
15359      */
15360     displayField: undefined,
15361     
15362     /**
15363      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15364      * mode = 'remote' or 'value' if mode = 'local'). 
15365      * Note: use of a valueField requires the user make a selection
15366      * in order for a value to be mapped.
15367      */
15368     valueField: undefined,
15369     /**
15370      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15371      */
15372     modalTitle : '',
15373     
15374     /**
15375      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15376      * field's data value (defaults to the underlying DOM element's name)
15377      */
15378     hiddenName: undefined,
15379     /**
15380      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15381      */
15382     listClass: '',
15383     /**
15384      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15385      */
15386     selectedClass: 'active',
15387     
15388     /**
15389      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15390      */
15391     shadow:'sides',
15392     /**
15393      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15394      * anchor positions (defaults to 'tl-bl')
15395      */
15396     listAlign: 'tl-bl?',
15397     /**
15398      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15399      */
15400     maxHeight: 300,
15401     /**
15402      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15403      * query specified by the allQuery config option (defaults to 'query')
15404      */
15405     triggerAction: 'query',
15406     /**
15407      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15408      * (defaults to 4, does not apply if editable = false)
15409      */
15410     minChars : 4,
15411     /**
15412      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15413      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15414      */
15415     typeAhead: false,
15416     /**
15417      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15418      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15419      */
15420     queryDelay: 500,
15421     /**
15422      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15423      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15424      */
15425     pageSize: 0,
15426     /**
15427      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15428      * when editable = true (defaults to false)
15429      */
15430     selectOnFocus:false,
15431     /**
15432      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15433      */
15434     queryParam: 'query',
15435     /**
15436      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15437      * when mode = 'remote' (defaults to 'Loading...')
15438      */
15439     loadingText: 'Loading...',
15440     /**
15441      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15442      */
15443     resizable: false,
15444     /**
15445      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15446      */
15447     handleHeight : 8,
15448     /**
15449      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15450      * traditional select (defaults to true)
15451      */
15452     editable: true,
15453     /**
15454      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15455      */
15456     allQuery: '',
15457     /**
15458      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15459      */
15460     mode: 'remote',
15461     /**
15462      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15463      * listWidth has a higher value)
15464      */
15465     minListWidth : 70,
15466     /**
15467      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15468      * allow the user to set arbitrary text into the field (defaults to false)
15469      */
15470     forceSelection:false,
15471     /**
15472      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15473      * if typeAhead = true (defaults to 250)
15474      */
15475     typeAheadDelay : 250,
15476     /**
15477      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15478      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15479      */
15480     valueNotFoundText : undefined,
15481     /**
15482      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15483      */
15484     blockFocus : false,
15485     
15486     /**
15487      * @cfg {Boolean} disableClear Disable showing of clear button.
15488      */
15489     disableClear : false,
15490     /**
15491      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15492      */
15493     alwaysQuery : false,
15494     
15495     /**
15496      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15497      */
15498     multiple : false,
15499     
15500     /**
15501      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15502      */
15503     invalidClass : "has-warning",
15504     
15505     /**
15506      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15507      */
15508     validClass : "has-success",
15509     
15510     /**
15511      * @cfg {Boolean} specialFilter (true|false) special filter default false
15512      */
15513     specialFilter : false,
15514     
15515     /**
15516      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15517      */
15518     mobileTouchView : true,
15519     
15520     /**
15521      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15522      */
15523     useNativeIOS : false,
15524     
15525     /**
15526      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15527      */
15528     mobile_restrict_height : false,
15529     
15530     ios_options : false,
15531     
15532     //private
15533     addicon : false,
15534     editicon: false,
15535     
15536     page: 0,
15537     hasQuery: false,
15538     append: false,
15539     loadNext: false,
15540     autoFocus : true,
15541     tickable : false,
15542     btnPosition : 'right',
15543     triggerList : true,
15544     showToggleBtn : true,
15545     animate : true,
15546     emptyResultText: 'Empty',
15547     triggerText : 'Select',
15548     emptyTitle : '',
15549     width : false,
15550     
15551     // element that contains real text value.. (when hidden is used..)
15552     
15553     getAutoCreate : function()
15554     {   
15555         var cfg = false;
15556         //render
15557         /*
15558          * Render classic select for iso
15559          */
15560         
15561         if(Roo.isIOS && this.useNativeIOS){
15562             cfg = this.getAutoCreateNativeIOS();
15563             return cfg;
15564         }
15565         
15566         /*
15567          * Touch Devices
15568          */
15569         
15570         if(Roo.isTouch && this.mobileTouchView){
15571             cfg = this.getAutoCreateTouchView();
15572             return cfg;;
15573         }
15574         
15575         /*
15576          *  Normal ComboBox
15577          */
15578         if(!this.tickable){
15579             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15580             return cfg;
15581         }
15582         
15583         /*
15584          *  ComboBox with tickable selections
15585          */
15586              
15587         var align = this.labelAlign || this.parentLabelAlign();
15588         
15589         cfg = {
15590             cls : 'form-group roo-combobox-tickable' //input-group
15591         };
15592         
15593         var btn_text_select = '';
15594         var btn_text_done = '';
15595         var btn_text_cancel = '';
15596         
15597         if (this.btn_text_show) {
15598             btn_text_select = 'Select';
15599             btn_text_done = 'Done';
15600             btn_text_cancel = 'Cancel'; 
15601         }
15602         
15603         var buttons = {
15604             tag : 'div',
15605             cls : 'tickable-buttons',
15606             cn : [
15607                 {
15608                     tag : 'button',
15609                     type : 'button',
15610                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15611                     //html : this.triggerText
15612                     html: btn_text_select
15613                 },
15614                 {
15615                     tag : 'button',
15616                     type : 'button',
15617                     name : 'ok',
15618                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15619                     //html : 'Done'
15620                     html: btn_text_done
15621                 },
15622                 {
15623                     tag : 'button',
15624                     type : 'button',
15625                     name : 'cancel',
15626                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15627                     //html : 'Cancel'
15628                     html: btn_text_cancel
15629                 }
15630             ]
15631         };
15632         
15633         if(this.editable){
15634             buttons.cn.unshift({
15635                 tag: 'input',
15636                 cls: 'roo-select2-search-field-input'
15637             });
15638         }
15639         
15640         var _this = this;
15641         
15642         Roo.each(buttons.cn, function(c){
15643             if (_this.size) {
15644                 c.cls += ' btn-' + _this.size;
15645             }
15646
15647             if (_this.disabled) {
15648                 c.disabled = true;
15649             }
15650         });
15651         
15652         var box = {
15653             tag: 'div',
15654             style : 'display: contents',
15655             cn: [
15656                 {
15657                     tag: 'input',
15658                     type : 'hidden',
15659                     cls: 'form-hidden-field'
15660                 },
15661                 {
15662                     tag: 'ul',
15663                     cls: 'roo-select2-choices',
15664                     cn:[
15665                         {
15666                             tag: 'li',
15667                             cls: 'roo-select2-search-field',
15668                             cn: [
15669                                 buttons
15670                             ]
15671                         }
15672                     ]
15673                 }
15674             ]
15675         };
15676         
15677         var combobox = {
15678             cls: 'roo-select2-container input-group roo-select2-container-multi',
15679             cn: [
15680                 
15681                 box
15682 //                {
15683 //                    tag: 'ul',
15684 //                    cls: 'typeahead typeahead-long dropdown-menu',
15685 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15686 //                }
15687             ]
15688         };
15689         
15690         if(this.hasFeedback && !this.allowBlank){
15691             
15692             var feedback = {
15693                 tag: 'span',
15694                 cls: 'glyphicon form-control-feedback'
15695             };
15696
15697             combobox.cn.push(feedback);
15698         }
15699         
15700         
15701         
15702         var indicator = {
15703             tag : 'i',
15704             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15705             tooltip : 'This field is required'
15706         };
15707         if (Roo.bootstrap.version == 4) {
15708             indicator = {
15709                 tag : 'i',
15710                 style : 'display:none'
15711             };
15712         }
15713         if (align ==='left' && this.fieldLabel.length) {
15714             
15715             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15716             
15717             cfg.cn = [
15718                 indicator,
15719                 {
15720                     tag: 'label',
15721                     'for' :  id,
15722                     cls : 'control-label col-form-label',
15723                     html : this.fieldLabel
15724
15725                 },
15726                 {
15727                     cls : "", 
15728                     cn: [
15729                         combobox
15730                     ]
15731                 }
15732
15733             ];
15734             
15735             var labelCfg = cfg.cn[1];
15736             var contentCfg = cfg.cn[2];
15737             
15738
15739             if(this.indicatorpos == 'right'){
15740                 
15741                 cfg.cn = [
15742                     {
15743                         tag: 'label',
15744                         'for' :  id,
15745                         cls : 'control-label col-form-label',
15746                         cn : [
15747                             {
15748                                 tag : 'span',
15749                                 html : this.fieldLabel
15750                             },
15751                             indicator
15752                         ]
15753                     },
15754                     {
15755                         cls : "",
15756                         cn: [
15757                             combobox
15758                         ]
15759                     }
15760
15761                 ];
15762                 
15763                 
15764                 
15765                 labelCfg = cfg.cn[0];
15766                 contentCfg = cfg.cn[1];
15767             
15768             }
15769             
15770             if(this.labelWidth > 12){
15771                 labelCfg.style = "width: " + this.labelWidth + 'px';
15772             }
15773             if(this.width * 1 > 0){
15774                 contentCfg.style = "width: " + this.width + 'px';
15775             }
15776             if(this.labelWidth < 13 && this.labelmd == 0){
15777                 this.labelmd = this.labelWidth;
15778             }
15779             
15780             if(this.labellg > 0){
15781                 labelCfg.cls += ' col-lg-' + this.labellg;
15782                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15783             }
15784             
15785             if(this.labelmd > 0){
15786                 labelCfg.cls += ' col-md-' + this.labelmd;
15787                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15788             }
15789             
15790             if(this.labelsm > 0){
15791                 labelCfg.cls += ' col-sm-' + this.labelsm;
15792                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15793             }
15794             
15795             if(this.labelxs > 0){
15796                 labelCfg.cls += ' col-xs-' + this.labelxs;
15797                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15798             }
15799                 
15800                 
15801         } else if ( this.fieldLabel.length) {
15802 //                Roo.log(" label");
15803                  cfg.cn = [
15804                    indicator,
15805                     {
15806                         tag: 'label',
15807                         //cls : 'input-group-addon',
15808                         html : this.fieldLabel
15809                     },
15810                     combobox
15811                 ];
15812                 
15813                 if(this.indicatorpos == 'right'){
15814                     cfg.cn = [
15815                         {
15816                             tag: 'label',
15817                             //cls : 'input-group-addon',
15818                             html : this.fieldLabel
15819                         },
15820                         indicator,
15821                         combobox
15822                     ];
15823                     
15824                 }
15825
15826         } else {
15827             
15828 //                Roo.log(" no label && no align");
15829                 cfg = combobox
15830                      
15831                 
15832         }
15833          
15834         var settings=this;
15835         ['xs','sm','md','lg'].map(function(size){
15836             if (settings[size]) {
15837                 cfg.cls += ' col-' + size + '-' + settings[size];
15838             }
15839         });
15840         
15841         return cfg;
15842         
15843     },
15844     
15845     _initEventsCalled : false,
15846     
15847     // private
15848     initEvents: function()
15849     {   
15850         if (this._initEventsCalled) { // as we call render... prevent looping...
15851             return;
15852         }
15853         this._initEventsCalled = true;
15854         
15855         if (!this.store) {
15856             throw "can not find store for combo";
15857         }
15858         
15859         this.indicator = this.indicatorEl();
15860         
15861         this.store = Roo.factory(this.store, Roo.data);
15862         this.store.parent = this;
15863         
15864         // if we are building from html. then this element is so complex, that we can not really
15865         // use the rendered HTML.
15866         // so we have to trash and replace the previous code.
15867         if (Roo.XComponent.build_from_html) {
15868             // remove this element....
15869             var e = this.el.dom, k=0;
15870             while (e ) { e = e.previousSibling;  ++k;}
15871
15872             this.el.remove();
15873             
15874             this.el=false;
15875             this.rendered = false;
15876             
15877             this.render(this.parent().getChildContainer(true), k);
15878         }
15879         
15880         if(Roo.isIOS && this.useNativeIOS){
15881             this.initIOSView();
15882             return;
15883         }
15884         
15885         /*
15886          * Touch Devices
15887          */
15888         
15889         if(Roo.isTouch && this.mobileTouchView){
15890             this.initTouchView();
15891             return;
15892         }
15893         
15894         if(this.tickable){
15895             this.initTickableEvents();
15896             return;
15897         }
15898         
15899         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15900         
15901         if(this.hiddenName){
15902             
15903             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15904             
15905             this.hiddenField.dom.value =
15906                 this.hiddenValue !== undefined ? this.hiddenValue :
15907                 this.value !== undefined ? this.value : '';
15908
15909             // prevent input submission
15910             this.el.dom.removeAttribute('name');
15911             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15912              
15913              
15914         }
15915         //if(Roo.isGecko){
15916         //    this.el.dom.setAttribute('autocomplete', 'off');
15917         //}
15918         
15919         var cls = 'x-combo-list';
15920         
15921         //this.list = new Roo.Layer({
15922         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15923         //});
15924         
15925         var _this = this;
15926         
15927         (function(){
15928             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15929             _this.list.setWidth(lw);
15930         }).defer(100);
15931         
15932         this.list.on('mouseover', this.onViewOver, this);
15933         this.list.on('mousemove', this.onViewMove, this);
15934         this.list.on('scroll', this.onViewScroll, this);
15935         
15936         /*
15937         this.list.swallowEvent('mousewheel');
15938         this.assetHeight = 0;
15939
15940         if(this.title){
15941             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15942             this.assetHeight += this.header.getHeight();
15943         }
15944
15945         this.innerList = this.list.createChild({cls:cls+'-inner'});
15946         this.innerList.on('mouseover', this.onViewOver, this);
15947         this.innerList.on('mousemove', this.onViewMove, this);
15948         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15949         
15950         if(this.allowBlank && !this.pageSize && !this.disableClear){
15951             this.footer = this.list.createChild({cls:cls+'-ft'});
15952             this.pageTb = new Roo.Toolbar(this.footer);
15953            
15954         }
15955         if(this.pageSize){
15956             this.footer = this.list.createChild({cls:cls+'-ft'});
15957             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15958                     {pageSize: this.pageSize});
15959             
15960         }
15961         
15962         if (this.pageTb && this.allowBlank && !this.disableClear) {
15963             var _this = this;
15964             this.pageTb.add(new Roo.Toolbar.Fill(), {
15965                 cls: 'x-btn-icon x-btn-clear',
15966                 text: '&#160;',
15967                 handler: function()
15968                 {
15969                     _this.collapse();
15970                     _this.clearValue();
15971                     _this.onSelect(false, -1);
15972                 }
15973             });
15974         }
15975         if (this.footer) {
15976             this.assetHeight += this.footer.getHeight();
15977         }
15978         */
15979             
15980         if(!this.tpl){
15981             this.tpl = Roo.bootstrap.version == 4 ?
15982                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15983                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15984         }
15985
15986         this.view = new Roo.View(this.list, this.tpl, {
15987             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15988         });
15989         //this.view.wrapEl.setDisplayed(false);
15990         this.view.on('click', this.onViewClick, this);
15991         
15992         
15993         this.store.on('beforeload', this.onBeforeLoad, this);
15994         this.store.on('load', this.onLoad, this);
15995         this.store.on('loadexception', this.onLoadException, this);
15996         /*
15997         if(this.resizable){
15998             this.resizer = new Roo.Resizable(this.list,  {
15999                pinned:true, handles:'se'
16000             });
16001             this.resizer.on('resize', function(r, w, h){
16002                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16003                 this.listWidth = w;
16004                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16005                 this.restrictHeight();
16006             }, this);
16007             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16008         }
16009         */
16010         if(!this.editable){
16011             this.editable = true;
16012             this.setEditable(false);
16013         }
16014         
16015         /*
16016         
16017         if (typeof(this.events.add.listeners) != 'undefined') {
16018             
16019             this.addicon = this.wrap.createChild(
16020                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16021        
16022             this.addicon.on('click', function(e) {
16023                 this.fireEvent('add', this);
16024             }, this);
16025         }
16026         if (typeof(this.events.edit.listeners) != 'undefined') {
16027             
16028             this.editicon = this.wrap.createChild(
16029                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16030             if (this.addicon) {
16031                 this.editicon.setStyle('margin-left', '40px');
16032             }
16033             this.editicon.on('click', function(e) {
16034                 
16035                 // we fire even  if inothing is selected..
16036                 this.fireEvent('edit', this, this.lastData );
16037                 
16038             }, this);
16039         }
16040         */
16041         
16042         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16043             "up" : function(e){
16044                 this.inKeyMode = true;
16045                 this.selectPrev();
16046             },
16047
16048             "down" : function(e){
16049                 if(!this.isExpanded()){
16050                     this.onTriggerClick();
16051                 }else{
16052                     this.inKeyMode = true;
16053                     this.selectNext();
16054                 }
16055             },
16056
16057             "enter" : function(e){
16058 //                this.onViewClick();
16059                 //return true;
16060                 this.collapse();
16061                 
16062                 if(this.fireEvent("specialkey", this, e)){
16063                     this.onViewClick(false);
16064                 }
16065                 
16066                 return true;
16067             },
16068
16069             "esc" : function(e){
16070                 this.collapse();
16071             },
16072
16073             "tab" : function(e){
16074                 this.collapse();
16075                 
16076                 if(this.fireEvent("specialkey", this, e)){
16077                     this.onViewClick(false);
16078                 }
16079                 
16080                 return true;
16081             },
16082
16083             scope : this,
16084
16085             doRelay : function(foo, bar, hname){
16086                 if(hname == 'down' || this.scope.isExpanded()){
16087                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16088                 }
16089                 return true;
16090             },
16091
16092             forceKeyDown: true
16093         });
16094         
16095         
16096         this.queryDelay = Math.max(this.queryDelay || 10,
16097                 this.mode == 'local' ? 10 : 250);
16098         
16099         
16100         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16101         
16102         if(this.typeAhead){
16103             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16104         }
16105         if(this.editable !== false){
16106             this.inputEl().on("keyup", this.onKeyUp, this);
16107         }
16108         if(this.forceSelection){
16109             this.inputEl().on('blur', this.doForce, this);
16110         }
16111         
16112         if(this.multiple){
16113             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16114             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16115         }
16116     },
16117     
16118     initTickableEvents: function()
16119     {   
16120         this.createList();
16121         
16122         if(this.hiddenName){
16123             
16124             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16125             
16126             this.hiddenField.dom.value =
16127                 this.hiddenValue !== undefined ? this.hiddenValue :
16128                 this.value !== undefined ? this.value : '';
16129
16130             // prevent input submission
16131             this.el.dom.removeAttribute('name');
16132             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16133              
16134              
16135         }
16136         
16137 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16138         
16139         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16140         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16141         if(this.triggerList){
16142             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16143         }
16144          
16145         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16146         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16147         
16148         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16149         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16150         
16151         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16152         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16153         
16154         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16155         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16156         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16157         
16158         this.okBtn.hide();
16159         this.cancelBtn.hide();
16160         
16161         var _this = this;
16162         
16163         (function(){
16164             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16165             _this.list.setWidth(lw);
16166         }).defer(100);
16167         
16168         this.list.on('mouseover', this.onViewOver, this);
16169         this.list.on('mousemove', this.onViewMove, this);
16170         
16171         this.list.on('scroll', this.onViewScroll, this);
16172         
16173         if(!this.tpl){
16174             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16175                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16176         }
16177
16178         this.view = new Roo.View(this.list, this.tpl, {
16179             singleSelect:true,
16180             tickable:true,
16181             parent:this,
16182             store: this.store,
16183             selectedClass: this.selectedClass
16184         });
16185         
16186         //this.view.wrapEl.setDisplayed(false);
16187         this.view.on('click', this.onViewClick, this);
16188         
16189         
16190         
16191         this.store.on('beforeload', this.onBeforeLoad, this);
16192         this.store.on('load', this.onLoad, this);
16193         this.store.on('loadexception', this.onLoadException, this);
16194         
16195         if(this.editable){
16196             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16197                 "up" : function(e){
16198                     this.inKeyMode = true;
16199                     this.selectPrev();
16200                 },
16201
16202                 "down" : function(e){
16203                     this.inKeyMode = true;
16204                     this.selectNext();
16205                 },
16206
16207                 "enter" : function(e){
16208                     if(this.fireEvent("specialkey", this, e)){
16209                         this.onViewClick(false);
16210                     }
16211                     
16212                     return true;
16213                 },
16214
16215                 "esc" : function(e){
16216                     this.onTickableFooterButtonClick(e, false, false);
16217                 },
16218
16219                 "tab" : function(e){
16220                     this.fireEvent("specialkey", this, e);
16221                     
16222                     this.onTickableFooterButtonClick(e, false, false);
16223                     
16224                     return true;
16225                 },
16226
16227                 scope : this,
16228
16229                 doRelay : function(e, fn, key){
16230                     if(this.scope.isExpanded()){
16231                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16232                     }
16233                     return true;
16234                 },
16235
16236                 forceKeyDown: true
16237             });
16238         }
16239         
16240         this.queryDelay = Math.max(this.queryDelay || 10,
16241                 this.mode == 'local' ? 10 : 250);
16242         
16243         
16244         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16245         
16246         if(this.typeAhead){
16247             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16248         }
16249         
16250         if(this.editable !== false){
16251             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16252         }
16253         
16254         this.indicator = this.indicatorEl();
16255         
16256         if(this.indicator){
16257             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16258             this.indicator.hide();
16259         }
16260         
16261     },
16262
16263     onDestroy : function(){
16264         if(this.view){
16265             this.view.setStore(null);
16266             this.view.el.removeAllListeners();
16267             this.view.el.remove();
16268             this.view.purgeListeners();
16269         }
16270         if(this.list){
16271             this.list.dom.innerHTML  = '';
16272         }
16273         
16274         if(this.store){
16275             this.store.un('beforeload', this.onBeforeLoad, this);
16276             this.store.un('load', this.onLoad, this);
16277             this.store.un('loadexception', this.onLoadException, this);
16278         }
16279         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16280     },
16281
16282     // private
16283     fireKey : function(e){
16284         if(e.isNavKeyPress() && !this.list.isVisible()){
16285             this.fireEvent("specialkey", this, e);
16286         }
16287     },
16288
16289     // private
16290     onResize: function(w, h)
16291     {
16292         
16293         
16294 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16295 //        
16296 //        if(typeof w != 'number'){
16297 //            // we do not handle it!?!?
16298 //            return;
16299 //        }
16300 //        var tw = this.trigger.getWidth();
16301 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16302 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16303 //        var x = w - tw;
16304 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16305 //            
16306 //        //this.trigger.setStyle('left', x+'px');
16307 //        
16308 //        if(this.list && this.listWidth === undefined){
16309 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16310 //            this.list.setWidth(lw);
16311 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16312 //        }
16313         
16314     
16315         
16316     },
16317
16318     /**
16319      * Allow or prevent the user from directly editing the field text.  If false is passed,
16320      * the user will only be able to select from the items defined in the dropdown list.  This method
16321      * is the runtime equivalent of setting the 'editable' config option at config time.
16322      * @param {Boolean} value True to allow the user to directly edit the field text
16323      */
16324     setEditable : function(value){
16325         if(value == this.editable){
16326             return;
16327         }
16328         this.editable = value;
16329         if(!value){
16330             this.inputEl().dom.setAttribute('readOnly', true);
16331             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16332             this.inputEl().addClass('x-combo-noedit');
16333         }else{
16334             this.inputEl().dom.setAttribute('readOnly', false);
16335             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16336             this.inputEl().removeClass('x-combo-noedit');
16337         }
16338     },
16339
16340     // private
16341     
16342     onBeforeLoad : function(combo,opts){
16343         if(!this.hasFocus){
16344             return;
16345         }
16346          if (!opts.add) {
16347             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16348          }
16349         this.restrictHeight();
16350         this.selectedIndex = -1;
16351     },
16352
16353     // private
16354     onLoad : function(){
16355         
16356         this.hasQuery = false;
16357         
16358         if(!this.hasFocus){
16359             return;
16360         }
16361         
16362         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16363             this.loading.hide();
16364         }
16365         
16366         if(this.store.getCount() > 0){
16367             
16368             this.expand();
16369             this.restrictHeight();
16370             if(this.lastQuery == this.allQuery){
16371                 if(this.editable && !this.tickable){
16372                     this.inputEl().dom.select();
16373                 }
16374                 
16375                 if(
16376                     !this.selectByValue(this.value, true) &&
16377                     this.autoFocus && 
16378                     (
16379                         !this.store.lastOptions ||
16380                         typeof(this.store.lastOptions.add) == 'undefined' || 
16381                         this.store.lastOptions.add != true
16382                     )
16383                 ){
16384                     this.select(0, true);
16385                 }
16386             }else{
16387                 if(this.autoFocus){
16388                     this.selectNext();
16389                 }
16390                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16391                     this.taTask.delay(this.typeAheadDelay);
16392                 }
16393             }
16394         }else{
16395             this.onEmptyResults();
16396         }
16397         
16398         //this.el.focus();
16399     },
16400     // private
16401     onLoadException : function()
16402     {
16403         this.hasQuery = false;
16404         
16405         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16406             this.loading.hide();
16407         }
16408         
16409         if(this.tickable && this.editable){
16410             return;
16411         }
16412         
16413         this.collapse();
16414         // only causes errors at present
16415         //Roo.log(this.store.reader.jsonData);
16416         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16417             // fixme
16418             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16419         //}
16420         
16421         
16422     },
16423     // private
16424     onTypeAhead : function(){
16425         if(this.store.getCount() > 0){
16426             var r = this.store.getAt(0);
16427             var newValue = r.data[this.displayField];
16428             var len = newValue.length;
16429             var selStart = this.getRawValue().length;
16430             
16431             if(selStart != len){
16432                 this.setRawValue(newValue);
16433                 this.selectText(selStart, newValue.length);
16434             }
16435         }
16436     },
16437
16438     // private
16439     onSelect : function(record, index){
16440         
16441         if(this.fireEvent('beforeselect', this, record, index) !== false){
16442         
16443             this.setFromData(index > -1 ? record.data : false);
16444             
16445             this.collapse();
16446             this.fireEvent('select', this, record, index);
16447         }
16448     },
16449
16450     /**
16451      * Returns the currently selected field value or empty string if no value is set.
16452      * @return {String} value The selected value
16453      */
16454     getValue : function()
16455     {
16456         if(Roo.isIOS && this.useNativeIOS){
16457             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16458         }
16459         
16460         if(this.multiple){
16461             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16462         }
16463         
16464         if(this.valueField){
16465             return typeof this.value != 'undefined' ? this.value : '';
16466         }else{
16467             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16468         }
16469     },
16470     
16471     getRawValue : function()
16472     {
16473         if(Roo.isIOS && this.useNativeIOS){
16474             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16475         }
16476         
16477         var v = this.inputEl().getValue();
16478         
16479         return v;
16480     },
16481
16482     /**
16483      * Clears any text/value currently set in the field
16484      */
16485     clearValue : function(){
16486         
16487         if(this.hiddenField){
16488             this.hiddenField.dom.value = '';
16489         }
16490         this.value = '';
16491         this.setRawValue('');
16492         this.lastSelectionText = '';
16493         this.lastData = false;
16494         
16495         var close = this.closeTriggerEl();
16496         
16497         if(close){
16498             close.hide();
16499         }
16500         
16501         this.validate();
16502         
16503     },
16504
16505     /**
16506      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16507      * will be displayed in the field.  If the value does not match the data value of an existing item,
16508      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16509      * Otherwise the field will be blank (although the value will still be set).
16510      * @param {String} value The value to match
16511      */
16512     setValue : function(v)
16513     {
16514         if(Roo.isIOS && this.useNativeIOS){
16515             this.setIOSValue(v);
16516             return;
16517         }
16518         
16519         if(this.multiple){
16520             this.syncValue();
16521             return;
16522         }
16523         
16524         var text = v;
16525         if(this.valueField){
16526             var r = this.findRecord(this.valueField, v);
16527             if(r){
16528                 text = r.data[this.displayField];
16529             }else if(this.valueNotFoundText !== undefined){
16530                 text = this.valueNotFoundText;
16531             }
16532         }
16533         this.lastSelectionText = text;
16534         if(this.hiddenField){
16535             this.hiddenField.dom.value = v;
16536         }
16537         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16538         this.value = v;
16539         
16540         var close = this.closeTriggerEl();
16541         
16542         if(close){
16543             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16544         }
16545         
16546         this.validate();
16547     },
16548     /**
16549      * @property {Object} the last set data for the element
16550      */
16551     
16552     lastData : false,
16553     /**
16554      * Sets the value of the field based on a object which is related to the record format for the store.
16555      * @param {Object} value the value to set as. or false on reset?
16556      */
16557     setFromData : function(o){
16558         
16559         if(this.multiple){
16560             this.addItem(o);
16561             return;
16562         }
16563             
16564         var dv = ''; // display value
16565         var vv = ''; // value value..
16566         this.lastData = o;
16567         if (this.displayField) {
16568             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16569         } else {
16570             // this is an error condition!!!
16571             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16572         }
16573         
16574         if(this.valueField){
16575             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16576         }
16577         
16578         var close = this.closeTriggerEl();
16579         
16580         if(close){
16581             if(dv.length || vv * 1 > 0){
16582                 close.show() ;
16583                 this.blockFocus=true;
16584             } else {
16585                 close.hide();
16586             }             
16587         }
16588         
16589         if(this.hiddenField){
16590             this.hiddenField.dom.value = vv;
16591             
16592             this.lastSelectionText = dv;
16593             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16594             this.value = vv;
16595             return;
16596         }
16597         // no hidden field.. - we store the value in 'value', but still display
16598         // display field!!!!
16599         this.lastSelectionText = dv;
16600         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16601         this.value = vv;
16602         
16603         
16604         
16605     },
16606     // private
16607     reset : function(){
16608         // overridden so that last data is reset..
16609         
16610         if(this.multiple){
16611             this.clearItem();
16612             return;
16613         }
16614         
16615         this.setValue(this.originalValue);
16616         //this.clearInvalid();
16617         this.lastData = false;
16618         if (this.view) {
16619             this.view.clearSelections();
16620         }
16621         
16622         this.validate();
16623     },
16624     // private
16625     findRecord : function(prop, value){
16626         var record;
16627         if(this.store.getCount() > 0){
16628             this.store.each(function(r){
16629                 if(r.data[prop] == value){
16630                     record = r;
16631                     return false;
16632                 }
16633                 return true;
16634             });
16635         }
16636         return record;
16637     },
16638     
16639     getName: function()
16640     {
16641         // returns hidden if it's set..
16642         if (!this.rendered) {return ''};
16643         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16644         
16645     },
16646     // private
16647     onViewMove : function(e, t){
16648         this.inKeyMode = false;
16649     },
16650
16651     // private
16652     onViewOver : function(e, t){
16653         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16654             return;
16655         }
16656         var item = this.view.findItemFromChild(t);
16657         
16658         if(item){
16659             var index = this.view.indexOf(item);
16660             this.select(index, false);
16661         }
16662     },
16663
16664     // private
16665     onViewClick : function(view, doFocus, el, e)
16666     {
16667         var index = this.view.getSelectedIndexes()[0];
16668         
16669         var r = this.store.getAt(index);
16670         
16671         if(this.tickable){
16672             
16673             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16674                 return;
16675             }
16676             
16677             var rm = false;
16678             var _this = this;
16679             
16680             Roo.each(this.tickItems, function(v,k){
16681                 
16682                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16683                     Roo.log(v);
16684                     _this.tickItems.splice(k, 1);
16685                     
16686                     if(typeof(e) == 'undefined' && view == false){
16687                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16688                     }
16689                     
16690                     rm = true;
16691                     return;
16692                 }
16693             });
16694             
16695             if(rm){
16696                 return;
16697             }
16698             
16699             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16700                 this.tickItems.push(r.data);
16701             }
16702             
16703             if(typeof(e) == 'undefined' && view == false){
16704                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16705             }
16706                     
16707             return;
16708         }
16709         
16710         if(r){
16711             this.onSelect(r, index);
16712         }
16713         if(doFocus !== false && !this.blockFocus){
16714             this.inputEl().focus();
16715         }
16716     },
16717
16718     // private
16719     restrictHeight : function(){
16720         //this.innerList.dom.style.height = '';
16721         //var inner = this.innerList.dom;
16722         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16723         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16724         //this.list.beginUpdate();
16725         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16726         this.list.alignTo(this.inputEl(), this.listAlign);
16727         this.list.alignTo(this.inputEl(), this.listAlign);
16728         //this.list.endUpdate();
16729     },
16730
16731     // private
16732     onEmptyResults : function(){
16733         
16734         if(this.tickable && this.editable){
16735             this.hasFocus = false;
16736             this.restrictHeight();
16737             return;
16738         }
16739         
16740         this.collapse();
16741     },
16742
16743     /**
16744      * Returns true if the dropdown list is expanded, else false.
16745      */
16746     isExpanded : function(){
16747         return this.list.isVisible();
16748     },
16749
16750     /**
16751      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16752      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16753      * @param {String} value The data value of the item to select
16754      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16755      * selected item if it is not currently in view (defaults to true)
16756      * @return {Boolean} True if the value matched an item in the list, else false
16757      */
16758     selectByValue : function(v, scrollIntoView){
16759         if(v !== undefined && v !== null){
16760             var r = this.findRecord(this.valueField || this.displayField, v);
16761             if(r){
16762                 this.select(this.store.indexOf(r), scrollIntoView);
16763                 return true;
16764             }
16765         }
16766         return false;
16767     },
16768
16769     /**
16770      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16771      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16772      * @param {Number} index The zero-based index of the list item to select
16773      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16774      * selected item if it is not currently in view (defaults to true)
16775      */
16776     select : function(index, scrollIntoView){
16777         this.selectedIndex = index;
16778         this.view.select(index);
16779         if(scrollIntoView !== false){
16780             var el = this.view.getNode(index);
16781             /*
16782              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16783              */
16784             if(el){
16785                 this.list.scrollChildIntoView(el, false);
16786             }
16787         }
16788     },
16789
16790     // private
16791     selectNext : function(){
16792         var ct = this.store.getCount();
16793         if(ct > 0){
16794             if(this.selectedIndex == -1){
16795                 this.select(0);
16796             }else if(this.selectedIndex < ct-1){
16797                 this.select(this.selectedIndex+1);
16798             }
16799         }
16800     },
16801
16802     // private
16803     selectPrev : function(){
16804         var ct = this.store.getCount();
16805         if(ct > 0){
16806             if(this.selectedIndex == -1){
16807                 this.select(0);
16808             }else if(this.selectedIndex != 0){
16809                 this.select(this.selectedIndex-1);
16810             }
16811         }
16812     },
16813
16814     // private
16815     onKeyUp : function(e){
16816         if(this.editable !== false && !e.isSpecialKey()){
16817             this.lastKey = e.getKey();
16818             this.dqTask.delay(this.queryDelay);
16819         }
16820     },
16821
16822     // private
16823     validateBlur : function(){
16824         return !this.list || !this.list.isVisible();   
16825     },
16826
16827     // private
16828     initQuery : function(){
16829         
16830         var v = this.getRawValue();
16831         
16832         if(this.tickable && this.editable){
16833             v = this.tickableInputEl().getValue();
16834         }
16835         
16836         this.doQuery(v);
16837     },
16838
16839     // private
16840     doForce : function(){
16841         if(this.inputEl().dom.value.length > 0){
16842             this.inputEl().dom.value =
16843                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16844              
16845         }
16846     },
16847
16848     /**
16849      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16850      * query allowing the query action to be canceled if needed.
16851      * @param {String} query The SQL query to execute
16852      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16853      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16854      * saved in the current store (defaults to false)
16855      */
16856     doQuery : function(q, forceAll){
16857         
16858         if(q === undefined || q === null){
16859             q = '';
16860         }
16861         var qe = {
16862             query: q,
16863             forceAll: forceAll,
16864             combo: this,
16865             cancel:false
16866         };
16867         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16868             return false;
16869         }
16870         q = qe.query;
16871         
16872         forceAll = qe.forceAll;
16873         if(forceAll === true || (q.length >= this.minChars)){
16874             
16875             this.hasQuery = true;
16876             
16877             if(this.lastQuery != q || this.alwaysQuery){
16878                 this.lastQuery = q;
16879                 if(this.mode == 'local'){
16880                     this.selectedIndex = -1;
16881                     if(forceAll){
16882                         this.store.clearFilter();
16883                     }else{
16884                         
16885                         if(this.specialFilter){
16886                             this.fireEvent('specialfilter', this);
16887                             this.onLoad();
16888                             return;
16889                         }
16890                         
16891                         this.store.filter(this.displayField, q);
16892                     }
16893                     
16894                     this.store.fireEvent("datachanged", this.store);
16895                     
16896                     this.onLoad();
16897                     
16898                     
16899                 }else{
16900                     
16901                     this.store.baseParams[this.queryParam] = q;
16902                     
16903                     var options = {params : this.getParams(q)};
16904                     
16905                     if(this.loadNext){
16906                         options.add = true;
16907                         options.params.start = this.page * this.pageSize;
16908                     }
16909                     
16910                     this.store.load(options);
16911                     
16912                     /*
16913                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16914                      *  we should expand the list on onLoad
16915                      *  so command out it
16916                      */
16917 //                    this.expand();
16918                 }
16919             }else{
16920                 this.selectedIndex = -1;
16921                 this.onLoad();   
16922             }
16923         }
16924         
16925         this.loadNext = false;
16926     },
16927     
16928     // private
16929     getParams : function(q){
16930         var p = {};
16931         //p[this.queryParam] = q;
16932         
16933         if(this.pageSize){
16934             p.start = 0;
16935             p.limit = this.pageSize;
16936         }
16937         return p;
16938     },
16939
16940     /**
16941      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16942      */
16943     collapse : function(){
16944         if(!this.isExpanded()){
16945             return;
16946         }
16947         
16948         this.list.hide();
16949         
16950         this.hasFocus = false;
16951         
16952         if(this.tickable){
16953             this.okBtn.hide();
16954             this.cancelBtn.hide();
16955             this.trigger.show();
16956             
16957             if(this.editable){
16958                 this.tickableInputEl().dom.value = '';
16959                 this.tickableInputEl().blur();
16960             }
16961             
16962         }
16963         
16964         Roo.get(document).un('mousedown', this.collapseIf, this);
16965         Roo.get(document).un('mousewheel', this.collapseIf, this);
16966         if (!this.editable) {
16967             Roo.get(document).un('keydown', this.listKeyPress, this);
16968         }
16969         this.fireEvent('collapse', this);
16970         
16971         this.validate();
16972     },
16973
16974     // private
16975     collapseIf : function(e){
16976         var in_combo  = e.within(this.el);
16977         var in_list =  e.within(this.list);
16978         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16979         
16980         if (in_combo || in_list || is_list) {
16981             //e.stopPropagation();
16982             return;
16983         }
16984         
16985         if(this.tickable){
16986             this.onTickableFooterButtonClick(e, false, false);
16987         }
16988
16989         this.collapse();
16990         
16991     },
16992
16993     /**
16994      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16995      */
16996     expand : function(){
16997        
16998         if(this.isExpanded() || !this.hasFocus){
16999             return;
17000         }
17001         
17002         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17003         this.list.setWidth(lw);
17004         
17005         Roo.log('expand');
17006         
17007         this.list.show();
17008         
17009         this.restrictHeight();
17010         
17011         if(this.tickable){
17012             
17013             this.tickItems = Roo.apply([], this.item);
17014             
17015             this.okBtn.show();
17016             this.cancelBtn.show();
17017             this.trigger.hide();
17018             
17019             if(this.editable){
17020                 this.tickableInputEl().focus();
17021             }
17022             
17023         }
17024         
17025         Roo.get(document).on('mousedown', this.collapseIf, this);
17026         Roo.get(document).on('mousewheel', this.collapseIf, this);
17027         if (!this.editable) {
17028             Roo.get(document).on('keydown', this.listKeyPress, this);
17029         }
17030         
17031         this.fireEvent('expand', this);
17032     },
17033
17034     // private
17035     // Implements the default empty TriggerField.onTriggerClick function
17036     onTriggerClick : function(e)
17037     {
17038         Roo.log('trigger click');
17039         
17040         if(this.disabled || !this.triggerList){
17041             return;
17042         }
17043         
17044         this.page = 0;
17045         this.loadNext = false;
17046         
17047         if(this.isExpanded()){
17048             this.collapse();
17049             if (!this.blockFocus) {
17050                 this.inputEl().focus();
17051             }
17052             
17053         }else {
17054             this.hasFocus = true;
17055             if(this.triggerAction == 'all') {
17056                 this.doQuery(this.allQuery, true);
17057             } else {
17058                 this.doQuery(this.getRawValue());
17059             }
17060             if (!this.blockFocus) {
17061                 this.inputEl().focus();
17062             }
17063         }
17064     },
17065     
17066     onTickableTriggerClick : function(e)
17067     {
17068         if(this.disabled){
17069             return;
17070         }
17071         
17072         this.page = 0;
17073         this.loadNext = false;
17074         this.hasFocus = true;
17075         
17076         if(this.triggerAction == 'all') {
17077             this.doQuery(this.allQuery, true);
17078         } else {
17079             this.doQuery(this.getRawValue());
17080         }
17081     },
17082     
17083     onSearchFieldClick : function(e)
17084     {
17085         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17086             this.onTickableFooterButtonClick(e, false, false);
17087             return;
17088         }
17089         
17090         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17091             return;
17092         }
17093         
17094         this.page = 0;
17095         this.loadNext = false;
17096         this.hasFocus = true;
17097         
17098         if(this.triggerAction == 'all') {
17099             this.doQuery(this.allQuery, true);
17100         } else {
17101             this.doQuery(this.getRawValue());
17102         }
17103     },
17104     
17105     listKeyPress : function(e)
17106     {
17107         //Roo.log('listkeypress');
17108         // scroll to first matching element based on key pres..
17109         if (e.isSpecialKey()) {
17110             return false;
17111         }
17112         var k = String.fromCharCode(e.getKey()).toUpperCase();
17113         //Roo.log(k);
17114         var match  = false;
17115         var csel = this.view.getSelectedNodes();
17116         var cselitem = false;
17117         if (csel.length) {
17118             var ix = this.view.indexOf(csel[0]);
17119             cselitem  = this.store.getAt(ix);
17120             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17121                 cselitem = false;
17122             }
17123             
17124         }
17125         
17126         this.store.each(function(v) { 
17127             if (cselitem) {
17128                 // start at existing selection.
17129                 if (cselitem.id == v.id) {
17130                     cselitem = false;
17131                 }
17132                 return true;
17133             }
17134                 
17135             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17136                 match = this.store.indexOf(v);
17137                 return false;
17138             }
17139             return true;
17140         }, this);
17141         
17142         if (match === false) {
17143             return true; // no more action?
17144         }
17145         // scroll to?
17146         this.view.select(match);
17147         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17148         sn.scrollIntoView(sn.dom.parentNode, false);
17149     },
17150     
17151     onViewScroll : function(e, t){
17152         
17153         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){
17154             return;
17155         }
17156         
17157         this.hasQuery = true;
17158         
17159         this.loading = this.list.select('.loading', true).first();
17160         
17161         if(this.loading === null){
17162             this.list.createChild({
17163                 tag: 'div',
17164                 cls: 'loading roo-select2-more-results roo-select2-active',
17165                 html: 'Loading more results...'
17166             });
17167             
17168             this.loading = this.list.select('.loading', true).first();
17169             
17170             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17171             
17172             this.loading.hide();
17173         }
17174         
17175         this.loading.show();
17176         
17177         var _combo = this;
17178         
17179         this.page++;
17180         this.loadNext = true;
17181         
17182         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17183         
17184         return;
17185     },
17186     
17187     addItem : function(o)
17188     {   
17189         var dv = ''; // display value
17190         
17191         if (this.displayField) {
17192             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17193         } else {
17194             // this is an error condition!!!
17195             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17196         }
17197         
17198         if(!dv.length){
17199             return;
17200         }
17201         
17202         var choice = this.choices.createChild({
17203             tag: 'li',
17204             cls: 'roo-select2-search-choice',
17205             cn: [
17206                 {
17207                     tag: 'div',
17208                     html: dv
17209                 },
17210                 {
17211                     tag: 'a',
17212                     href: '#',
17213                     cls: 'roo-select2-search-choice-close fa fa-times',
17214                     tabindex: '-1'
17215                 }
17216             ]
17217             
17218         }, this.searchField);
17219         
17220         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17221         
17222         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17223         
17224         this.item.push(o);
17225         
17226         this.lastData = o;
17227         
17228         this.syncValue();
17229         
17230         this.inputEl().dom.value = '';
17231         
17232         this.validate();
17233     },
17234     
17235     onRemoveItem : function(e, _self, o)
17236     {
17237         e.preventDefault();
17238         
17239         this.lastItem = Roo.apply([], this.item);
17240         
17241         var index = this.item.indexOf(o.data) * 1;
17242         
17243         if( index < 0){
17244             Roo.log('not this item?!');
17245             return;
17246         }
17247         
17248         this.item.splice(index, 1);
17249         o.item.remove();
17250         
17251         this.syncValue();
17252         
17253         this.fireEvent('remove', this, e);
17254         
17255         this.validate();
17256         
17257     },
17258     
17259     syncValue : function()
17260     {
17261         if(!this.item.length){
17262             this.clearValue();
17263             return;
17264         }
17265             
17266         var value = [];
17267         var _this = this;
17268         Roo.each(this.item, function(i){
17269             if(_this.valueField){
17270                 value.push(i[_this.valueField]);
17271                 return;
17272             }
17273
17274             value.push(i);
17275         });
17276
17277         this.value = value.join(',');
17278
17279         if(this.hiddenField){
17280             this.hiddenField.dom.value = this.value;
17281         }
17282         
17283         this.store.fireEvent("datachanged", this.store);
17284         
17285         this.validate();
17286     },
17287     
17288     clearItem : function()
17289     {
17290         if(!this.multiple){
17291             return;
17292         }
17293         
17294         this.item = [];
17295         
17296         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17297            c.remove();
17298         });
17299         
17300         this.syncValue();
17301         
17302         this.validate();
17303         
17304         if(this.tickable && !Roo.isTouch){
17305             this.view.refresh();
17306         }
17307     },
17308     
17309     inputEl: function ()
17310     {
17311         if(Roo.isIOS && this.useNativeIOS){
17312             return this.el.select('select.roo-ios-select', true).first();
17313         }
17314         
17315         if(Roo.isTouch && this.mobileTouchView){
17316             return this.el.select('input.form-control',true).first();
17317         }
17318         
17319         if(this.tickable){
17320             return this.searchField;
17321         }
17322         
17323         return this.el.select('input.form-control',true).first();
17324     },
17325     
17326     onTickableFooterButtonClick : function(e, btn, el)
17327     {
17328         e.preventDefault();
17329         
17330         this.lastItem = Roo.apply([], this.item);
17331         
17332         if(btn && btn.name == 'cancel'){
17333             this.tickItems = Roo.apply([], this.item);
17334             this.collapse();
17335             return;
17336         }
17337         
17338         this.clearItem();
17339         
17340         var _this = this;
17341         
17342         Roo.each(this.tickItems, function(o){
17343             _this.addItem(o);
17344         });
17345         
17346         this.collapse();
17347         
17348     },
17349     
17350     validate : function()
17351     {
17352         if(this.getVisibilityEl().hasClass('hidden')){
17353             return true;
17354         }
17355         
17356         var v = this.getRawValue();
17357         
17358         if(this.multiple){
17359             v = this.getValue();
17360         }
17361         
17362         if(this.disabled || this.allowBlank || v.length){
17363             this.markValid();
17364             return true;
17365         }
17366         
17367         this.markInvalid();
17368         return false;
17369     },
17370     
17371     tickableInputEl : function()
17372     {
17373         if(!this.tickable || !this.editable){
17374             return this.inputEl();
17375         }
17376         
17377         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17378     },
17379     
17380     
17381     getAutoCreateTouchView : function()
17382     {
17383         var id = Roo.id();
17384         
17385         var cfg = {
17386             cls: 'form-group' //input-group
17387         };
17388         
17389         var input =  {
17390             tag: 'input',
17391             id : id,
17392             type : this.inputType,
17393             cls : 'form-control x-combo-noedit',
17394             autocomplete: 'new-password',
17395             placeholder : this.placeholder || '',
17396             readonly : true
17397         };
17398         
17399         if (this.name) {
17400             input.name = this.name;
17401         }
17402         
17403         if (this.size) {
17404             input.cls += ' input-' + this.size;
17405         }
17406         
17407         if (this.disabled) {
17408             input.disabled = true;
17409         }
17410         
17411         var inputblock = {
17412             cls : 'roo-combobox-wrap',
17413             cn : [
17414                 input
17415             ]
17416         };
17417         
17418         if(this.before){
17419             inputblock.cls += ' input-group';
17420             
17421             inputblock.cn.unshift({
17422                 tag :'span',
17423                 cls : 'input-group-addon input-group-prepend input-group-text',
17424                 html : this.before
17425             });
17426         }
17427         
17428         if(this.removable && !this.multiple){
17429             inputblock.cls += ' roo-removable';
17430             
17431             inputblock.cn.push({
17432                 tag: 'button',
17433                 html : 'x',
17434                 cls : 'roo-combo-removable-btn close'
17435             });
17436         }
17437
17438         if(this.hasFeedback && !this.allowBlank){
17439             
17440             inputblock.cls += ' has-feedback';
17441             
17442             inputblock.cn.push({
17443                 tag: 'span',
17444                 cls: 'glyphicon form-control-feedback'
17445             });
17446             
17447         }
17448         
17449         if (this.after) {
17450             
17451             inputblock.cls += (this.before) ? '' : ' input-group';
17452             
17453             inputblock.cn.push({
17454                 tag :'span',
17455                 cls : 'input-group-addon input-group-append input-group-text',
17456                 html : this.after
17457             });
17458         }
17459
17460         
17461         var ibwrap = inputblock;
17462         
17463         if(this.multiple){
17464             ibwrap = {
17465                 tag: 'ul',
17466                 cls: 'roo-select2-choices',
17467                 cn:[
17468                     {
17469                         tag: 'li',
17470                         cls: 'roo-select2-search-field',
17471                         cn: [
17472
17473                             inputblock
17474                         ]
17475                     }
17476                 ]
17477             };
17478         
17479             
17480         }
17481         
17482         var combobox = {
17483             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17484             cn: [
17485                 {
17486                     tag: 'input',
17487                     type : 'hidden',
17488                     cls: 'form-hidden-field'
17489                 },
17490                 ibwrap
17491             ]
17492         };
17493         
17494         if(!this.multiple && this.showToggleBtn){
17495             
17496             var caret = {
17497                 cls: 'caret'
17498             };
17499             
17500             if (this.caret != false) {
17501                 caret = {
17502                      tag: 'i',
17503                      cls: 'fa fa-' + this.caret
17504                 };
17505                 
17506             }
17507             
17508             combobox.cn.push({
17509                 tag :'span',
17510                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17511                 cn : [
17512                     Roo.bootstrap.version == 3 ? caret : '',
17513                     {
17514                         tag: 'span',
17515                         cls: 'combobox-clear',
17516                         cn  : [
17517                             {
17518                                 tag : 'i',
17519                                 cls: 'icon-remove'
17520                             }
17521                         ]
17522                     }
17523                 ]
17524
17525             })
17526         }
17527         
17528         if(this.multiple){
17529             combobox.cls += ' roo-select2-container-multi';
17530         }
17531         
17532         var align = this.labelAlign || this.parentLabelAlign();
17533         
17534         if (align ==='left' && this.fieldLabel.length) {
17535
17536             cfg.cn = [
17537                 {
17538                    tag : 'i',
17539                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17540                    tooltip : 'This field is required'
17541                 },
17542                 {
17543                     tag: 'label',
17544                     cls : 'control-label col-form-label',
17545                     html : this.fieldLabel
17546
17547                 },
17548                 {
17549                     cls : 'roo-combobox-wrap ', 
17550                     cn: [
17551                         combobox
17552                     ]
17553                 }
17554             ];
17555             
17556             var labelCfg = cfg.cn[1];
17557             var contentCfg = cfg.cn[2];
17558             
17559
17560             if(this.indicatorpos == 'right'){
17561                 cfg.cn = [
17562                     {
17563                         tag: 'label',
17564                         'for' :  id,
17565                         cls : 'control-label col-form-label',
17566                         cn : [
17567                             {
17568                                 tag : 'span',
17569                                 html : this.fieldLabel
17570                             },
17571                             {
17572                                 tag : 'i',
17573                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17574                                 tooltip : 'This field is required'
17575                             }
17576                         ]
17577                     },
17578                     {
17579                         cls : "roo-combobox-wrap ",
17580                         cn: [
17581                             combobox
17582                         ]
17583                     }
17584
17585                 ];
17586                 
17587                 labelCfg = cfg.cn[0];
17588                 contentCfg = cfg.cn[1];
17589             }
17590             
17591            
17592             
17593             if(this.labelWidth > 12){
17594                 labelCfg.style = "width: " + this.labelWidth + 'px';
17595             }
17596            
17597             if(this.labelWidth < 13 && this.labelmd == 0){
17598                 this.labelmd = this.labelWidth;
17599             }
17600             
17601             if(this.labellg > 0){
17602                 labelCfg.cls += ' col-lg-' + this.labellg;
17603                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17604             }
17605             
17606             if(this.labelmd > 0){
17607                 labelCfg.cls += ' col-md-' + this.labelmd;
17608                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17609             }
17610             
17611             if(this.labelsm > 0){
17612                 labelCfg.cls += ' col-sm-' + this.labelsm;
17613                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17614             }
17615             
17616             if(this.labelxs > 0){
17617                 labelCfg.cls += ' col-xs-' + this.labelxs;
17618                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17619             }
17620                 
17621                 
17622         } else if ( this.fieldLabel.length) {
17623             cfg.cn = [
17624                 {
17625                    tag : 'i',
17626                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17627                    tooltip : 'This field is required'
17628                 },
17629                 {
17630                     tag: 'label',
17631                     cls : 'control-label',
17632                     html : this.fieldLabel
17633
17634                 },
17635                 {
17636                     cls : '', 
17637                     cn: [
17638                         combobox
17639                     ]
17640                 }
17641             ];
17642             
17643             if(this.indicatorpos == 'right'){
17644                 cfg.cn = [
17645                     {
17646                         tag: 'label',
17647                         cls : 'control-label',
17648                         html : this.fieldLabel,
17649                         cn : [
17650                             {
17651                                tag : 'i',
17652                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17653                                tooltip : 'This field is required'
17654                             }
17655                         ]
17656                     },
17657                     {
17658                         cls : '', 
17659                         cn: [
17660                             combobox
17661                         ]
17662                     }
17663                 ];
17664             }
17665         } else {
17666             cfg.cn = combobox;    
17667         }
17668         
17669         
17670         var settings = this;
17671         
17672         ['xs','sm','md','lg'].map(function(size){
17673             if (settings[size]) {
17674                 cfg.cls += ' col-' + size + '-' + settings[size];
17675             }
17676         });
17677         
17678         return cfg;
17679     },
17680     
17681     initTouchView : function()
17682     {
17683         this.renderTouchView();
17684         
17685         this.touchViewEl.on('scroll', function(){
17686             this.el.dom.scrollTop = 0;
17687         }, this);
17688         
17689         this.originalValue = this.getValue();
17690         
17691         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17692         
17693         this.inputEl().on("click", this.showTouchView, this);
17694         if (this.triggerEl) {
17695             this.triggerEl.on("click", this.showTouchView, this);
17696         }
17697         
17698         
17699         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17700         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17701         
17702         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17703         
17704         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17705         this.store.on('load', this.onTouchViewLoad, this);
17706         this.store.on('loadexception', this.onTouchViewLoadException, this);
17707         
17708         if(this.hiddenName){
17709             
17710             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17711             
17712             this.hiddenField.dom.value =
17713                 this.hiddenValue !== undefined ? this.hiddenValue :
17714                 this.value !== undefined ? this.value : '';
17715         
17716             this.el.dom.removeAttribute('name');
17717             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17718         }
17719         
17720         if(this.multiple){
17721             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17722             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17723         }
17724         
17725         if(this.removable && !this.multiple){
17726             var close = this.closeTriggerEl();
17727             if(close){
17728                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17729                 close.on('click', this.removeBtnClick, this, close);
17730             }
17731         }
17732         /*
17733          * fix the bug in Safari iOS8
17734          */
17735         this.inputEl().on("focus", function(e){
17736             document.activeElement.blur();
17737         }, this);
17738         
17739         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17740         
17741         return;
17742         
17743         
17744     },
17745     
17746     renderTouchView : function()
17747     {
17748         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17749         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17750         
17751         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17752         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17753         
17754         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17755         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17756         this.touchViewBodyEl.setStyle('overflow', 'auto');
17757         
17758         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17759         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17760         
17761         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17762         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17763         
17764     },
17765     
17766     showTouchView : function()
17767     {
17768         if(this.disabled){
17769             return;
17770         }
17771         
17772         this.touchViewHeaderEl.hide();
17773
17774         if(this.modalTitle.length){
17775             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17776             this.touchViewHeaderEl.show();
17777         }
17778
17779         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17780         this.touchViewEl.show();
17781
17782         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17783         
17784         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17785         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17786
17787         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17788
17789         if(this.modalTitle.length){
17790             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17791         }
17792         
17793         this.touchViewBodyEl.setHeight(bodyHeight);
17794
17795         if(this.animate){
17796             var _this = this;
17797             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17798         }else{
17799             this.touchViewEl.addClass(['in','show']);
17800         }
17801         
17802         if(this._touchViewMask){
17803             Roo.get(document.body).addClass("x-body-masked");
17804             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17805             this._touchViewMask.setStyle('z-index', 10000);
17806             this._touchViewMask.addClass('show');
17807         }
17808         
17809         this.doTouchViewQuery();
17810         
17811     },
17812     
17813     hideTouchView : function()
17814     {
17815         this.touchViewEl.removeClass(['in','show']);
17816
17817         if(this.animate){
17818             var _this = this;
17819             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17820         }else{
17821             this.touchViewEl.setStyle('display', 'none');
17822         }
17823         
17824         if(this._touchViewMask){
17825             this._touchViewMask.removeClass('show');
17826             Roo.get(document.body).removeClass("x-body-masked");
17827         }
17828     },
17829     
17830     setTouchViewValue : function()
17831     {
17832         if(this.multiple){
17833             this.clearItem();
17834         
17835             var _this = this;
17836
17837             Roo.each(this.tickItems, function(o){
17838                 this.addItem(o);
17839             }, this);
17840         }
17841         
17842         this.hideTouchView();
17843     },
17844     
17845     doTouchViewQuery : function()
17846     {
17847         var qe = {
17848             query: '',
17849             forceAll: true,
17850             combo: this,
17851             cancel:false
17852         };
17853         
17854         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17855             return false;
17856         }
17857         
17858         if(!this.alwaysQuery || this.mode == 'local'){
17859             this.onTouchViewLoad();
17860             return;
17861         }
17862         
17863         this.store.load();
17864     },
17865     
17866     onTouchViewBeforeLoad : function(combo,opts)
17867     {
17868         return;
17869     },
17870
17871     // private
17872     onTouchViewLoad : function()
17873     {
17874         if(this.store.getCount() < 1){
17875             this.onTouchViewEmptyResults();
17876             return;
17877         }
17878         
17879         this.clearTouchView();
17880         
17881         var rawValue = this.getRawValue();
17882         
17883         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17884         
17885         this.tickItems = [];
17886         
17887         this.store.data.each(function(d, rowIndex){
17888             var row = this.touchViewListGroup.createChild(template);
17889             
17890             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17891                 row.addClass(d.data.cls);
17892             }
17893             
17894             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17895                 var cfg = {
17896                     data : d.data,
17897                     html : d.data[this.displayField]
17898                 };
17899                 
17900                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17901                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17902                 }
17903             }
17904             row.removeClass('selected');
17905             if(!this.multiple && this.valueField &&
17906                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17907             {
17908                 // radio buttons..
17909                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17910                 row.addClass('selected');
17911             }
17912             
17913             if(this.multiple && this.valueField &&
17914                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17915             {
17916                 
17917                 // checkboxes...
17918                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17919                 this.tickItems.push(d.data);
17920             }
17921             
17922             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17923             
17924         }, this);
17925         
17926         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17927         
17928         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17929
17930         if(this.modalTitle.length){
17931             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17932         }
17933
17934         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17935         
17936         if(this.mobile_restrict_height && listHeight < bodyHeight){
17937             this.touchViewBodyEl.setHeight(listHeight);
17938         }
17939         
17940         var _this = this;
17941         
17942         if(firstChecked && listHeight > bodyHeight){
17943             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17944         }
17945         
17946     },
17947     
17948     onTouchViewLoadException : function()
17949     {
17950         this.hideTouchView();
17951     },
17952     
17953     onTouchViewEmptyResults : function()
17954     {
17955         this.clearTouchView();
17956         
17957         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17958         
17959         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17960         
17961     },
17962     
17963     clearTouchView : function()
17964     {
17965         this.touchViewListGroup.dom.innerHTML = '';
17966     },
17967     
17968     onTouchViewClick : function(e, el, o)
17969     {
17970         e.preventDefault();
17971         
17972         var row = o.row;
17973         var rowIndex = o.rowIndex;
17974         
17975         var r = this.store.getAt(rowIndex);
17976         
17977         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17978             
17979             if(!this.multiple){
17980                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17981                     c.dom.removeAttribute('checked');
17982                 }, this);
17983
17984                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17985
17986                 this.setFromData(r.data);
17987
17988                 var close = this.closeTriggerEl();
17989
17990                 if(close){
17991                     close.show();
17992                 }
17993
17994                 this.hideTouchView();
17995
17996                 this.fireEvent('select', this, r, rowIndex);
17997
17998                 return;
17999             }
18000
18001             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18002                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18003                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18004                 return;
18005             }
18006
18007             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18008             this.addItem(r.data);
18009             this.tickItems.push(r.data);
18010         }
18011     },
18012     
18013     getAutoCreateNativeIOS : function()
18014     {
18015         var cfg = {
18016             cls: 'form-group' //input-group,
18017         };
18018         
18019         var combobox =  {
18020             tag: 'select',
18021             cls : 'roo-ios-select'
18022         };
18023         
18024         if (this.name) {
18025             combobox.name = this.name;
18026         }
18027         
18028         if (this.disabled) {
18029             combobox.disabled = true;
18030         }
18031         
18032         var settings = this;
18033         
18034         ['xs','sm','md','lg'].map(function(size){
18035             if (settings[size]) {
18036                 cfg.cls += ' col-' + size + '-' + settings[size];
18037             }
18038         });
18039         
18040         cfg.cn = combobox;
18041         
18042         return cfg;
18043         
18044     },
18045     
18046     initIOSView : function()
18047     {
18048         this.store.on('load', this.onIOSViewLoad, this);
18049         
18050         return;
18051     },
18052     
18053     onIOSViewLoad : function()
18054     {
18055         if(this.store.getCount() < 1){
18056             return;
18057         }
18058         
18059         this.clearIOSView();
18060         
18061         if(this.allowBlank) {
18062             
18063             var default_text = '-- SELECT --';
18064             
18065             if(this.placeholder.length){
18066                 default_text = this.placeholder;
18067             }
18068             
18069             if(this.emptyTitle.length){
18070                 default_text += ' - ' + this.emptyTitle + ' -';
18071             }
18072             
18073             var opt = this.inputEl().createChild({
18074                 tag: 'option',
18075                 value : 0,
18076                 html : default_text
18077             });
18078             
18079             var o = {};
18080             o[this.valueField] = 0;
18081             o[this.displayField] = default_text;
18082             
18083             this.ios_options.push({
18084                 data : o,
18085                 el : opt
18086             });
18087             
18088         }
18089         
18090         this.store.data.each(function(d, rowIndex){
18091             
18092             var html = '';
18093             
18094             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18095                 html = d.data[this.displayField];
18096             }
18097             
18098             var value = '';
18099             
18100             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18101                 value = d.data[this.valueField];
18102             }
18103             
18104             var option = {
18105                 tag: 'option',
18106                 value : value,
18107                 html : html
18108             };
18109             
18110             if(this.value == d.data[this.valueField]){
18111                 option['selected'] = true;
18112             }
18113             
18114             var opt = this.inputEl().createChild(option);
18115             
18116             this.ios_options.push({
18117                 data : d.data,
18118                 el : opt
18119             });
18120             
18121         }, this);
18122         
18123         this.inputEl().on('change', function(){
18124            this.fireEvent('select', this);
18125         }, this);
18126         
18127     },
18128     
18129     clearIOSView: function()
18130     {
18131         this.inputEl().dom.innerHTML = '';
18132         
18133         this.ios_options = [];
18134     },
18135     
18136     setIOSValue: function(v)
18137     {
18138         this.value = v;
18139         
18140         if(!this.ios_options){
18141             return;
18142         }
18143         
18144         Roo.each(this.ios_options, function(opts){
18145            
18146            opts.el.dom.removeAttribute('selected');
18147            
18148            if(opts.data[this.valueField] != v){
18149                return;
18150            }
18151            
18152            opts.el.dom.setAttribute('selected', true);
18153            
18154         }, this);
18155     }
18156
18157     /** 
18158     * @cfg {Boolean} grow 
18159     * @hide 
18160     */
18161     /** 
18162     * @cfg {Number} growMin 
18163     * @hide 
18164     */
18165     /** 
18166     * @cfg {Number} growMax 
18167     * @hide 
18168     */
18169     /**
18170      * @hide
18171      * @method autoSize
18172      */
18173 });
18174
18175 Roo.apply(Roo.bootstrap.ComboBox,  {
18176     
18177     header : {
18178         tag: 'div',
18179         cls: 'modal-header',
18180         cn: [
18181             {
18182                 tag: 'h4',
18183                 cls: 'modal-title'
18184             }
18185         ]
18186     },
18187     
18188     body : {
18189         tag: 'div',
18190         cls: 'modal-body',
18191         cn: [
18192             {
18193                 tag: 'ul',
18194                 cls: 'list-group'
18195             }
18196         ]
18197     },
18198     
18199     listItemRadio : {
18200         tag: 'li',
18201         cls: 'list-group-item',
18202         cn: [
18203             {
18204                 tag: 'span',
18205                 cls: 'roo-combobox-list-group-item-value'
18206             },
18207             {
18208                 tag: 'div',
18209                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18210                 cn: [
18211                     {
18212                         tag: 'input',
18213                         type: 'radio'
18214                     },
18215                     {
18216                         tag: 'label'
18217                     }
18218                 ]
18219             }
18220         ]
18221     },
18222     
18223     listItemCheckbox : {
18224         tag: 'li',
18225         cls: 'list-group-item',
18226         cn: [
18227             {
18228                 tag: 'span',
18229                 cls: 'roo-combobox-list-group-item-value'
18230             },
18231             {
18232                 tag: 'div',
18233                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18234                 cn: [
18235                     {
18236                         tag: 'input',
18237                         type: 'checkbox'
18238                     },
18239                     {
18240                         tag: 'label'
18241                     }
18242                 ]
18243             }
18244         ]
18245     },
18246     
18247     emptyResult : {
18248         tag: 'div',
18249         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18250     },
18251     
18252     footer : {
18253         tag: 'div',
18254         cls: 'modal-footer',
18255         cn: [
18256             {
18257                 tag: 'div',
18258                 cls: 'row',
18259                 cn: [
18260                     {
18261                         tag: 'div',
18262                         cls: 'col-xs-6 text-left',
18263                         cn: {
18264                             tag: 'button',
18265                             cls: 'btn btn-danger roo-touch-view-cancel',
18266                             html: 'Cancel'
18267                         }
18268                     },
18269                     {
18270                         tag: 'div',
18271                         cls: 'col-xs-6 text-right',
18272                         cn: {
18273                             tag: 'button',
18274                             cls: 'btn btn-success roo-touch-view-ok',
18275                             html: 'OK'
18276                         }
18277                     }
18278                 ]
18279             }
18280         ]
18281         
18282     }
18283 });
18284
18285 Roo.apply(Roo.bootstrap.ComboBox,  {
18286     
18287     touchViewTemplate : {
18288         tag: 'div',
18289         cls: 'modal fade roo-combobox-touch-view',
18290         cn: [
18291             {
18292                 tag: 'div',
18293                 cls: 'modal-dialog',
18294                 style : 'position:fixed', // we have to fix position....
18295                 cn: [
18296                     {
18297                         tag: 'div',
18298                         cls: 'modal-content',
18299                         cn: [
18300                             Roo.bootstrap.ComboBox.header,
18301                             Roo.bootstrap.ComboBox.body,
18302                             Roo.bootstrap.ComboBox.footer
18303                         ]
18304                     }
18305                 ]
18306             }
18307         ]
18308     }
18309 });/*
18310  * Based on:
18311  * Ext JS Library 1.1.1
18312  * Copyright(c) 2006-2007, Ext JS, LLC.
18313  *
18314  * Originally Released Under LGPL - original licence link has changed is not relivant.
18315  *
18316  * Fork - LGPL
18317  * <script type="text/javascript">
18318  */
18319
18320 /**
18321  * @class Roo.View
18322  * @extends Roo.util.Observable
18323  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18324  * This class also supports single and multi selection modes. <br>
18325  * Create a data model bound view:
18326  <pre><code>
18327  var store = new Roo.data.Store(...);
18328
18329  var view = new Roo.View({
18330     el : "my-element",
18331     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18332  
18333     singleSelect: true,
18334     selectedClass: "ydataview-selected",
18335     store: store
18336  });
18337
18338  // listen for node click?
18339  view.on("click", function(vw, index, node, e){
18340  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18341  });
18342
18343  // load XML data
18344  dataModel.load("foobar.xml");
18345  </code></pre>
18346  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18347  * <br><br>
18348  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18349  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18350  * 
18351  * Note: old style constructor is still suported (container, template, config)
18352  * 
18353  * @constructor
18354  * Create a new View
18355  * @param {Object} config The config object
18356  * 
18357  */
18358 Roo.View = function(config, depreciated_tpl, depreciated_config){
18359     
18360     this.parent = false;
18361     
18362     if (typeof(depreciated_tpl) == 'undefined') {
18363         // new way.. - universal constructor.
18364         Roo.apply(this, config);
18365         this.el  = Roo.get(this.el);
18366     } else {
18367         // old format..
18368         this.el  = Roo.get(config);
18369         this.tpl = depreciated_tpl;
18370         Roo.apply(this, depreciated_config);
18371     }
18372     this.wrapEl  = this.el.wrap().wrap();
18373     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18374     
18375     
18376     if(typeof(this.tpl) == "string"){
18377         this.tpl = new Roo.Template(this.tpl);
18378     } else {
18379         // support xtype ctors..
18380         this.tpl = new Roo.factory(this.tpl, Roo);
18381     }
18382     
18383     
18384     this.tpl.compile();
18385     
18386     /** @private */
18387     this.addEvents({
18388         /**
18389          * @event beforeclick
18390          * Fires before a click is processed. Returns false to cancel the default action.
18391          * @param {Roo.View} this
18392          * @param {Number} index The index of the target node
18393          * @param {HTMLElement} node The target node
18394          * @param {Roo.EventObject} e The raw event object
18395          */
18396             "beforeclick" : true,
18397         /**
18398          * @event click
18399          * Fires when a template node is clicked.
18400          * @param {Roo.View} this
18401          * @param {Number} index The index of the target node
18402          * @param {HTMLElement} node The target node
18403          * @param {Roo.EventObject} e The raw event object
18404          */
18405             "click" : true,
18406         /**
18407          * @event dblclick
18408          * Fires when a template node is double clicked.
18409          * @param {Roo.View} this
18410          * @param {Number} index The index of the target node
18411          * @param {HTMLElement} node The target node
18412          * @param {Roo.EventObject} e The raw event object
18413          */
18414             "dblclick" : true,
18415         /**
18416          * @event contextmenu
18417          * Fires when a template node is right clicked.
18418          * @param {Roo.View} this
18419          * @param {Number} index The index of the target node
18420          * @param {HTMLElement} node The target node
18421          * @param {Roo.EventObject} e The raw event object
18422          */
18423             "contextmenu" : true,
18424         /**
18425          * @event selectionchange
18426          * Fires when the selected nodes change.
18427          * @param {Roo.View} this
18428          * @param {Array} selections Array of the selected nodes
18429          */
18430             "selectionchange" : true,
18431     
18432         /**
18433          * @event beforeselect
18434          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18435          * @param {Roo.View} this
18436          * @param {HTMLElement} node The node to be selected
18437          * @param {Array} selections Array of currently selected nodes
18438          */
18439             "beforeselect" : true,
18440         /**
18441          * @event preparedata
18442          * Fires on every row to render, to allow you to change the data.
18443          * @param {Roo.View} this
18444          * @param {Object} data to be rendered (change this)
18445          */
18446           "preparedata" : true
18447           
18448           
18449         });
18450
18451
18452
18453     this.el.on({
18454         "click": this.onClick,
18455         "dblclick": this.onDblClick,
18456         "contextmenu": this.onContextMenu,
18457         scope:this
18458     });
18459
18460     this.selections = [];
18461     this.nodes = [];
18462     this.cmp = new Roo.CompositeElementLite([]);
18463     if(this.store){
18464         this.store = Roo.factory(this.store, Roo.data);
18465         this.setStore(this.store, true);
18466     }
18467     
18468     if ( this.footer && this.footer.xtype) {
18469            
18470          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18471         
18472         this.footer.dataSource = this.store;
18473         this.footer.container = fctr;
18474         this.footer = Roo.factory(this.footer, Roo);
18475         fctr.insertFirst(this.el);
18476         
18477         // this is a bit insane - as the paging toolbar seems to detach the el..
18478 //        dom.parentNode.parentNode.parentNode
18479          // they get detached?
18480     }
18481     
18482     
18483     Roo.View.superclass.constructor.call(this);
18484     
18485     
18486 };
18487
18488 Roo.extend(Roo.View, Roo.util.Observable, {
18489     
18490      /**
18491      * @cfg {Roo.data.Store} store Data store to load data from.
18492      */
18493     store : false,
18494     
18495     /**
18496      * @cfg {String|Roo.Element} el The container element.
18497      */
18498     el : '',
18499     
18500     /**
18501      * @cfg {String|Roo.Template} tpl The template used by this View 
18502      */
18503     tpl : false,
18504     /**
18505      * @cfg {String} dataName the named area of the template to use as the data area
18506      *                          Works with domtemplates roo-name="name"
18507      */
18508     dataName: false,
18509     /**
18510      * @cfg {String} selectedClass The css class to add to selected nodes
18511      */
18512     selectedClass : "x-view-selected",
18513      /**
18514      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18515      */
18516     emptyText : "",
18517     
18518     /**
18519      * @cfg {String} text to display on mask (default Loading)
18520      */
18521     mask : false,
18522     /**
18523      * @cfg {Boolean} multiSelect Allow multiple selection
18524      */
18525     multiSelect : false,
18526     /**
18527      * @cfg {Boolean} singleSelect Allow single selection
18528      */
18529     singleSelect:  false,
18530     
18531     /**
18532      * @cfg {Boolean} toggleSelect - selecting 
18533      */
18534     toggleSelect : false,
18535     
18536     /**
18537      * @cfg {Boolean} tickable - selecting 
18538      */
18539     tickable : false,
18540     
18541     /**
18542      * Returns the element this view is bound to.
18543      * @return {Roo.Element}
18544      */
18545     getEl : function(){
18546         return this.wrapEl;
18547     },
18548     
18549     
18550
18551     /**
18552      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18553      */
18554     refresh : function(){
18555         //Roo.log('refresh');
18556         var t = this.tpl;
18557         
18558         // if we are using something like 'domtemplate', then
18559         // the what gets used is:
18560         // t.applySubtemplate(NAME, data, wrapping data..)
18561         // the outer template then get' applied with
18562         //     the store 'extra data'
18563         // and the body get's added to the
18564         //      roo-name="data" node?
18565         //      <span class='roo-tpl-{name}'></span> ?????
18566         
18567         
18568         
18569         this.clearSelections();
18570         this.el.update("");
18571         var html = [];
18572         var records = this.store.getRange();
18573         if(records.length < 1) {
18574             
18575             // is this valid??  = should it render a template??
18576             
18577             this.el.update(this.emptyText);
18578             return;
18579         }
18580         var el = this.el;
18581         if (this.dataName) {
18582             this.el.update(t.apply(this.store.meta)); //????
18583             el = this.el.child('.roo-tpl-' + this.dataName);
18584         }
18585         
18586         for(var i = 0, len = records.length; i < len; i++){
18587             var data = this.prepareData(records[i].data, i, records[i]);
18588             this.fireEvent("preparedata", this, data, i, records[i]);
18589             
18590             var d = Roo.apply({}, data);
18591             
18592             if(this.tickable){
18593                 Roo.apply(d, {'roo-id' : Roo.id()});
18594                 
18595                 var _this = this;
18596             
18597                 Roo.each(this.parent.item, function(item){
18598                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18599                         return;
18600                     }
18601                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18602                 });
18603             }
18604             
18605             html[html.length] = Roo.util.Format.trim(
18606                 this.dataName ?
18607                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18608                     t.apply(d)
18609             );
18610         }
18611         
18612         
18613         
18614         el.update(html.join(""));
18615         this.nodes = el.dom.childNodes;
18616         this.updateIndexes(0);
18617     },
18618     
18619
18620     /**
18621      * Function to override to reformat the data that is sent to
18622      * the template for each node.
18623      * DEPRICATED - use the preparedata event handler.
18624      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18625      * a JSON object for an UpdateManager bound view).
18626      */
18627     prepareData : function(data, index, record)
18628     {
18629         this.fireEvent("preparedata", this, data, index, record);
18630         return data;
18631     },
18632
18633     onUpdate : function(ds, record){
18634         // Roo.log('on update');   
18635         this.clearSelections();
18636         var index = this.store.indexOf(record);
18637         var n = this.nodes[index];
18638         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18639         n.parentNode.removeChild(n);
18640         this.updateIndexes(index, index);
18641     },
18642
18643     
18644     
18645 // --------- FIXME     
18646     onAdd : function(ds, records, index)
18647     {
18648         //Roo.log(['on Add', ds, records, index] );        
18649         this.clearSelections();
18650         if(this.nodes.length == 0){
18651             this.refresh();
18652             return;
18653         }
18654         var n = this.nodes[index];
18655         for(var i = 0, len = records.length; i < len; i++){
18656             var d = this.prepareData(records[i].data, i, records[i]);
18657             if(n){
18658                 this.tpl.insertBefore(n, d);
18659             }else{
18660                 
18661                 this.tpl.append(this.el, d);
18662             }
18663         }
18664         this.updateIndexes(index);
18665     },
18666
18667     onRemove : function(ds, record, index){
18668        // Roo.log('onRemove');
18669         this.clearSelections();
18670         var el = this.dataName  ?
18671             this.el.child('.roo-tpl-' + this.dataName) :
18672             this.el; 
18673         
18674         el.dom.removeChild(this.nodes[index]);
18675         this.updateIndexes(index);
18676     },
18677
18678     /**
18679      * Refresh an individual node.
18680      * @param {Number} index
18681      */
18682     refreshNode : function(index){
18683         this.onUpdate(this.store, this.store.getAt(index));
18684     },
18685
18686     updateIndexes : function(startIndex, endIndex){
18687         var ns = this.nodes;
18688         startIndex = startIndex || 0;
18689         endIndex = endIndex || ns.length - 1;
18690         for(var i = startIndex; i <= endIndex; i++){
18691             ns[i].nodeIndex = i;
18692         }
18693     },
18694
18695     /**
18696      * Changes the data store this view uses and refresh the view.
18697      * @param {Store} store
18698      */
18699     setStore : function(store, initial){
18700         if(!initial && this.store){
18701             this.store.un("datachanged", this.refresh);
18702             this.store.un("add", this.onAdd);
18703             this.store.un("remove", this.onRemove);
18704             this.store.un("update", this.onUpdate);
18705             this.store.un("clear", this.refresh);
18706             this.store.un("beforeload", this.onBeforeLoad);
18707             this.store.un("load", this.onLoad);
18708             this.store.un("loadexception", this.onLoad);
18709         }
18710         if(store){
18711           
18712             store.on("datachanged", this.refresh, this);
18713             store.on("add", this.onAdd, this);
18714             store.on("remove", this.onRemove, this);
18715             store.on("update", this.onUpdate, this);
18716             store.on("clear", this.refresh, this);
18717             store.on("beforeload", this.onBeforeLoad, this);
18718             store.on("load", this.onLoad, this);
18719             store.on("loadexception", this.onLoad, this);
18720         }
18721         
18722         if(store){
18723             this.refresh();
18724         }
18725     },
18726     /**
18727      * onbeforeLoad - masks the loading area.
18728      *
18729      */
18730     onBeforeLoad : function(store,opts)
18731     {
18732          //Roo.log('onBeforeLoad');   
18733         if (!opts.add) {
18734             this.el.update("");
18735         }
18736         this.el.mask(this.mask ? this.mask : "Loading" ); 
18737     },
18738     onLoad : function ()
18739     {
18740         this.el.unmask();
18741     },
18742     
18743
18744     /**
18745      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18746      * @param {HTMLElement} node
18747      * @return {HTMLElement} The template node
18748      */
18749     findItemFromChild : function(node){
18750         var el = this.dataName  ?
18751             this.el.child('.roo-tpl-' + this.dataName,true) :
18752             this.el.dom; 
18753         
18754         if(!node || node.parentNode == el){
18755                     return node;
18756             }
18757             var p = node.parentNode;
18758             while(p && p != el){
18759             if(p.parentNode == el){
18760                 return p;
18761             }
18762             p = p.parentNode;
18763         }
18764             return null;
18765     },
18766
18767     /** @ignore */
18768     onClick : function(e){
18769         var item = this.findItemFromChild(e.getTarget());
18770         if(item){
18771             var index = this.indexOf(item);
18772             if(this.onItemClick(item, index, e) !== false){
18773                 this.fireEvent("click", this, index, item, e);
18774             }
18775         }else{
18776             this.clearSelections();
18777         }
18778     },
18779
18780     /** @ignore */
18781     onContextMenu : function(e){
18782         var item = this.findItemFromChild(e.getTarget());
18783         if(item){
18784             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18785         }
18786     },
18787
18788     /** @ignore */
18789     onDblClick : function(e){
18790         var item = this.findItemFromChild(e.getTarget());
18791         if(item){
18792             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18793         }
18794     },
18795
18796     onItemClick : function(item, index, e)
18797     {
18798         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18799             return false;
18800         }
18801         if (this.toggleSelect) {
18802             var m = this.isSelected(item) ? 'unselect' : 'select';
18803             //Roo.log(m);
18804             var _t = this;
18805             _t[m](item, true, false);
18806             return true;
18807         }
18808         if(this.multiSelect || this.singleSelect){
18809             if(this.multiSelect && e.shiftKey && this.lastSelection){
18810                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18811             }else{
18812                 this.select(item, this.multiSelect && e.ctrlKey);
18813                 this.lastSelection = item;
18814             }
18815             
18816             if(!this.tickable){
18817                 e.preventDefault();
18818             }
18819             
18820         }
18821         return true;
18822     },
18823
18824     /**
18825      * Get the number of selected nodes.
18826      * @return {Number}
18827      */
18828     getSelectionCount : function(){
18829         return this.selections.length;
18830     },
18831
18832     /**
18833      * Get the currently selected nodes.
18834      * @return {Array} An array of HTMLElements
18835      */
18836     getSelectedNodes : function(){
18837         return this.selections;
18838     },
18839
18840     /**
18841      * Get the indexes of the selected nodes.
18842      * @return {Array}
18843      */
18844     getSelectedIndexes : function(){
18845         var indexes = [], s = this.selections;
18846         for(var i = 0, len = s.length; i < len; i++){
18847             indexes.push(s[i].nodeIndex);
18848         }
18849         return indexes;
18850     },
18851
18852     /**
18853      * Clear all selections
18854      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18855      */
18856     clearSelections : function(suppressEvent){
18857         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18858             this.cmp.elements = this.selections;
18859             this.cmp.removeClass(this.selectedClass);
18860             this.selections = [];
18861             if(!suppressEvent){
18862                 this.fireEvent("selectionchange", this, this.selections);
18863             }
18864         }
18865     },
18866
18867     /**
18868      * Returns true if the passed node is selected
18869      * @param {HTMLElement/Number} node The node or node index
18870      * @return {Boolean}
18871      */
18872     isSelected : function(node){
18873         var s = this.selections;
18874         if(s.length < 1){
18875             return false;
18876         }
18877         node = this.getNode(node);
18878         return s.indexOf(node) !== -1;
18879     },
18880
18881     /**
18882      * Selects nodes.
18883      * @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
18884      * @param {Boolean} keepExisting (optional) true to keep existing selections
18885      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18886      */
18887     select : function(nodeInfo, keepExisting, suppressEvent){
18888         if(nodeInfo instanceof Array){
18889             if(!keepExisting){
18890                 this.clearSelections(true);
18891             }
18892             for(var i = 0, len = nodeInfo.length; i < len; i++){
18893                 this.select(nodeInfo[i], true, true);
18894             }
18895             return;
18896         } 
18897         var node = this.getNode(nodeInfo);
18898         if(!node || this.isSelected(node)){
18899             return; // already selected.
18900         }
18901         if(!keepExisting){
18902             this.clearSelections(true);
18903         }
18904         
18905         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18906             Roo.fly(node).addClass(this.selectedClass);
18907             this.selections.push(node);
18908             if(!suppressEvent){
18909                 this.fireEvent("selectionchange", this, this.selections);
18910             }
18911         }
18912         
18913         
18914     },
18915       /**
18916      * Unselects nodes.
18917      * @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
18918      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18919      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18920      */
18921     unselect : function(nodeInfo, keepExisting, suppressEvent)
18922     {
18923         if(nodeInfo instanceof Array){
18924             Roo.each(this.selections, function(s) {
18925                 this.unselect(s, nodeInfo);
18926             }, this);
18927             return;
18928         }
18929         var node = this.getNode(nodeInfo);
18930         if(!node || !this.isSelected(node)){
18931             //Roo.log("not selected");
18932             return; // not selected.
18933         }
18934         // fireevent???
18935         var ns = [];
18936         Roo.each(this.selections, function(s) {
18937             if (s == node ) {
18938                 Roo.fly(node).removeClass(this.selectedClass);
18939
18940                 return;
18941             }
18942             ns.push(s);
18943         },this);
18944         
18945         this.selections= ns;
18946         this.fireEvent("selectionchange", this, this.selections);
18947     },
18948
18949     /**
18950      * Gets a template node.
18951      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18952      * @return {HTMLElement} The node or null if it wasn't found
18953      */
18954     getNode : function(nodeInfo){
18955         if(typeof nodeInfo == "string"){
18956             return document.getElementById(nodeInfo);
18957         }else if(typeof nodeInfo == "number"){
18958             return this.nodes[nodeInfo];
18959         }
18960         return nodeInfo;
18961     },
18962
18963     /**
18964      * Gets a range template nodes.
18965      * @param {Number} startIndex
18966      * @param {Number} endIndex
18967      * @return {Array} An array of nodes
18968      */
18969     getNodes : function(start, end){
18970         var ns = this.nodes;
18971         start = start || 0;
18972         end = typeof end == "undefined" ? ns.length - 1 : end;
18973         var nodes = [];
18974         if(start <= end){
18975             for(var i = start; i <= end; i++){
18976                 nodes.push(ns[i]);
18977             }
18978         } else{
18979             for(var i = start; i >= end; i--){
18980                 nodes.push(ns[i]);
18981             }
18982         }
18983         return nodes;
18984     },
18985
18986     /**
18987      * Finds the index of the passed node
18988      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18989      * @return {Number} The index of the node or -1
18990      */
18991     indexOf : function(node){
18992         node = this.getNode(node);
18993         if(typeof node.nodeIndex == "number"){
18994             return node.nodeIndex;
18995         }
18996         var ns = this.nodes;
18997         for(var i = 0, len = ns.length; i < len; i++){
18998             if(ns[i] == node){
18999                 return i;
19000             }
19001         }
19002         return -1;
19003     }
19004 });
19005 /*
19006  * - LGPL
19007  *
19008  * based on jquery fullcalendar
19009  * 
19010  */
19011
19012 Roo.bootstrap = Roo.bootstrap || {};
19013 /**
19014  * @class Roo.bootstrap.Calendar
19015  * @extends Roo.bootstrap.Component
19016  * Bootstrap Calendar class
19017  * @cfg {Boolean} loadMask (true|false) default false
19018  * @cfg {Object} header generate the user specific header of the calendar, default false
19019
19020  * @constructor
19021  * Create a new Container
19022  * @param {Object} config The config object
19023  */
19024
19025
19026
19027 Roo.bootstrap.Calendar = function(config){
19028     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19029      this.addEvents({
19030         /**
19031              * @event select
19032              * Fires when a date is selected
19033              * @param {DatePicker} this
19034              * @param {Date} date The selected date
19035              */
19036         'select': true,
19037         /**
19038              * @event monthchange
19039              * Fires when the displayed month changes 
19040              * @param {DatePicker} this
19041              * @param {Date} date The selected month
19042              */
19043         'monthchange': true,
19044         /**
19045              * @event evententer
19046              * Fires when mouse over an event
19047              * @param {Calendar} this
19048              * @param {event} Event
19049              */
19050         'evententer': true,
19051         /**
19052              * @event eventleave
19053              * Fires when the mouse leaves an
19054              * @param {Calendar} this
19055              * @param {event}
19056              */
19057         'eventleave': true,
19058         /**
19059              * @event eventclick
19060              * Fires when the mouse click an
19061              * @param {Calendar} this
19062              * @param {event}
19063              */
19064         'eventclick': true
19065         
19066     });
19067
19068 };
19069
19070 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19071     
19072      /**
19073      * @cfg {Number} startDay
19074      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19075      */
19076     startDay : 0,
19077     
19078     loadMask : false,
19079     
19080     header : false,
19081       
19082     getAutoCreate : function(){
19083         
19084         
19085         var fc_button = function(name, corner, style, content ) {
19086             return Roo.apply({},{
19087                 tag : 'span',
19088                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19089                          (corner.length ?
19090                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19091                             ''
19092                         ),
19093                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19094                 unselectable: 'on'
19095             });
19096         };
19097         
19098         var header = {};
19099         
19100         if(!this.header){
19101             header = {
19102                 tag : 'table',
19103                 cls : 'fc-header',
19104                 style : 'width:100%',
19105                 cn : [
19106                     {
19107                         tag: 'tr',
19108                         cn : [
19109                             {
19110                                 tag : 'td',
19111                                 cls : 'fc-header-left',
19112                                 cn : [
19113                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19114                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19115                                     { tag: 'span', cls: 'fc-header-space' },
19116                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19117
19118
19119                                 ]
19120                             },
19121
19122                             {
19123                                 tag : 'td',
19124                                 cls : 'fc-header-center',
19125                                 cn : [
19126                                     {
19127                                         tag: 'span',
19128                                         cls: 'fc-header-title',
19129                                         cn : {
19130                                             tag: 'H2',
19131                                             html : 'month / year'
19132                                         }
19133                                     }
19134
19135                                 ]
19136                             },
19137                             {
19138                                 tag : 'td',
19139                                 cls : 'fc-header-right',
19140                                 cn : [
19141                               /*      fc_button('month', 'left', '', 'month' ),
19142                                     fc_button('week', '', '', 'week' ),
19143                                     fc_button('day', 'right', '', 'day' )
19144                                 */    
19145
19146                                 ]
19147                             }
19148
19149                         ]
19150                     }
19151                 ]
19152             };
19153         }
19154         
19155         header = this.header;
19156         
19157        
19158         var cal_heads = function() {
19159             var ret = [];
19160             // fixme - handle this.
19161             
19162             for (var i =0; i < Date.dayNames.length; i++) {
19163                 var d = Date.dayNames[i];
19164                 ret.push({
19165                     tag: 'th',
19166                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19167                     html : d.substring(0,3)
19168                 });
19169                 
19170             }
19171             ret[0].cls += ' fc-first';
19172             ret[6].cls += ' fc-last';
19173             return ret;
19174         };
19175         var cal_cell = function(n) {
19176             return  {
19177                 tag: 'td',
19178                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19179                 cn : [
19180                     {
19181                         cn : [
19182                             {
19183                                 cls: 'fc-day-number',
19184                                 html: 'D'
19185                             },
19186                             {
19187                                 cls: 'fc-day-content',
19188                              
19189                                 cn : [
19190                                      {
19191                                         style: 'position: relative;' // height: 17px;
19192                                     }
19193                                 ]
19194                             }
19195                             
19196                             
19197                         ]
19198                     }
19199                 ]
19200                 
19201             }
19202         };
19203         var cal_rows = function() {
19204             
19205             var ret = [];
19206             for (var r = 0; r < 6; r++) {
19207                 var row= {
19208                     tag : 'tr',
19209                     cls : 'fc-week',
19210                     cn : []
19211                 };
19212                 
19213                 for (var i =0; i < Date.dayNames.length; i++) {
19214                     var d = Date.dayNames[i];
19215                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19216
19217                 }
19218                 row.cn[0].cls+=' fc-first';
19219                 row.cn[0].cn[0].style = 'min-height:90px';
19220                 row.cn[6].cls+=' fc-last';
19221                 ret.push(row);
19222                 
19223             }
19224             ret[0].cls += ' fc-first';
19225             ret[4].cls += ' fc-prev-last';
19226             ret[5].cls += ' fc-last';
19227             return ret;
19228             
19229         };
19230         
19231         var cal_table = {
19232             tag: 'table',
19233             cls: 'fc-border-separate',
19234             style : 'width:100%',
19235             cellspacing  : 0,
19236             cn : [
19237                 { 
19238                     tag: 'thead',
19239                     cn : [
19240                         { 
19241                             tag: 'tr',
19242                             cls : 'fc-first fc-last',
19243                             cn : cal_heads()
19244                         }
19245                     ]
19246                 },
19247                 { 
19248                     tag: 'tbody',
19249                     cn : cal_rows()
19250                 }
19251                   
19252             ]
19253         };
19254          
19255          var cfg = {
19256             cls : 'fc fc-ltr',
19257             cn : [
19258                 header,
19259                 {
19260                     cls : 'fc-content',
19261                     style : "position: relative;",
19262                     cn : [
19263                         {
19264                             cls : 'fc-view fc-view-month fc-grid',
19265                             style : 'position: relative',
19266                             unselectable : 'on',
19267                             cn : [
19268                                 {
19269                                     cls : 'fc-event-container',
19270                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19271                                 },
19272                                 cal_table
19273                             ]
19274                         }
19275                     ]
19276     
19277                 }
19278            ] 
19279             
19280         };
19281         
19282          
19283         
19284         return cfg;
19285     },
19286     
19287     
19288     initEvents : function()
19289     {
19290         if(!this.store){
19291             throw "can not find store for calendar";
19292         }
19293         
19294         var mark = {
19295             tag: "div",
19296             cls:"x-dlg-mask",
19297             style: "text-align:center",
19298             cn: [
19299                 {
19300                     tag: "div",
19301                     style: "background-color:white;width:50%;margin:250 auto",
19302                     cn: [
19303                         {
19304                             tag: "img",
19305                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19306                         },
19307                         {
19308                             tag: "span",
19309                             html: "Loading"
19310                         }
19311                         
19312                     ]
19313                 }
19314             ]
19315         };
19316         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19317         
19318         var size = this.el.select('.fc-content', true).first().getSize();
19319         this.maskEl.setSize(size.width, size.height);
19320         this.maskEl.enableDisplayMode("block");
19321         if(!this.loadMask){
19322             this.maskEl.hide();
19323         }
19324         
19325         this.store = Roo.factory(this.store, Roo.data);
19326         this.store.on('load', this.onLoad, this);
19327         this.store.on('beforeload', this.onBeforeLoad, this);
19328         
19329         this.resize();
19330         
19331         this.cells = this.el.select('.fc-day',true);
19332         //Roo.log(this.cells);
19333         this.textNodes = this.el.query('.fc-day-number');
19334         this.cells.addClassOnOver('fc-state-hover');
19335         
19336         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19337         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19338         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19339         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19340         
19341         this.on('monthchange', this.onMonthChange, this);
19342         
19343         this.update(new Date().clearTime());
19344     },
19345     
19346     resize : function() {
19347         var sz  = this.el.getSize();
19348         
19349         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19350         this.el.select('.fc-day-content div',true).setHeight(34);
19351     },
19352     
19353     
19354     // private
19355     showPrevMonth : function(e){
19356         this.update(this.activeDate.add("mo", -1));
19357     },
19358     showToday : function(e){
19359         this.update(new Date().clearTime());
19360     },
19361     // private
19362     showNextMonth : function(e){
19363         this.update(this.activeDate.add("mo", 1));
19364     },
19365
19366     // private
19367     showPrevYear : function(){
19368         this.update(this.activeDate.add("y", -1));
19369     },
19370
19371     // private
19372     showNextYear : function(){
19373         this.update(this.activeDate.add("y", 1));
19374     },
19375
19376     
19377    // private
19378     update : function(date)
19379     {
19380         var vd = this.activeDate;
19381         this.activeDate = date;
19382 //        if(vd && this.el){
19383 //            var t = date.getTime();
19384 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19385 //                Roo.log('using add remove');
19386 //                
19387 //                this.fireEvent('monthchange', this, date);
19388 //                
19389 //                this.cells.removeClass("fc-state-highlight");
19390 //                this.cells.each(function(c){
19391 //                   if(c.dateValue == t){
19392 //                       c.addClass("fc-state-highlight");
19393 //                       setTimeout(function(){
19394 //                            try{c.dom.firstChild.focus();}catch(e){}
19395 //                       }, 50);
19396 //                       return false;
19397 //                   }
19398 //                   return true;
19399 //                });
19400 //                return;
19401 //            }
19402 //        }
19403         
19404         var days = date.getDaysInMonth();
19405         
19406         var firstOfMonth = date.getFirstDateOfMonth();
19407         var startingPos = firstOfMonth.getDay()-this.startDay;
19408         
19409         if(startingPos < this.startDay){
19410             startingPos += 7;
19411         }
19412         
19413         var pm = date.add(Date.MONTH, -1);
19414         var prevStart = pm.getDaysInMonth()-startingPos;
19415 //        
19416         this.cells = this.el.select('.fc-day',true);
19417         this.textNodes = this.el.query('.fc-day-number');
19418         this.cells.addClassOnOver('fc-state-hover');
19419         
19420         var cells = this.cells.elements;
19421         var textEls = this.textNodes;
19422         
19423         Roo.each(cells, function(cell){
19424             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19425         });
19426         
19427         days += startingPos;
19428
19429         // convert everything to numbers so it's fast
19430         var day = 86400000;
19431         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19432         //Roo.log(d);
19433         //Roo.log(pm);
19434         //Roo.log(prevStart);
19435         
19436         var today = new Date().clearTime().getTime();
19437         var sel = date.clearTime().getTime();
19438         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19439         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19440         var ddMatch = this.disabledDatesRE;
19441         var ddText = this.disabledDatesText;
19442         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19443         var ddaysText = this.disabledDaysText;
19444         var format = this.format;
19445         
19446         var setCellClass = function(cal, cell){
19447             cell.row = 0;
19448             cell.events = [];
19449             cell.more = [];
19450             //Roo.log('set Cell Class');
19451             cell.title = "";
19452             var t = d.getTime();
19453             
19454             //Roo.log(d);
19455             
19456             cell.dateValue = t;
19457             if(t == today){
19458                 cell.className += " fc-today";
19459                 cell.className += " fc-state-highlight";
19460                 cell.title = cal.todayText;
19461             }
19462             if(t == sel){
19463                 // disable highlight in other month..
19464                 //cell.className += " fc-state-highlight";
19465                 
19466             }
19467             // disabling
19468             if(t < min) {
19469                 cell.className = " fc-state-disabled";
19470                 cell.title = cal.minText;
19471                 return;
19472             }
19473             if(t > max) {
19474                 cell.className = " fc-state-disabled";
19475                 cell.title = cal.maxText;
19476                 return;
19477             }
19478             if(ddays){
19479                 if(ddays.indexOf(d.getDay()) != -1){
19480                     cell.title = ddaysText;
19481                     cell.className = " fc-state-disabled";
19482                 }
19483             }
19484             if(ddMatch && format){
19485                 var fvalue = d.dateFormat(format);
19486                 if(ddMatch.test(fvalue)){
19487                     cell.title = ddText.replace("%0", fvalue);
19488                     cell.className = " fc-state-disabled";
19489                 }
19490             }
19491             
19492             if (!cell.initialClassName) {
19493                 cell.initialClassName = cell.dom.className;
19494             }
19495             
19496             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19497         };
19498
19499         var i = 0;
19500         
19501         for(; i < startingPos; i++) {
19502             textEls[i].innerHTML = (++prevStart);
19503             d.setDate(d.getDate()+1);
19504             
19505             cells[i].className = "fc-past fc-other-month";
19506             setCellClass(this, cells[i]);
19507         }
19508         
19509         var intDay = 0;
19510         
19511         for(; i < days; i++){
19512             intDay = i - startingPos + 1;
19513             textEls[i].innerHTML = (intDay);
19514             d.setDate(d.getDate()+1);
19515             
19516             cells[i].className = ''; // "x-date-active";
19517             setCellClass(this, cells[i]);
19518         }
19519         var extraDays = 0;
19520         
19521         for(; i < 42; i++) {
19522             textEls[i].innerHTML = (++extraDays);
19523             d.setDate(d.getDate()+1);
19524             
19525             cells[i].className = "fc-future fc-other-month";
19526             setCellClass(this, cells[i]);
19527         }
19528         
19529         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19530         
19531         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19532         
19533         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19534         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19535         
19536         if(totalRows != 6){
19537             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19538             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19539         }
19540         
19541         this.fireEvent('monthchange', this, date);
19542         
19543         
19544         /*
19545         if(!this.internalRender){
19546             var main = this.el.dom.firstChild;
19547             var w = main.offsetWidth;
19548             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19549             Roo.fly(main).setWidth(w);
19550             this.internalRender = true;
19551             // opera does not respect the auto grow header center column
19552             // then, after it gets a width opera refuses to recalculate
19553             // without a second pass
19554             if(Roo.isOpera && !this.secondPass){
19555                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19556                 this.secondPass = true;
19557                 this.update.defer(10, this, [date]);
19558             }
19559         }
19560         */
19561         
19562     },
19563     
19564     findCell : function(dt) {
19565         dt = dt.clearTime().getTime();
19566         var ret = false;
19567         this.cells.each(function(c){
19568             //Roo.log("check " +c.dateValue + '?=' + dt);
19569             if(c.dateValue == dt){
19570                 ret = c;
19571                 return false;
19572             }
19573             return true;
19574         });
19575         
19576         return ret;
19577     },
19578     
19579     findCells : function(ev) {
19580         var s = ev.start.clone().clearTime().getTime();
19581        // Roo.log(s);
19582         var e= ev.end.clone().clearTime().getTime();
19583        // Roo.log(e);
19584         var ret = [];
19585         this.cells.each(function(c){
19586              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19587             
19588             if(c.dateValue > e){
19589                 return ;
19590             }
19591             if(c.dateValue < s){
19592                 return ;
19593             }
19594             ret.push(c);
19595         });
19596         
19597         return ret;    
19598     },
19599     
19600 //    findBestRow: function(cells)
19601 //    {
19602 //        var ret = 0;
19603 //        
19604 //        for (var i =0 ; i < cells.length;i++) {
19605 //            ret  = Math.max(cells[i].rows || 0,ret);
19606 //        }
19607 //        return ret;
19608 //        
19609 //    },
19610     
19611     
19612     addItem : function(ev)
19613     {
19614         // look for vertical location slot in
19615         var cells = this.findCells(ev);
19616         
19617 //        ev.row = this.findBestRow(cells);
19618         
19619         // work out the location.
19620         
19621         var crow = false;
19622         var rows = [];
19623         for(var i =0; i < cells.length; i++) {
19624             
19625             cells[i].row = cells[0].row;
19626             
19627             if(i == 0){
19628                 cells[i].row = cells[i].row + 1;
19629             }
19630             
19631             if (!crow) {
19632                 crow = {
19633                     start : cells[i],
19634                     end :  cells[i]
19635                 };
19636                 continue;
19637             }
19638             if (crow.start.getY() == cells[i].getY()) {
19639                 // on same row.
19640                 crow.end = cells[i];
19641                 continue;
19642             }
19643             // different row.
19644             rows.push(crow);
19645             crow = {
19646                 start: cells[i],
19647                 end : cells[i]
19648             };
19649             
19650         }
19651         
19652         rows.push(crow);
19653         ev.els = [];
19654         ev.rows = rows;
19655         ev.cells = cells;
19656         
19657         cells[0].events.push(ev);
19658         
19659         this.calevents.push(ev);
19660     },
19661     
19662     clearEvents: function() {
19663         
19664         if(!this.calevents){
19665             return;
19666         }
19667         
19668         Roo.each(this.cells.elements, function(c){
19669             c.row = 0;
19670             c.events = [];
19671             c.more = [];
19672         });
19673         
19674         Roo.each(this.calevents, function(e) {
19675             Roo.each(e.els, function(el) {
19676                 el.un('mouseenter' ,this.onEventEnter, this);
19677                 el.un('mouseleave' ,this.onEventLeave, this);
19678                 el.remove();
19679             },this);
19680         },this);
19681         
19682         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19683             e.remove();
19684         });
19685         
19686     },
19687     
19688     renderEvents: function()
19689     {   
19690         var _this = this;
19691         
19692         this.cells.each(function(c) {
19693             
19694             if(c.row < 5){
19695                 return;
19696             }
19697             
19698             var ev = c.events;
19699             
19700             var r = 4;
19701             if(c.row != c.events.length){
19702                 r = 4 - (4 - (c.row - c.events.length));
19703             }
19704             
19705             c.events = ev.slice(0, r);
19706             c.more = ev.slice(r);
19707             
19708             if(c.more.length && c.more.length == 1){
19709                 c.events.push(c.more.pop());
19710             }
19711             
19712             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19713             
19714         });
19715             
19716         this.cells.each(function(c) {
19717             
19718             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19719             
19720             
19721             for (var e = 0; e < c.events.length; e++){
19722                 var ev = c.events[e];
19723                 var rows = ev.rows;
19724                 
19725                 for(var i = 0; i < rows.length; i++) {
19726                 
19727                     // how many rows should it span..
19728
19729                     var  cfg = {
19730                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19731                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19732
19733                         unselectable : "on",
19734                         cn : [
19735                             {
19736                                 cls: 'fc-event-inner',
19737                                 cn : [
19738     //                                {
19739     //                                  tag:'span',
19740     //                                  cls: 'fc-event-time',
19741     //                                  html : cells.length > 1 ? '' : ev.time
19742     //                                },
19743                                     {
19744                                       tag:'span',
19745                                       cls: 'fc-event-title',
19746                                       html : String.format('{0}', ev.title)
19747                                     }
19748
19749
19750                                 ]
19751                             },
19752                             {
19753                                 cls: 'ui-resizable-handle ui-resizable-e',
19754                                 html : '&nbsp;&nbsp;&nbsp'
19755                             }
19756
19757                         ]
19758                     };
19759
19760                     if (i == 0) {
19761                         cfg.cls += ' fc-event-start';
19762                     }
19763                     if ((i+1) == rows.length) {
19764                         cfg.cls += ' fc-event-end';
19765                     }
19766
19767                     var ctr = _this.el.select('.fc-event-container',true).first();
19768                     var cg = ctr.createChild(cfg);
19769
19770                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19771                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19772
19773                     var r = (c.more.length) ? 1 : 0;
19774                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19775                     cg.setWidth(ebox.right - sbox.x -2);
19776
19777                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19778                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19779                     cg.on('click', _this.onEventClick, _this, ev);
19780
19781                     ev.els.push(cg);
19782                     
19783                 }
19784                 
19785             }
19786             
19787             
19788             if(c.more.length){
19789                 var  cfg = {
19790                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19791                     style : 'position: absolute',
19792                     unselectable : "on",
19793                     cn : [
19794                         {
19795                             cls: 'fc-event-inner',
19796                             cn : [
19797                                 {
19798                                   tag:'span',
19799                                   cls: 'fc-event-title',
19800                                   html : 'More'
19801                                 }
19802
19803
19804                             ]
19805                         },
19806                         {
19807                             cls: 'ui-resizable-handle ui-resizable-e',
19808                             html : '&nbsp;&nbsp;&nbsp'
19809                         }
19810
19811                     ]
19812                 };
19813
19814                 var ctr = _this.el.select('.fc-event-container',true).first();
19815                 var cg = ctr.createChild(cfg);
19816
19817                 var sbox = c.select('.fc-day-content',true).first().getBox();
19818                 var ebox = c.select('.fc-day-content',true).first().getBox();
19819                 //Roo.log(cg);
19820                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19821                 cg.setWidth(ebox.right - sbox.x -2);
19822
19823                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19824                 
19825             }
19826             
19827         });
19828         
19829         
19830         
19831     },
19832     
19833     onEventEnter: function (e, el,event,d) {
19834         this.fireEvent('evententer', this, el, event);
19835     },
19836     
19837     onEventLeave: function (e, el,event,d) {
19838         this.fireEvent('eventleave', this, el, event);
19839     },
19840     
19841     onEventClick: function (e, el,event,d) {
19842         this.fireEvent('eventclick', this, el, event);
19843     },
19844     
19845     onMonthChange: function () {
19846         this.store.load();
19847     },
19848     
19849     onMoreEventClick: function(e, el, more)
19850     {
19851         var _this = this;
19852         
19853         this.calpopover.placement = 'right';
19854         this.calpopover.setTitle('More');
19855         
19856         this.calpopover.setContent('');
19857         
19858         var ctr = this.calpopover.el.select('.popover-content', true).first();
19859         
19860         Roo.each(more, function(m){
19861             var cfg = {
19862                 cls : 'fc-event-hori fc-event-draggable',
19863                 html : m.title
19864             };
19865             var cg = ctr.createChild(cfg);
19866             
19867             cg.on('click', _this.onEventClick, _this, m);
19868         });
19869         
19870         this.calpopover.show(el);
19871         
19872         
19873     },
19874     
19875     onLoad: function () 
19876     {   
19877         this.calevents = [];
19878         var cal = this;
19879         
19880         if(this.store.getCount() > 0){
19881             this.store.data.each(function(d){
19882                cal.addItem({
19883                     id : d.data.id,
19884                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19885                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19886                     time : d.data.start_time,
19887                     title : d.data.title,
19888                     description : d.data.description,
19889                     venue : d.data.venue
19890                 });
19891             });
19892         }
19893         
19894         this.renderEvents();
19895         
19896         if(this.calevents.length && this.loadMask){
19897             this.maskEl.hide();
19898         }
19899     },
19900     
19901     onBeforeLoad: function()
19902     {
19903         this.clearEvents();
19904         if(this.loadMask){
19905             this.maskEl.show();
19906         }
19907     }
19908 });
19909
19910  
19911  /*
19912  * - LGPL
19913  *
19914  * element
19915  * 
19916  */
19917
19918 /**
19919  * @class Roo.bootstrap.Popover
19920  * @extends Roo.bootstrap.Component
19921  * Bootstrap Popover class
19922  * @cfg {String} html contents of the popover   (or false to use children..)
19923  * @cfg {String} title of popover (or false to hide)
19924  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19925  * @cfg {String} trigger click || hover (or false to trigger manually)
19926  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19927  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19928  *      - if false and it has a 'parent' then it will be automatically added to that element
19929  *      - if string - Roo.get  will be called 
19930  * @cfg {Number} delay - delay before showing
19931  
19932  * @constructor
19933  * Create a new Popover
19934  * @param {Object} config The config object
19935  */
19936
19937 Roo.bootstrap.Popover = function(config){
19938     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19939     
19940     this.addEvents({
19941         // raw events
19942          /**
19943          * @event show
19944          * After the popover show
19945          * 
19946          * @param {Roo.bootstrap.Popover} this
19947          */
19948         "show" : true,
19949         /**
19950          * @event hide
19951          * After the popover hide
19952          * 
19953          * @param {Roo.bootstrap.Popover} this
19954          */
19955         "hide" : true
19956     });
19957 };
19958
19959 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19960     
19961     title: false,
19962     html: false,
19963     
19964     placement : 'right',
19965     trigger : 'hover', // hover
19966     modal : false,
19967     delay : 0,
19968     
19969     over: false,
19970     
19971     can_build_overlaid : false,
19972     
19973     maskEl : false, // the mask element
19974     headerEl : false,
19975     contentEl : false,
19976     alignEl : false, // when show is called with an element - this get's stored.
19977     
19978     getChildContainer : function()
19979     {
19980         return this.contentEl;
19981         
19982     },
19983     getPopoverHeader : function()
19984     {
19985         this.title = true; // flag not to hide it..
19986         this.headerEl.addClass('p-0');
19987         return this.headerEl
19988     },
19989     
19990     
19991     getAutoCreate : function(){
19992          
19993         var cfg = {
19994            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19995            style: 'display:block',
19996            cn : [
19997                 {
19998                     cls : 'arrow'
19999                 },
20000                 {
20001                     cls : 'popover-inner ',
20002                     cn : [
20003                         {
20004                             tag: 'h3',
20005                             cls: 'popover-title popover-header',
20006                             html : this.title === false ? '' : this.title
20007                         },
20008                         {
20009                             cls : 'popover-content popover-body '  + (this.cls || ''),
20010                             html : this.html || ''
20011                         }
20012                     ]
20013                     
20014                 }
20015            ]
20016         };
20017         
20018         return cfg;
20019     },
20020     /**
20021      * @param {string} the title
20022      */
20023     setTitle: function(str)
20024     {
20025         this.title = str;
20026         if (this.el) {
20027             this.headerEl.dom.innerHTML = str;
20028         }
20029         
20030     },
20031     /**
20032      * @param {string} the body content
20033      */
20034     setContent: function(str)
20035     {
20036         this.html = str;
20037         if (this.contentEl) {
20038             this.contentEl.dom.innerHTML = str;
20039         }
20040         
20041     },
20042     // as it get's added to the bottom of the page.
20043     onRender : function(ct, position)
20044     {
20045         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20046         
20047         
20048         
20049         if(!this.el){
20050             var cfg = Roo.apply({},  this.getAutoCreate());
20051             cfg.id = Roo.id();
20052             
20053             if (this.cls) {
20054                 cfg.cls += ' ' + this.cls;
20055             }
20056             if (this.style) {
20057                 cfg.style = this.style;
20058             }
20059             //Roo.log("adding to ");
20060             this.el = Roo.get(document.body).createChild(cfg, position);
20061 //            Roo.log(this.el);
20062         }
20063         
20064         this.contentEl = this.el.select('.popover-content',true).first();
20065         this.headerEl =  this.el.select('.popover-title',true).first();
20066         
20067         var nitems = [];
20068         if(typeof(this.items) != 'undefined'){
20069             var items = this.items;
20070             delete this.items;
20071
20072             for(var i =0;i < items.length;i++) {
20073                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20074             }
20075         }
20076
20077         this.items = nitems;
20078         
20079         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20080         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20081         
20082         
20083         
20084         this.initEvents();
20085     },
20086     
20087     resizeMask : function()
20088     {
20089         this.maskEl.setSize(
20090             Roo.lib.Dom.getViewWidth(true),
20091             Roo.lib.Dom.getViewHeight(true)
20092         );
20093     },
20094     
20095     initEvents : function()
20096     {
20097         
20098         if (!this.modal) { 
20099             Roo.bootstrap.Popover.register(this);
20100         }
20101          
20102         this.arrowEl = this.el.select('.arrow',true).first();
20103         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20104         this.el.enableDisplayMode('block');
20105         this.el.hide();
20106  
20107         
20108         if (this.over === false && !this.parent()) {
20109             return; 
20110         }
20111         if (this.triggers === false) {
20112             return;
20113         }
20114          
20115         // support parent
20116         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20117         var triggers = this.trigger ? this.trigger.split(' ') : [];
20118         Roo.each(triggers, function(trigger) {
20119         
20120             if (trigger == 'click') {
20121                 on_el.on('click', this.toggle, this);
20122             } else if (trigger != 'manual') {
20123                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20124                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20125       
20126                 on_el.on(eventIn  ,this.enter, this);
20127                 on_el.on(eventOut, this.leave, this);
20128             }
20129         }, this);
20130     },
20131     
20132     
20133     // private
20134     timeout : null,
20135     hoverState : null,
20136     
20137     toggle : function () {
20138         this.hoverState == 'in' ? this.leave() : this.enter();
20139     },
20140     
20141     enter : function () {
20142         
20143         clearTimeout(this.timeout);
20144     
20145         this.hoverState = 'in';
20146     
20147         if (!this.delay || !this.delay.show) {
20148             this.show();
20149             return;
20150         }
20151         var _t = this;
20152         this.timeout = setTimeout(function () {
20153             if (_t.hoverState == 'in') {
20154                 _t.show();
20155             }
20156         }, this.delay.show)
20157     },
20158     
20159     leave : function() {
20160         clearTimeout(this.timeout);
20161     
20162         this.hoverState = 'out';
20163     
20164         if (!this.delay || !this.delay.hide) {
20165             this.hide();
20166             return;
20167         }
20168         var _t = this;
20169         this.timeout = setTimeout(function () {
20170             if (_t.hoverState == 'out') {
20171                 _t.hide();
20172             }
20173         }, this.delay.hide)
20174     },
20175     /**
20176      * Show the popover
20177      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20178      * @param {string} (left|right|top|bottom) position
20179      */
20180     show : function (on_el, placement)
20181     {
20182         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20183         on_el = on_el || false; // default to false
20184          
20185         if (!on_el) {
20186             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20187                 on_el = this.parent().el;
20188             } else if (this.over) {
20189                 Roo.get(this.over);
20190             }
20191             
20192         }
20193         
20194         this.alignEl = Roo.get( on_el );
20195
20196         if (!this.el) {
20197             this.render(document.body);
20198         }
20199         
20200         
20201          
20202         
20203         if (this.title === false) {
20204             this.headerEl.hide();
20205         }
20206         
20207        
20208         this.el.show();
20209         this.el.dom.style.display = 'block';
20210          
20211  
20212         if (this.alignEl) {
20213             this.updatePosition(this.placement, true);
20214              
20215         } else {
20216             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20217             var es = this.el.getSize();
20218             var x = Roo.lib.Dom.getViewWidth()/2;
20219             var y = Roo.lib.Dom.getViewHeight()/2;
20220             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20221             
20222         }
20223
20224         
20225         //var arrow = this.el.select('.arrow',true).first();
20226         //arrow.set(align[2], 
20227         
20228         this.el.addClass('in');
20229         
20230          
20231         
20232         this.hoverState = 'in';
20233         
20234         if (this.modal) {
20235             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20236             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20237             this.maskEl.dom.style.display = 'block';
20238             this.maskEl.addClass('show');
20239         }
20240         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20241  
20242         this.fireEvent('show', this);
20243         
20244     },
20245     /**
20246      * fire this manually after loading a grid in the table for example
20247      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20248      * @param {Boolean} try and move it if we cant get right position.
20249      */
20250     updatePosition : function(placement, try_move)
20251     {
20252         // allow for calling with no parameters
20253         placement = placement   ? placement :  this.placement;
20254         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20255         
20256         this.el.removeClass([
20257             'fade','top','bottom', 'left', 'right','in',
20258             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20259         ]);
20260         this.el.addClass(placement + ' bs-popover-' + placement);
20261         
20262         if (!this.alignEl ) {
20263             return false;
20264         }
20265         
20266         switch (placement) {
20267             case 'right':
20268                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20269                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20270                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20271                     //normal display... or moved up/down.
20272                     this.el.setXY(offset);
20273                     var xy = this.alignEl.getAnchorXY('tr', false);
20274                     xy[0]+=2;xy[1]+=5;
20275                     this.arrowEl.setXY(xy);
20276                     return true;
20277                 }
20278                 // continue through...
20279                 return this.updatePosition('left', false);
20280                 
20281             
20282             case 'left':
20283                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20284                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20285                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20286                     //normal display... or moved up/down.
20287                     this.el.setXY(offset);
20288                     var xy = this.alignEl.getAnchorXY('tl', false);
20289                     xy[0]-=10;xy[1]+=5; // << fix me
20290                     this.arrowEl.setXY(xy);
20291                     return true;
20292                 }
20293                 // call self...
20294                 return this.updatePosition('right', false);
20295             
20296             case 'top':
20297                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20298                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20299                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20300                     //normal display... or moved up/down.
20301                     this.el.setXY(offset);
20302                     var xy = this.alignEl.getAnchorXY('t', false);
20303                     xy[1]-=10; // << fix me
20304                     this.arrowEl.setXY(xy);
20305                     return true;
20306                 }
20307                 // fall through
20308                return this.updatePosition('bottom', false);
20309             
20310             case 'bottom':
20311                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20312                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20313                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20314                     //normal display... or moved up/down.
20315                     this.el.setXY(offset);
20316                     var xy = this.alignEl.getAnchorXY('b', false);
20317                      xy[1]+=2; // << fix me
20318                     this.arrowEl.setXY(xy);
20319                     return true;
20320                 }
20321                 // fall through
20322                 return this.updatePosition('top', false);
20323                 
20324             
20325         }
20326         
20327         
20328         return false;
20329     },
20330     
20331     hide : function()
20332     {
20333         this.el.setXY([0,0]);
20334         this.el.removeClass('in');
20335         this.el.hide();
20336         this.hoverState = null;
20337         this.maskEl.hide(); // always..
20338         this.fireEvent('hide', this);
20339     }
20340     
20341 });
20342
20343
20344 Roo.apply(Roo.bootstrap.Popover, {
20345
20346     alignment : {
20347         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20348         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20349         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20350         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20351     },
20352     
20353     zIndex : 20001,
20354
20355     clickHander : false,
20356     
20357
20358     onMouseDown : function(e)
20359     {
20360         if (!e.getTarget(".roo-popover")) {
20361             this.hideAll();
20362         }
20363          
20364     },
20365     
20366     popups : [],
20367     
20368     register : function(popup)
20369     {
20370         if (!Roo.bootstrap.Popover.clickHandler) {
20371             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20372         }
20373         // hide other popups.
20374         this.hideAll();
20375         this.popups.push(popup);
20376     },
20377     hideAll : function()
20378     {
20379         this.popups.forEach(function(p) {
20380             p.hide();
20381         });
20382     }
20383
20384 });/*
20385  * - LGPL
20386  *
20387  * Card header - holder for the card header elements.
20388  * 
20389  */
20390
20391 /**
20392  * @class Roo.bootstrap.PopoverNav
20393  * @extends Roo.bootstrap.NavGroup
20394  * Bootstrap Popover header navigation class
20395  * @constructor
20396  * Create a new Popover Header Navigation 
20397  * @param {Object} config The config object
20398  */
20399
20400 Roo.bootstrap.PopoverNav = function(config){
20401     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20402 };
20403
20404 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20405     
20406     
20407     container_method : 'getPopoverHeader' 
20408     
20409      
20410     
20411     
20412    
20413 });
20414
20415  
20416
20417  /*
20418  * - LGPL
20419  *
20420  * Progress
20421  * 
20422  */
20423
20424 /**
20425  * @class Roo.bootstrap.Progress
20426  * @extends Roo.bootstrap.Component
20427  * Bootstrap Progress class
20428  * @cfg {Boolean} striped striped of the progress bar
20429  * @cfg {Boolean} active animated of the progress bar
20430  * 
20431  * 
20432  * @constructor
20433  * Create a new Progress
20434  * @param {Object} config The config object
20435  */
20436
20437 Roo.bootstrap.Progress = function(config){
20438     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20439 };
20440
20441 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20442     
20443     striped : false,
20444     active: false,
20445     
20446     getAutoCreate : function(){
20447         var cfg = {
20448             tag: 'div',
20449             cls: 'progress'
20450         };
20451         
20452         
20453         if(this.striped){
20454             cfg.cls += ' progress-striped';
20455         }
20456       
20457         if(this.active){
20458             cfg.cls += ' active';
20459         }
20460         
20461         
20462         return cfg;
20463     }
20464    
20465 });
20466
20467  
20468
20469  /*
20470  * - LGPL
20471  *
20472  * ProgressBar
20473  * 
20474  */
20475
20476 /**
20477  * @class Roo.bootstrap.ProgressBar
20478  * @extends Roo.bootstrap.Component
20479  * Bootstrap ProgressBar class
20480  * @cfg {Number} aria_valuenow aria-value now
20481  * @cfg {Number} aria_valuemin aria-value min
20482  * @cfg {Number} aria_valuemax aria-value max
20483  * @cfg {String} label label for the progress bar
20484  * @cfg {String} panel (success | info | warning | danger )
20485  * @cfg {String} role role of the progress bar
20486  * @cfg {String} sr_only text
20487  * 
20488  * 
20489  * @constructor
20490  * Create a new ProgressBar
20491  * @param {Object} config The config object
20492  */
20493
20494 Roo.bootstrap.ProgressBar = function(config){
20495     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20496 };
20497
20498 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20499     
20500     aria_valuenow : 0,
20501     aria_valuemin : 0,
20502     aria_valuemax : 100,
20503     label : false,
20504     panel : false,
20505     role : false,
20506     sr_only: false,
20507     
20508     getAutoCreate : function()
20509     {
20510         
20511         var cfg = {
20512             tag: 'div',
20513             cls: 'progress-bar',
20514             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20515         };
20516         
20517         if(this.sr_only){
20518             cfg.cn = {
20519                 tag: 'span',
20520                 cls: 'sr-only',
20521                 html: this.sr_only
20522             }
20523         }
20524         
20525         if(this.role){
20526             cfg.role = this.role;
20527         }
20528         
20529         if(this.aria_valuenow){
20530             cfg['aria-valuenow'] = this.aria_valuenow;
20531         }
20532         
20533         if(this.aria_valuemin){
20534             cfg['aria-valuemin'] = this.aria_valuemin;
20535         }
20536         
20537         if(this.aria_valuemax){
20538             cfg['aria-valuemax'] = this.aria_valuemax;
20539         }
20540         
20541         if(this.label && !this.sr_only){
20542             cfg.html = this.label;
20543         }
20544         
20545         if(this.panel){
20546             cfg.cls += ' progress-bar-' + this.panel;
20547         }
20548         
20549         return cfg;
20550     },
20551     
20552     update : function(aria_valuenow)
20553     {
20554         this.aria_valuenow = aria_valuenow;
20555         
20556         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20557     }
20558    
20559 });
20560
20561  
20562
20563  /*
20564  * - LGPL
20565  *
20566  * column
20567  * 
20568  */
20569
20570 /**
20571  * @class Roo.bootstrap.TabGroup
20572  * @extends Roo.bootstrap.Column
20573  * Bootstrap Column class
20574  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20575  * @cfg {Boolean} carousel true to make the group behave like a carousel
20576  * @cfg {Boolean} bullets show bullets for the panels
20577  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20578  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20579  * @cfg {Boolean} showarrow (true|false) show arrow default true
20580  * 
20581  * @constructor
20582  * Create a new TabGroup
20583  * @param {Object} config The config object
20584  */
20585
20586 Roo.bootstrap.TabGroup = function(config){
20587     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20588     if (!this.navId) {
20589         this.navId = Roo.id();
20590     }
20591     this.tabs = [];
20592     Roo.bootstrap.TabGroup.register(this);
20593     
20594 };
20595
20596 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20597     
20598     carousel : false,
20599     transition : false,
20600     bullets : 0,
20601     timer : 0,
20602     autoslide : false,
20603     slideFn : false,
20604     slideOnTouch : false,
20605     showarrow : true,
20606     
20607     getAutoCreate : function()
20608     {
20609         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20610         
20611         cfg.cls += ' tab-content';
20612         
20613         if (this.carousel) {
20614             cfg.cls += ' carousel slide';
20615             
20616             cfg.cn = [{
20617                cls : 'carousel-inner',
20618                cn : []
20619             }];
20620         
20621             if(this.bullets  && !Roo.isTouch){
20622                 
20623                 var bullets = {
20624                     cls : 'carousel-bullets',
20625                     cn : []
20626                 };
20627                
20628                 if(this.bullets_cls){
20629                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20630                 }
20631                 
20632                 bullets.cn.push({
20633                     cls : 'clear'
20634                 });
20635                 
20636                 cfg.cn[0].cn.push(bullets);
20637             }
20638             
20639             if(this.showarrow){
20640                 cfg.cn[0].cn.push({
20641                     tag : 'div',
20642                     class : 'carousel-arrow',
20643                     cn : [
20644                         {
20645                             tag : 'div',
20646                             class : 'carousel-prev',
20647                             cn : [
20648                                 {
20649                                     tag : 'i',
20650                                     class : 'fa fa-chevron-left'
20651                                 }
20652                             ]
20653                         },
20654                         {
20655                             tag : 'div',
20656                             class : 'carousel-next',
20657                             cn : [
20658                                 {
20659                                     tag : 'i',
20660                                     class : 'fa fa-chevron-right'
20661                                 }
20662                             ]
20663                         }
20664                     ]
20665                 });
20666             }
20667             
20668         }
20669         
20670         return cfg;
20671     },
20672     
20673     initEvents:  function()
20674     {
20675 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20676 //            this.el.on("touchstart", this.onTouchStart, this);
20677 //        }
20678         
20679         if(this.autoslide){
20680             var _this = this;
20681             
20682             this.slideFn = window.setInterval(function() {
20683                 _this.showPanelNext();
20684             }, this.timer);
20685         }
20686         
20687         if(this.showarrow){
20688             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20689             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20690         }
20691         
20692         
20693     },
20694     
20695 //    onTouchStart : function(e, el, o)
20696 //    {
20697 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20698 //            return;
20699 //        }
20700 //        
20701 //        this.showPanelNext();
20702 //    },
20703     
20704     
20705     getChildContainer : function()
20706     {
20707         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20708     },
20709     
20710     /**
20711     * register a Navigation item
20712     * @param {Roo.bootstrap.NavItem} the navitem to add
20713     */
20714     register : function(item)
20715     {
20716         this.tabs.push( item);
20717         item.navId = this.navId; // not really needed..
20718         this.addBullet();
20719     
20720     },
20721     
20722     getActivePanel : function()
20723     {
20724         var r = false;
20725         Roo.each(this.tabs, function(t) {
20726             if (t.active) {
20727                 r = t;
20728                 return false;
20729             }
20730             return null;
20731         });
20732         return r;
20733         
20734     },
20735     getPanelByName : function(n)
20736     {
20737         var r = false;
20738         Roo.each(this.tabs, function(t) {
20739             if (t.tabId == n) {
20740                 r = t;
20741                 return false;
20742             }
20743             return null;
20744         });
20745         return r;
20746     },
20747     indexOfPanel : function(p)
20748     {
20749         var r = false;
20750         Roo.each(this.tabs, function(t,i) {
20751             if (t.tabId == p.tabId) {
20752                 r = i;
20753                 return false;
20754             }
20755             return null;
20756         });
20757         return r;
20758     },
20759     /**
20760      * show a specific panel
20761      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20762      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20763      */
20764     showPanel : function (pan)
20765     {
20766         if(this.transition || typeof(pan) == 'undefined'){
20767             Roo.log("waiting for the transitionend");
20768             return false;
20769         }
20770         
20771         if (typeof(pan) == 'number') {
20772             pan = this.tabs[pan];
20773         }
20774         
20775         if (typeof(pan) == 'string') {
20776             pan = this.getPanelByName(pan);
20777         }
20778         
20779         var cur = this.getActivePanel();
20780         
20781         if(!pan || !cur){
20782             Roo.log('pan or acitve pan is undefined');
20783             return false;
20784         }
20785         
20786         if (pan.tabId == this.getActivePanel().tabId) {
20787             return true;
20788         }
20789         
20790         if (false === cur.fireEvent('beforedeactivate')) {
20791             return false;
20792         }
20793         
20794         if(this.bullets > 0 && !Roo.isTouch){
20795             this.setActiveBullet(this.indexOfPanel(pan));
20796         }
20797         
20798         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20799             
20800             //class="carousel-item carousel-item-next carousel-item-left"
20801             
20802             this.transition = true;
20803             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20804             var lr = dir == 'next' ? 'left' : 'right';
20805             pan.el.addClass(dir); // or prev
20806             pan.el.addClass('carousel-item-' + dir); // or prev
20807             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20808             cur.el.addClass(lr); // or right
20809             pan.el.addClass(lr);
20810             cur.el.addClass('carousel-item-' +lr); // or right
20811             pan.el.addClass('carousel-item-' +lr);
20812             
20813             
20814             var _this = this;
20815             cur.el.on('transitionend', function() {
20816                 Roo.log("trans end?");
20817                 
20818                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20819                 pan.setActive(true);
20820                 
20821                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20822                 cur.setActive(false);
20823                 
20824                 _this.transition = false;
20825                 
20826             }, this, { single:  true } );
20827             
20828             return true;
20829         }
20830         
20831         cur.setActive(false);
20832         pan.setActive(true);
20833         
20834         return true;
20835         
20836     },
20837     showPanelNext : function()
20838     {
20839         var i = this.indexOfPanel(this.getActivePanel());
20840         
20841         if (i >= this.tabs.length - 1 && !this.autoslide) {
20842             return;
20843         }
20844         
20845         if (i >= this.tabs.length - 1 && this.autoslide) {
20846             i = -1;
20847         }
20848         
20849         this.showPanel(this.tabs[i+1]);
20850     },
20851     
20852     showPanelPrev : function()
20853     {
20854         var i = this.indexOfPanel(this.getActivePanel());
20855         
20856         if (i  < 1 && !this.autoslide) {
20857             return;
20858         }
20859         
20860         if (i < 1 && this.autoslide) {
20861             i = this.tabs.length;
20862         }
20863         
20864         this.showPanel(this.tabs[i-1]);
20865     },
20866     
20867     
20868     addBullet: function()
20869     {
20870         if(!this.bullets || Roo.isTouch){
20871             return;
20872         }
20873         var ctr = this.el.select('.carousel-bullets',true).first();
20874         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20875         var bullet = ctr.createChild({
20876             cls : 'bullet bullet-' + i
20877         },ctr.dom.lastChild);
20878         
20879         
20880         var _this = this;
20881         
20882         bullet.on('click', (function(e, el, o, ii, t){
20883
20884             e.preventDefault();
20885
20886             this.showPanel(ii);
20887
20888             if(this.autoslide && this.slideFn){
20889                 clearInterval(this.slideFn);
20890                 this.slideFn = window.setInterval(function() {
20891                     _this.showPanelNext();
20892                 }, this.timer);
20893             }
20894
20895         }).createDelegate(this, [i, bullet], true));
20896                 
20897         
20898     },
20899      
20900     setActiveBullet : function(i)
20901     {
20902         if(Roo.isTouch){
20903             return;
20904         }
20905         
20906         Roo.each(this.el.select('.bullet', true).elements, function(el){
20907             el.removeClass('selected');
20908         });
20909
20910         var bullet = this.el.select('.bullet-' + i, true).first();
20911         
20912         if(!bullet){
20913             return;
20914         }
20915         
20916         bullet.addClass('selected');
20917     }
20918     
20919     
20920   
20921 });
20922
20923  
20924
20925  
20926  
20927 Roo.apply(Roo.bootstrap.TabGroup, {
20928     
20929     groups: {},
20930      /**
20931     * register a Navigation Group
20932     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20933     */
20934     register : function(navgrp)
20935     {
20936         this.groups[navgrp.navId] = navgrp;
20937         
20938     },
20939     /**
20940     * fetch a Navigation Group based on the navigation ID
20941     * if one does not exist , it will get created.
20942     * @param {string} the navgroup to add
20943     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20944     */
20945     get: function(navId) {
20946         if (typeof(this.groups[navId]) == 'undefined') {
20947             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20948         }
20949         return this.groups[navId] ;
20950     }
20951     
20952     
20953     
20954 });
20955
20956  /*
20957  * - LGPL
20958  *
20959  * TabPanel
20960  * 
20961  */
20962
20963 /**
20964  * @class Roo.bootstrap.TabPanel
20965  * @extends Roo.bootstrap.Component
20966  * Bootstrap TabPanel class
20967  * @cfg {Boolean} active panel active
20968  * @cfg {String} html panel content
20969  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20970  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20971  * @cfg {String} href click to link..
20972  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20973  * 
20974  * 
20975  * @constructor
20976  * Create a new TabPanel
20977  * @param {Object} config The config object
20978  */
20979
20980 Roo.bootstrap.TabPanel = function(config){
20981     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20982     this.addEvents({
20983         /**
20984              * @event changed
20985              * Fires when the active status changes
20986              * @param {Roo.bootstrap.TabPanel} this
20987              * @param {Boolean} state the new state
20988             
20989          */
20990         'changed': true,
20991         /**
20992              * @event beforedeactivate
20993              * Fires before a tab is de-activated - can be used to do validation on a form.
20994              * @param {Roo.bootstrap.TabPanel} this
20995              * @return {Boolean} false if there is an error
20996             
20997          */
20998         'beforedeactivate': true
20999      });
21000     
21001     this.tabId = this.tabId || Roo.id();
21002   
21003 };
21004
21005 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21006     
21007     active: false,
21008     html: false,
21009     tabId: false,
21010     navId : false,
21011     href : '',
21012     touchSlide : false,
21013     getAutoCreate : function(){
21014         
21015         
21016         var cfg = {
21017             tag: 'div',
21018             // item is needed for carousel - not sure if it has any effect otherwise
21019             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21020             html: this.html || ''
21021         };
21022         
21023         if(this.active){
21024             cfg.cls += ' active';
21025         }
21026         
21027         if(this.tabId){
21028             cfg.tabId = this.tabId;
21029         }
21030         
21031         
21032         
21033         return cfg;
21034     },
21035     
21036     initEvents:  function()
21037     {
21038         var p = this.parent();
21039         
21040         this.navId = this.navId || p.navId;
21041         
21042         if (typeof(this.navId) != 'undefined') {
21043             // not really needed.. but just in case.. parent should be a NavGroup.
21044             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21045             
21046             tg.register(this);
21047             
21048             var i = tg.tabs.length - 1;
21049             
21050             if(this.active && tg.bullets > 0 && i < tg.bullets){
21051                 tg.setActiveBullet(i);
21052             }
21053         }
21054         
21055         this.el.on('click', this.onClick, this);
21056         
21057         if(Roo.isTouch && this.touchSlide){
21058             this.el.on("touchstart", this.onTouchStart, this);
21059             this.el.on("touchmove", this.onTouchMove, this);
21060             this.el.on("touchend", this.onTouchEnd, this);
21061         }
21062         
21063     },
21064     
21065     onRender : function(ct, position)
21066     {
21067         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21068     },
21069     
21070     setActive : function(state)
21071     {
21072         Roo.log("panel - set active " + this.tabId + "=" + state);
21073         
21074         this.active = state;
21075         if (!state) {
21076             this.el.removeClass('active');
21077             
21078         } else  if (!this.el.hasClass('active')) {
21079             this.el.addClass('active');
21080         }
21081         
21082         this.fireEvent('changed', this, state);
21083     },
21084     
21085     onClick : function(e)
21086     {
21087         e.preventDefault();
21088         
21089         if(!this.href.length){
21090             return;
21091         }
21092         
21093         window.location.href = this.href;
21094     },
21095     
21096     startX : 0,
21097     startY : 0,
21098     endX : 0,
21099     endY : 0,
21100     swiping : false,
21101     
21102     onTouchStart : function(e)
21103     {
21104         this.swiping = false;
21105         
21106         this.startX = e.browserEvent.touches[0].clientX;
21107         this.startY = e.browserEvent.touches[0].clientY;
21108     },
21109     
21110     onTouchMove : function(e)
21111     {
21112         this.swiping = true;
21113         
21114         this.endX = e.browserEvent.touches[0].clientX;
21115         this.endY = e.browserEvent.touches[0].clientY;
21116     },
21117     
21118     onTouchEnd : function(e)
21119     {
21120         if(!this.swiping){
21121             this.onClick(e);
21122             return;
21123         }
21124         
21125         var tabGroup = this.parent();
21126         
21127         if(this.endX > this.startX){ // swiping right
21128             tabGroup.showPanelPrev();
21129             return;
21130         }
21131         
21132         if(this.startX > this.endX){ // swiping left
21133             tabGroup.showPanelNext();
21134             return;
21135         }
21136     }
21137     
21138     
21139 });
21140  
21141
21142  
21143
21144  /*
21145  * - LGPL
21146  *
21147  * DateField
21148  * 
21149  */
21150
21151 /**
21152  * @class Roo.bootstrap.DateField
21153  * @extends Roo.bootstrap.Input
21154  * Bootstrap DateField class
21155  * @cfg {Number} weekStart default 0
21156  * @cfg {String} viewMode default empty, (months|years)
21157  * @cfg {String} minViewMode default empty, (months|years)
21158  * @cfg {Number} startDate default -Infinity
21159  * @cfg {Number} endDate default Infinity
21160  * @cfg {Boolean} todayHighlight default false
21161  * @cfg {Boolean} todayBtn default false
21162  * @cfg {Boolean} calendarWeeks default false
21163  * @cfg {Object} daysOfWeekDisabled default empty
21164  * @cfg {Boolean} singleMode default false (true | false)
21165  * 
21166  * @cfg {Boolean} keyboardNavigation default true
21167  * @cfg {String} language default en
21168  * 
21169  * @constructor
21170  * Create a new DateField
21171  * @param {Object} config The config object
21172  */
21173
21174 Roo.bootstrap.DateField = function(config){
21175     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21176      this.addEvents({
21177             /**
21178              * @event show
21179              * Fires when this field show.
21180              * @param {Roo.bootstrap.DateField} this
21181              * @param {Mixed} date The date value
21182              */
21183             show : true,
21184             /**
21185              * @event show
21186              * Fires when this field hide.
21187              * @param {Roo.bootstrap.DateField} this
21188              * @param {Mixed} date The date value
21189              */
21190             hide : true,
21191             /**
21192              * @event select
21193              * Fires when select a date.
21194              * @param {Roo.bootstrap.DateField} this
21195              * @param {Mixed} date The date value
21196              */
21197             select : true,
21198             /**
21199              * @event beforeselect
21200              * Fires when before select a date.
21201              * @param {Roo.bootstrap.DateField} this
21202              * @param {Mixed} date The date value
21203              */
21204             beforeselect : true
21205         });
21206 };
21207
21208 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21209     
21210     /**
21211      * @cfg {String} format
21212      * The default date format string which can be overriden for localization support.  The format must be
21213      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21214      */
21215     format : "m/d/y",
21216     /**
21217      * @cfg {String} altFormats
21218      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21219      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21220      */
21221     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21222     
21223     weekStart : 0,
21224     
21225     viewMode : '',
21226     
21227     minViewMode : '',
21228     
21229     todayHighlight : false,
21230     
21231     todayBtn: false,
21232     
21233     language: 'en',
21234     
21235     keyboardNavigation: true,
21236     
21237     calendarWeeks: false,
21238     
21239     startDate: -Infinity,
21240     
21241     endDate: Infinity,
21242     
21243     daysOfWeekDisabled: [],
21244     
21245     _events: [],
21246     
21247     singleMode : false,
21248     
21249     UTCDate: function()
21250     {
21251         return new Date(Date.UTC.apply(Date, arguments));
21252     },
21253     
21254     UTCToday: function()
21255     {
21256         var today = new Date();
21257         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21258     },
21259     
21260     getDate: function() {
21261             var d = this.getUTCDate();
21262             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21263     },
21264     
21265     getUTCDate: function() {
21266             return this.date;
21267     },
21268     
21269     setDate: function(d) {
21270             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21271     },
21272     
21273     setUTCDate: function(d) {
21274             this.date = d;
21275             this.setValue(this.formatDate(this.date));
21276     },
21277         
21278     onRender: function(ct, position)
21279     {
21280         
21281         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21282         
21283         this.language = this.language || 'en';
21284         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21285         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21286         
21287         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21288         this.format = this.format || 'm/d/y';
21289         this.isInline = false;
21290         this.isInput = true;
21291         this.component = this.el.select('.add-on', true).first() || false;
21292         this.component = (this.component && this.component.length === 0) ? false : this.component;
21293         this.hasInput = this.component && this.inputEl().length;
21294         
21295         if (typeof(this.minViewMode === 'string')) {
21296             switch (this.minViewMode) {
21297                 case 'months':
21298                     this.minViewMode = 1;
21299                     break;
21300                 case 'years':
21301                     this.minViewMode = 2;
21302                     break;
21303                 default:
21304                     this.minViewMode = 0;
21305                     break;
21306             }
21307         }
21308         
21309         if (typeof(this.viewMode === 'string')) {
21310             switch (this.viewMode) {
21311                 case 'months':
21312                     this.viewMode = 1;
21313                     break;
21314                 case 'years':
21315                     this.viewMode = 2;
21316                     break;
21317                 default:
21318                     this.viewMode = 0;
21319                     break;
21320             }
21321         }
21322                 
21323         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21324         
21325 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21326         
21327         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21328         
21329         this.picker().on('mousedown', this.onMousedown, this);
21330         this.picker().on('click', this.onClick, this);
21331         
21332         this.picker().addClass('datepicker-dropdown');
21333         
21334         this.startViewMode = this.viewMode;
21335         
21336         if(this.singleMode){
21337             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21338                 v.setVisibilityMode(Roo.Element.DISPLAY);
21339                 v.hide();
21340             });
21341             
21342             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21343                 v.setStyle('width', '189px');
21344             });
21345         }
21346         
21347         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21348             if(!this.calendarWeeks){
21349                 v.remove();
21350                 return;
21351             }
21352             
21353             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21354             v.attr('colspan', function(i, val){
21355                 return parseInt(val) + 1;
21356             });
21357         });
21358                         
21359         
21360         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21361         
21362         this.setStartDate(this.startDate);
21363         this.setEndDate(this.endDate);
21364         
21365         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21366         
21367         this.fillDow();
21368         this.fillMonths();
21369         this.update();
21370         this.showMode();
21371         
21372         if(this.isInline) {
21373             this.showPopup();
21374         }
21375     },
21376     
21377     picker : function()
21378     {
21379         return this.pickerEl;
21380 //        return this.el.select('.datepicker', true).first();
21381     },
21382     
21383     fillDow: function()
21384     {
21385         var dowCnt = this.weekStart;
21386         
21387         var dow = {
21388             tag: 'tr',
21389             cn: [
21390                 
21391             ]
21392         };
21393         
21394         if(this.calendarWeeks){
21395             dow.cn.push({
21396                 tag: 'th',
21397                 cls: 'cw',
21398                 html: '&nbsp;'
21399             })
21400         }
21401         
21402         while (dowCnt < this.weekStart + 7) {
21403             dow.cn.push({
21404                 tag: 'th',
21405                 cls: 'dow',
21406                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21407             });
21408         }
21409         
21410         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21411     },
21412     
21413     fillMonths: function()
21414     {    
21415         var i = 0;
21416         var months = this.picker().select('>.datepicker-months td', true).first();
21417         
21418         months.dom.innerHTML = '';
21419         
21420         while (i < 12) {
21421             var month = {
21422                 tag: 'span',
21423                 cls: 'month',
21424                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21425             };
21426             
21427             months.createChild(month);
21428         }
21429         
21430     },
21431     
21432     update: function()
21433     {
21434         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;
21435         
21436         if (this.date < this.startDate) {
21437             this.viewDate = new Date(this.startDate);
21438         } else if (this.date > this.endDate) {
21439             this.viewDate = new Date(this.endDate);
21440         } else {
21441             this.viewDate = new Date(this.date);
21442         }
21443         
21444         this.fill();
21445     },
21446     
21447     fill: function() 
21448     {
21449         var d = new Date(this.viewDate),
21450                 year = d.getUTCFullYear(),
21451                 month = d.getUTCMonth(),
21452                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21453                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21454                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21455                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21456                 currentDate = this.date && this.date.valueOf(),
21457                 today = this.UTCToday();
21458         
21459         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21460         
21461 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21462         
21463 //        this.picker.select('>tfoot th.today').
21464 //                                              .text(dates[this.language].today)
21465 //                                              .toggle(this.todayBtn !== false);
21466     
21467         this.updateNavArrows();
21468         this.fillMonths();
21469                                                 
21470         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21471         
21472         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21473          
21474         prevMonth.setUTCDate(day);
21475         
21476         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21477         
21478         var nextMonth = new Date(prevMonth);
21479         
21480         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21481         
21482         nextMonth = nextMonth.valueOf();
21483         
21484         var fillMonths = false;
21485         
21486         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21487         
21488         while(prevMonth.valueOf() <= nextMonth) {
21489             var clsName = '';
21490             
21491             if (prevMonth.getUTCDay() === this.weekStart) {
21492                 if(fillMonths){
21493                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21494                 }
21495                     
21496                 fillMonths = {
21497                     tag: 'tr',
21498                     cn: []
21499                 };
21500                 
21501                 if(this.calendarWeeks){
21502                     // ISO 8601: First week contains first thursday.
21503                     // ISO also states week starts on Monday, but we can be more abstract here.
21504                     var
21505                     // Start of current week: based on weekstart/current date
21506                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21507                     // Thursday of this week
21508                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21509                     // First Thursday of year, year from thursday
21510                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21511                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21512                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21513                     
21514                     fillMonths.cn.push({
21515                         tag: 'td',
21516                         cls: 'cw',
21517                         html: calWeek
21518                     });
21519                 }
21520             }
21521             
21522             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21523                 clsName += ' old';
21524             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21525                 clsName += ' new';
21526             }
21527             if (this.todayHighlight &&
21528                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21529                 prevMonth.getUTCMonth() == today.getMonth() &&
21530                 prevMonth.getUTCDate() == today.getDate()) {
21531                 clsName += ' today';
21532             }
21533             
21534             if (currentDate && prevMonth.valueOf() === currentDate) {
21535                 clsName += ' active';
21536             }
21537             
21538             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21539                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21540                     clsName += ' disabled';
21541             }
21542             
21543             fillMonths.cn.push({
21544                 tag: 'td',
21545                 cls: 'day ' + clsName,
21546                 html: prevMonth.getDate()
21547             });
21548             
21549             prevMonth.setDate(prevMonth.getDate()+1);
21550         }
21551           
21552         var currentYear = this.date && this.date.getUTCFullYear();
21553         var currentMonth = this.date && this.date.getUTCMonth();
21554         
21555         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21556         
21557         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21558             v.removeClass('active');
21559             
21560             if(currentYear === year && k === currentMonth){
21561                 v.addClass('active');
21562             }
21563             
21564             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21565                 v.addClass('disabled');
21566             }
21567             
21568         });
21569         
21570         
21571         year = parseInt(year/10, 10) * 10;
21572         
21573         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21574         
21575         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21576         
21577         year -= 1;
21578         for (var i = -1; i < 11; i++) {
21579             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21580                 tag: 'span',
21581                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21582                 html: year
21583             });
21584             
21585             year += 1;
21586         }
21587     },
21588     
21589     showMode: function(dir) 
21590     {
21591         if (dir) {
21592             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21593         }
21594         
21595         Roo.each(this.picker().select('>div',true).elements, function(v){
21596             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21597             v.hide();
21598         });
21599         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21600     },
21601     
21602     place: function()
21603     {
21604         if(this.isInline) {
21605             return;
21606         }
21607         
21608         this.picker().removeClass(['bottom', 'top']);
21609         
21610         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21611             /*
21612              * place to the top of element!
21613              *
21614              */
21615             
21616             this.picker().addClass('top');
21617             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21618             
21619             return;
21620         }
21621         
21622         this.picker().addClass('bottom');
21623         
21624         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21625     },
21626     
21627     parseDate : function(value)
21628     {
21629         if(!value || value instanceof Date){
21630             return value;
21631         }
21632         var v = Date.parseDate(value, this.format);
21633         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21634             v = Date.parseDate(value, 'Y-m-d');
21635         }
21636         if(!v && this.altFormats){
21637             if(!this.altFormatsArray){
21638                 this.altFormatsArray = this.altFormats.split("|");
21639             }
21640             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21641                 v = Date.parseDate(value, this.altFormatsArray[i]);
21642             }
21643         }
21644         return v;
21645     },
21646     
21647     formatDate : function(date, fmt)
21648     {   
21649         return (!date || !(date instanceof Date)) ?
21650         date : date.dateFormat(fmt || this.format);
21651     },
21652     
21653     onFocus : function()
21654     {
21655         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21656         this.showPopup();
21657     },
21658     
21659     onBlur : function()
21660     {
21661         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21662         
21663         var d = this.inputEl().getValue();
21664         
21665         this.setValue(d);
21666                 
21667         this.hidePopup();
21668     },
21669     
21670     showPopup : function()
21671     {
21672         this.picker().show();
21673         this.update();
21674         this.place();
21675         
21676         this.fireEvent('showpopup', this, this.date);
21677     },
21678     
21679     hidePopup : function()
21680     {
21681         if(this.isInline) {
21682             return;
21683         }
21684         this.picker().hide();
21685         this.viewMode = this.startViewMode;
21686         this.showMode();
21687         
21688         this.fireEvent('hidepopup', this, this.date);
21689         
21690     },
21691     
21692     onMousedown: function(e)
21693     {
21694         e.stopPropagation();
21695         e.preventDefault();
21696     },
21697     
21698     keyup: function(e)
21699     {
21700         Roo.bootstrap.DateField.superclass.keyup.call(this);
21701         this.update();
21702     },
21703
21704     setValue: function(v)
21705     {
21706         if(this.fireEvent('beforeselect', this, v) !== false){
21707             var d = new Date(this.parseDate(v) ).clearTime();
21708         
21709             if(isNaN(d.getTime())){
21710                 this.date = this.viewDate = '';
21711                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21712                 return;
21713             }
21714
21715             v = this.formatDate(d);
21716
21717             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21718
21719             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21720
21721             this.update();
21722
21723             this.fireEvent('select', this, this.date);
21724         }
21725     },
21726     
21727     getValue: function()
21728     {
21729         return this.formatDate(this.date);
21730     },
21731     
21732     fireKey: function(e)
21733     {
21734         if (!this.picker().isVisible()){
21735             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21736                 this.showPopup();
21737             }
21738             return;
21739         }
21740         
21741         var dateChanged = false,
21742         dir, day, month,
21743         newDate, newViewDate;
21744         
21745         switch(e.keyCode){
21746             case 27: // escape
21747                 this.hidePopup();
21748                 e.preventDefault();
21749                 break;
21750             case 37: // left
21751             case 39: // right
21752                 if (!this.keyboardNavigation) {
21753                     break;
21754                 }
21755                 dir = e.keyCode == 37 ? -1 : 1;
21756                 
21757                 if (e.ctrlKey){
21758                     newDate = this.moveYear(this.date, dir);
21759                     newViewDate = this.moveYear(this.viewDate, dir);
21760                 } else if (e.shiftKey){
21761                     newDate = this.moveMonth(this.date, dir);
21762                     newViewDate = this.moveMonth(this.viewDate, dir);
21763                 } else {
21764                     newDate = new Date(this.date);
21765                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21766                     newViewDate = new Date(this.viewDate);
21767                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21768                 }
21769                 if (this.dateWithinRange(newDate)){
21770                     this.date = newDate;
21771                     this.viewDate = newViewDate;
21772                     this.setValue(this.formatDate(this.date));
21773 //                    this.update();
21774                     e.preventDefault();
21775                     dateChanged = true;
21776                 }
21777                 break;
21778             case 38: // up
21779             case 40: // down
21780                 if (!this.keyboardNavigation) {
21781                     break;
21782                 }
21783                 dir = e.keyCode == 38 ? -1 : 1;
21784                 if (e.ctrlKey){
21785                     newDate = this.moveYear(this.date, dir);
21786                     newViewDate = this.moveYear(this.viewDate, dir);
21787                 } else if (e.shiftKey){
21788                     newDate = this.moveMonth(this.date, dir);
21789                     newViewDate = this.moveMonth(this.viewDate, dir);
21790                 } else {
21791                     newDate = new Date(this.date);
21792                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21793                     newViewDate = new Date(this.viewDate);
21794                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21795                 }
21796                 if (this.dateWithinRange(newDate)){
21797                     this.date = newDate;
21798                     this.viewDate = newViewDate;
21799                     this.setValue(this.formatDate(this.date));
21800 //                    this.update();
21801                     e.preventDefault();
21802                     dateChanged = true;
21803                 }
21804                 break;
21805             case 13: // enter
21806                 this.setValue(this.formatDate(this.date));
21807                 this.hidePopup();
21808                 e.preventDefault();
21809                 break;
21810             case 9: // tab
21811                 this.setValue(this.formatDate(this.date));
21812                 this.hidePopup();
21813                 break;
21814             case 16: // shift
21815             case 17: // ctrl
21816             case 18: // alt
21817                 break;
21818             default :
21819                 this.hidePopup();
21820                 
21821         }
21822     },
21823     
21824     
21825     onClick: function(e) 
21826     {
21827         e.stopPropagation();
21828         e.preventDefault();
21829         
21830         var target = e.getTarget();
21831         
21832         if(target.nodeName.toLowerCase() === 'i'){
21833             target = Roo.get(target).dom.parentNode;
21834         }
21835         
21836         var nodeName = target.nodeName;
21837         var className = target.className;
21838         var html = target.innerHTML;
21839         //Roo.log(nodeName);
21840         
21841         switch(nodeName.toLowerCase()) {
21842             case 'th':
21843                 switch(className) {
21844                     case 'switch':
21845                         this.showMode(1);
21846                         break;
21847                     case 'prev':
21848                     case 'next':
21849                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21850                         switch(this.viewMode){
21851                                 case 0:
21852                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21853                                         break;
21854                                 case 1:
21855                                 case 2:
21856                                         this.viewDate = this.moveYear(this.viewDate, dir);
21857                                         break;
21858                         }
21859                         this.fill();
21860                         break;
21861                     case 'today':
21862                         var date = new Date();
21863                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21864 //                        this.fill()
21865                         this.setValue(this.formatDate(this.date));
21866                         
21867                         this.hidePopup();
21868                         break;
21869                 }
21870                 break;
21871             case 'span':
21872                 if (className.indexOf('disabled') < 0) {
21873                     this.viewDate.setUTCDate(1);
21874                     if (className.indexOf('month') > -1) {
21875                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21876                     } else {
21877                         var year = parseInt(html, 10) || 0;
21878                         this.viewDate.setUTCFullYear(year);
21879                         
21880                     }
21881                     
21882                     if(this.singleMode){
21883                         this.setValue(this.formatDate(this.viewDate));
21884                         this.hidePopup();
21885                         return;
21886                     }
21887                     
21888                     this.showMode(-1);
21889                     this.fill();
21890                 }
21891                 break;
21892                 
21893             case 'td':
21894                 //Roo.log(className);
21895                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21896                     var day = parseInt(html, 10) || 1;
21897                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21898                         month = (this.viewDate || new Date()).getUTCMonth();
21899
21900                     if (className.indexOf('old') > -1) {
21901                         if(month === 0 ){
21902                             month = 11;
21903                             year -= 1;
21904                         }else{
21905                             month -= 1;
21906                         }
21907                     } else if (className.indexOf('new') > -1) {
21908                         if (month == 11) {
21909                             month = 0;
21910                             year += 1;
21911                         } else {
21912                             month += 1;
21913                         }
21914                     }
21915                     //Roo.log([year,month,day]);
21916                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21917                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21918 //                    this.fill();
21919                     //Roo.log(this.formatDate(this.date));
21920                     this.setValue(this.formatDate(this.date));
21921                     this.hidePopup();
21922                 }
21923                 break;
21924         }
21925     },
21926     
21927     setStartDate: function(startDate)
21928     {
21929         this.startDate = startDate || -Infinity;
21930         if (this.startDate !== -Infinity) {
21931             this.startDate = this.parseDate(this.startDate);
21932         }
21933         this.update();
21934         this.updateNavArrows();
21935     },
21936
21937     setEndDate: function(endDate)
21938     {
21939         this.endDate = endDate || Infinity;
21940         if (this.endDate !== Infinity) {
21941             this.endDate = this.parseDate(this.endDate);
21942         }
21943         this.update();
21944         this.updateNavArrows();
21945     },
21946     
21947     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21948     {
21949         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21950         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21951             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21952         }
21953         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21954             return parseInt(d, 10);
21955         });
21956         this.update();
21957         this.updateNavArrows();
21958     },
21959     
21960     updateNavArrows: function() 
21961     {
21962         if(this.singleMode){
21963             return;
21964         }
21965         
21966         var d = new Date(this.viewDate),
21967         year = d.getUTCFullYear(),
21968         month = d.getUTCMonth();
21969         
21970         Roo.each(this.picker().select('.prev', true).elements, function(v){
21971             v.show();
21972             switch (this.viewMode) {
21973                 case 0:
21974
21975                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21976                         v.hide();
21977                     }
21978                     break;
21979                 case 1:
21980                 case 2:
21981                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21982                         v.hide();
21983                     }
21984                     break;
21985             }
21986         });
21987         
21988         Roo.each(this.picker().select('.next', true).elements, function(v){
21989             v.show();
21990             switch (this.viewMode) {
21991                 case 0:
21992
21993                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21994                         v.hide();
21995                     }
21996                     break;
21997                 case 1:
21998                 case 2:
21999                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22000                         v.hide();
22001                     }
22002                     break;
22003             }
22004         })
22005     },
22006     
22007     moveMonth: function(date, dir)
22008     {
22009         if (!dir) {
22010             return date;
22011         }
22012         var new_date = new Date(date.valueOf()),
22013         day = new_date.getUTCDate(),
22014         month = new_date.getUTCMonth(),
22015         mag = Math.abs(dir),
22016         new_month, test;
22017         dir = dir > 0 ? 1 : -1;
22018         if (mag == 1){
22019             test = dir == -1
22020             // If going back one month, make sure month is not current month
22021             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22022             ? function(){
22023                 return new_date.getUTCMonth() == month;
22024             }
22025             // If going forward one month, make sure month is as expected
22026             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22027             : function(){
22028                 return new_date.getUTCMonth() != new_month;
22029             };
22030             new_month = month + dir;
22031             new_date.setUTCMonth(new_month);
22032             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22033             if (new_month < 0 || new_month > 11) {
22034                 new_month = (new_month + 12) % 12;
22035             }
22036         } else {
22037             // For magnitudes >1, move one month at a time...
22038             for (var i=0; i<mag; i++) {
22039                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22040                 new_date = this.moveMonth(new_date, dir);
22041             }
22042             // ...then reset the day, keeping it in the new month
22043             new_month = new_date.getUTCMonth();
22044             new_date.setUTCDate(day);
22045             test = function(){
22046                 return new_month != new_date.getUTCMonth();
22047             };
22048         }
22049         // Common date-resetting loop -- if date is beyond end of month, make it
22050         // end of month
22051         while (test()){
22052             new_date.setUTCDate(--day);
22053             new_date.setUTCMonth(new_month);
22054         }
22055         return new_date;
22056     },
22057
22058     moveYear: function(date, dir)
22059     {
22060         return this.moveMonth(date, dir*12);
22061     },
22062
22063     dateWithinRange: function(date)
22064     {
22065         return date >= this.startDate && date <= this.endDate;
22066     },
22067
22068     
22069     remove: function() 
22070     {
22071         this.picker().remove();
22072     },
22073     
22074     validateValue : function(value)
22075     {
22076         if(this.getVisibilityEl().hasClass('hidden')){
22077             return true;
22078         }
22079         
22080         if(value.length < 1)  {
22081             if(this.allowBlank){
22082                 return true;
22083             }
22084             return false;
22085         }
22086         
22087         if(value.length < this.minLength){
22088             return false;
22089         }
22090         if(value.length > this.maxLength){
22091             return false;
22092         }
22093         if(this.vtype){
22094             var vt = Roo.form.VTypes;
22095             if(!vt[this.vtype](value, this)){
22096                 return false;
22097             }
22098         }
22099         if(typeof this.validator == "function"){
22100             var msg = this.validator(value);
22101             if(msg !== true){
22102                 return false;
22103             }
22104         }
22105         
22106         if(this.regex && !this.regex.test(value)){
22107             return false;
22108         }
22109         
22110         if(typeof(this.parseDate(value)) == 'undefined'){
22111             return false;
22112         }
22113         
22114         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22115             return false;
22116         }      
22117         
22118         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22119             return false;
22120         } 
22121         
22122         
22123         return true;
22124     },
22125     
22126     reset : function()
22127     {
22128         this.date = this.viewDate = '';
22129         
22130         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22131     }
22132    
22133 });
22134
22135 Roo.apply(Roo.bootstrap.DateField,  {
22136     
22137     head : {
22138         tag: 'thead',
22139         cn: [
22140         {
22141             tag: 'tr',
22142             cn: [
22143             {
22144                 tag: 'th',
22145                 cls: 'prev',
22146                 html: '<i class="fa fa-arrow-left"/>'
22147             },
22148             {
22149                 tag: 'th',
22150                 cls: 'switch',
22151                 colspan: '5'
22152             },
22153             {
22154                 tag: 'th',
22155                 cls: 'next',
22156                 html: '<i class="fa fa-arrow-right"/>'
22157             }
22158
22159             ]
22160         }
22161         ]
22162     },
22163     
22164     content : {
22165         tag: 'tbody',
22166         cn: [
22167         {
22168             tag: 'tr',
22169             cn: [
22170             {
22171                 tag: 'td',
22172                 colspan: '7'
22173             }
22174             ]
22175         }
22176         ]
22177     },
22178     
22179     footer : {
22180         tag: 'tfoot',
22181         cn: [
22182         {
22183             tag: 'tr',
22184             cn: [
22185             {
22186                 tag: 'th',
22187                 colspan: '7',
22188                 cls: 'today'
22189             }
22190                     
22191             ]
22192         }
22193         ]
22194     },
22195     
22196     dates:{
22197         en: {
22198             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22199             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22200             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22201             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22202             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22203             today: "Today"
22204         }
22205     },
22206     
22207     modes: [
22208     {
22209         clsName: 'days',
22210         navFnc: 'Month',
22211         navStep: 1
22212     },
22213     {
22214         clsName: 'months',
22215         navFnc: 'FullYear',
22216         navStep: 1
22217     },
22218     {
22219         clsName: 'years',
22220         navFnc: 'FullYear',
22221         navStep: 10
22222     }]
22223 });
22224
22225 Roo.apply(Roo.bootstrap.DateField,  {
22226   
22227     template : {
22228         tag: 'div',
22229         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22230         cn: [
22231         {
22232             tag: 'div',
22233             cls: 'datepicker-days',
22234             cn: [
22235             {
22236                 tag: 'table',
22237                 cls: 'table-condensed',
22238                 cn:[
22239                 Roo.bootstrap.DateField.head,
22240                 {
22241                     tag: 'tbody'
22242                 },
22243                 Roo.bootstrap.DateField.footer
22244                 ]
22245             }
22246             ]
22247         },
22248         {
22249             tag: 'div',
22250             cls: 'datepicker-months',
22251             cn: [
22252             {
22253                 tag: 'table',
22254                 cls: 'table-condensed',
22255                 cn:[
22256                 Roo.bootstrap.DateField.head,
22257                 Roo.bootstrap.DateField.content,
22258                 Roo.bootstrap.DateField.footer
22259                 ]
22260             }
22261             ]
22262         },
22263         {
22264             tag: 'div',
22265             cls: 'datepicker-years',
22266             cn: [
22267             {
22268                 tag: 'table',
22269                 cls: 'table-condensed',
22270                 cn:[
22271                 Roo.bootstrap.DateField.head,
22272                 Roo.bootstrap.DateField.content,
22273                 Roo.bootstrap.DateField.footer
22274                 ]
22275             }
22276             ]
22277         }
22278         ]
22279     }
22280 });
22281
22282  
22283
22284  /*
22285  * - LGPL
22286  *
22287  * TimeField
22288  * 
22289  */
22290
22291 /**
22292  * @class Roo.bootstrap.TimeField
22293  * @extends Roo.bootstrap.Input
22294  * Bootstrap DateField class
22295  * 
22296  * 
22297  * @constructor
22298  * Create a new TimeField
22299  * @param {Object} config The config object
22300  */
22301
22302 Roo.bootstrap.TimeField = function(config){
22303     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22304     this.addEvents({
22305             /**
22306              * @event show
22307              * Fires when this field show.
22308              * @param {Roo.bootstrap.DateField} thisthis
22309              * @param {Mixed} date The date value
22310              */
22311             show : true,
22312             /**
22313              * @event show
22314              * Fires when this field hide.
22315              * @param {Roo.bootstrap.DateField} this
22316              * @param {Mixed} date The date value
22317              */
22318             hide : true,
22319             /**
22320              * @event select
22321              * Fires when select a date.
22322              * @param {Roo.bootstrap.DateField} this
22323              * @param {Mixed} date The date value
22324              */
22325             select : true
22326         });
22327 };
22328
22329 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22330     
22331     /**
22332      * @cfg {String} format
22333      * The default time format string which can be overriden for localization support.  The format must be
22334      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22335      */
22336     format : "H:i",
22337
22338     getAutoCreate : function()
22339     {
22340         this.after = '<i class="fa far fa-clock"></i>';
22341         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22342         
22343          
22344     },
22345     onRender: function(ct, position)
22346     {
22347         
22348         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22349                 
22350         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22351         
22352         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22353         
22354         this.pop = this.picker().select('>.datepicker-time',true).first();
22355         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22356         
22357         this.picker().on('mousedown', this.onMousedown, this);
22358         this.picker().on('click', this.onClick, this);
22359         
22360         this.picker().addClass('datepicker-dropdown');
22361     
22362         this.fillTime();
22363         this.update();
22364             
22365         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22366         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22367         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22368         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22369         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22370         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22371
22372     },
22373     
22374     fireKey: function(e){
22375         if (!this.picker().isVisible()){
22376             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22377                 this.show();
22378             }
22379             return;
22380         }
22381
22382         e.preventDefault();
22383         
22384         switch(e.keyCode){
22385             case 27: // escape
22386                 this.hide();
22387                 break;
22388             case 37: // left
22389             case 39: // right
22390                 this.onTogglePeriod();
22391                 break;
22392             case 38: // up
22393                 this.onIncrementMinutes();
22394                 break;
22395             case 40: // down
22396                 this.onDecrementMinutes();
22397                 break;
22398             case 13: // enter
22399             case 9: // tab
22400                 this.setTime();
22401                 break;
22402         }
22403     },
22404     
22405     onClick: function(e) {
22406         e.stopPropagation();
22407         e.preventDefault();
22408     },
22409     
22410     picker : function()
22411     {
22412         return this.pickerEl;
22413     },
22414     
22415     fillTime: function()
22416     {    
22417         var time = this.pop.select('tbody', true).first();
22418         
22419         time.dom.innerHTML = '';
22420         
22421         time.createChild({
22422             tag: 'tr',
22423             cn: [
22424                 {
22425                     tag: 'td',
22426                     cn: [
22427                         {
22428                             tag: 'a',
22429                             href: '#',
22430                             cls: 'btn',
22431                             cn: [
22432                                 {
22433                                     tag: 'i',
22434                                     cls: 'hours-up fa fas fa-chevron-up'
22435                                 }
22436                             ]
22437                         } 
22438                     ]
22439                 },
22440                 {
22441                     tag: 'td',
22442                     cls: 'separator'
22443                 },
22444                 {
22445                     tag: 'td',
22446                     cn: [
22447                         {
22448                             tag: 'a',
22449                             href: '#',
22450                             cls: 'btn',
22451                             cn: [
22452                                 {
22453                                     tag: 'i',
22454                                     cls: 'minutes-up fa fas fa-chevron-up'
22455                                 }
22456                             ]
22457                         }
22458                     ]
22459                 },
22460                 {
22461                     tag: 'td',
22462                     cls: 'separator'
22463                 }
22464             ]
22465         });
22466         
22467         time.createChild({
22468             tag: 'tr',
22469             cn: [
22470                 {
22471                     tag: 'td',
22472                     cn: [
22473                         {
22474                             tag: 'span',
22475                             cls: 'timepicker-hour',
22476                             html: '00'
22477                         }  
22478                     ]
22479                 },
22480                 {
22481                     tag: 'td',
22482                     cls: 'separator',
22483                     html: ':'
22484                 },
22485                 {
22486                     tag: 'td',
22487                     cn: [
22488                         {
22489                             tag: 'span',
22490                             cls: 'timepicker-minute',
22491                             html: '00'
22492                         }  
22493                     ]
22494                 },
22495                 {
22496                     tag: 'td',
22497                     cls: 'separator'
22498                 },
22499                 {
22500                     tag: 'td',
22501                     cn: [
22502                         {
22503                             tag: 'button',
22504                             type: 'button',
22505                             cls: 'btn btn-primary period',
22506                             html: 'AM'
22507                             
22508                         }
22509                     ]
22510                 }
22511             ]
22512         });
22513         
22514         time.createChild({
22515             tag: 'tr',
22516             cn: [
22517                 {
22518                     tag: 'td',
22519                     cn: [
22520                         {
22521                             tag: 'a',
22522                             href: '#',
22523                             cls: 'btn',
22524                             cn: [
22525                                 {
22526                                     tag: 'span',
22527                                     cls: 'hours-down fa fas fa-chevron-down'
22528                                 }
22529                             ]
22530                         }
22531                     ]
22532                 },
22533                 {
22534                     tag: 'td',
22535                     cls: 'separator'
22536                 },
22537                 {
22538                     tag: 'td',
22539                     cn: [
22540                         {
22541                             tag: 'a',
22542                             href: '#',
22543                             cls: 'btn',
22544                             cn: [
22545                                 {
22546                                     tag: 'span',
22547                                     cls: 'minutes-down fa fas fa-chevron-down'
22548                                 }
22549                             ]
22550                         }
22551                     ]
22552                 },
22553                 {
22554                     tag: 'td',
22555                     cls: 'separator'
22556                 }
22557             ]
22558         });
22559         
22560     },
22561     
22562     update: function()
22563     {
22564         
22565         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22566         
22567         this.fill();
22568     },
22569     
22570     fill: function() 
22571     {
22572         var hours = this.time.getHours();
22573         var minutes = this.time.getMinutes();
22574         var period = 'AM';
22575         
22576         if(hours > 11){
22577             period = 'PM';
22578         }
22579         
22580         if(hours == 0){
22581             hours = 12;
22582         }
22583         
22584         
22585         if(hours > 12){
22586             hours = hours - 12;
22587         }
22588         
22589         if(hours < 10){
22590             hours = '0' + hours;
22591         }
22592         
22593         if(minutes < 10){
22594             minutes = '0' + minutes;
22595         }
22596         
22597         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22598         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22599         this.pop.select('button', true).first().dom.innerHTML = period;
22600         
22601     },
22602     
22603     place: function()
22604     {   
22605         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22606         
22607         var cls = ['bottom'];
22608         
22609         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22610             cls.pop();
22611             cls.push('top');
22612         }
22613         
22614         cls.push('right');
22615         
22616         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22617             cls.pop();
22618             cls.push('left');
22619         }
22620         //this.picker().setXY(20000,20000);
22621         this.picker().addClass(cls.join('-'));
22622         
22623         var _this = this;
22624         
22625         Roo.each(cls, function(c){
22626             if(c == 'bottom'){
22627                 (function() {
22628                  //  
22629                 }).defer(200);
22630                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22631                 //_this.picker().setTop(_this.inputEl().getHeight());
22632                 return;
22633             }
22634             if(c == 'top'){
22635                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22636                 
22637                 //_this.picker().setTop(0 - _this.picker().getHeight());
22638                 return;
22639             }
22640             /*
22641             if(c == 'left'){
22642                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22643                 return;
22644             }
22645             if(c == 'right'){
22646                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22647                 return;
22648             }
22649             */
22650         });
22651         
22652     },
22653   
22654     onFocus : function()
22655     {
22656         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22657         this.show();
22658     },
22659     
22660     onBlur : function()
22661     {
22662         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22663         this.hide();
22664     },
22665     
22666     show : function()
22667     {
22668         this.picker().show();
22669         this.pop.show();
22670         this.update();
22671         this.place();
22672         
22673         this.fireEvent('show', this, this.date);
22674     },
22675     
22676     hide : function()
22677     {
22678         this.picker().hide();
22679         this.pop.hide();
22680         
22681         this.fireEvent('hide', this, this.date);
22682     },
22683     
22684     setTime : function()
22685     {
22686         this.hide();
22687         this.setValue(this.time.format(this.format));
22688         
22689         this.fireEvent('select', this, this.date);
22690         
22691         
22692     },
22693     
22694     onMousedown: function(e){
22695         e.stopPropagation();
22696         e.preventDefault();
22697     },
22698     
22699     onIncrementHours: function()
22700     {
22701         Roo.log('onIncrementHours');
22702         this.time = this.time.add(Date.HOUR, 1);
22703         this.update();
22704         
22705     },
22706     
22707     onDecrementHours: function()
22708     {
22709         Roo.log('onDecrementHours');
22710         this.time = this.time.add(Date.HOUR, -1);
22711         this.update();
22712     },
22713     
22714     onIncrementMinutes: function()
22715     {
22716         Roo.log('onIncrementMinutes');
22717         this.time = this.time.add(Date.MINUTE, 1);
22718         this.update();
22719     },
22720     
22721     onDecrementMinutes: function()
22722     {
22723         Roo.log('onDecrementMinutes');
22724         this.time = this.time.add(Date.MINUTE, -1);
22725         this.update();
22726     },
22727     
22728     onTogglePeriod: function()
22729     {
22730         Roo.log('onTogglePeriod');
22731         this.time = this.time.add(Date.HOUR, 12);
22732         this.update();
22733     }
22734     
22735    
22736 });
22737  
22738
22739 Roo.apply(Roo.bootstrap.TimeField,  {
22740   
22741     template : {
22742         tag: 'div',
22743         cls: 'datepicker dropdown-menu',
22744         cn: [
22745             {
22746                 tag: 'div',
22747                 cls: 'datepicker-time',
22748                 cn: [
22749                 {
22750                     tag: 'table',
22751                     cls: 'table-condensed',
22752                     cn:[
22753                         {
22754                             tag: 'tbody',
22755                             cn: [
22756                                 {
22757                                     tag: 'tr',
22758                                     cn: [
22759                                     {
22760                                         tag: 'td',
22761                                         colspan: '7'
22762                                     }
22763                                     ]
22764                                 }
22765                             ]
22766                         },
22767                         {
22768                             tag: 'tfoot',
22769                             cn: [
22770                                 {
22771                                     tag: 'tr',
22772                                     cn: [
22773                                     {
22774                                         tag: 'th',
22775                                         colspan: '7',
22776                                         cls: '',
22777                                         cn: [
22778                                             {
22779                                                 tag: 'button',
22780                                                 cls: 'btn btn-info ok',
22781                                                 html: 'OK'
22782                                             }
22783                                         ]
22784                                     }
22785                     
22786                                     ]
22787                                 }
22788                             ]
22789                         }
22790                     ]
22791                 }
22792                 ]
22793             }
22794         ]
22795     }
22796 });
22797
22798  
22799
22800  /*
22801  * - LGPL
22802  *
22803  * MonthField
22804  * 
22805  */
22806
22807 /**
22808  * @class Roo.bootstrap.MonthField
22809  * @extends Roo.bootstrap.Input
22810  * Bootstrap MonthField class
22811  * 
22812  * @cfg {String} language default en
22813  * 
22814  * @constructor
22815  * Create a new MonthField
22816  * @param {Object} config The config object
22817  */
22818
22819 Roo.bootstrap.MonthField = function(config){
22820     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22821     
22822     this.addEvents({
22823         /**
22824          * @event show
22825          * Fires when this field show.
22826          * @param {Roo.bootstrap.MonthField} this
22827          * @param {Mixed} date The date value
22828          */
22829         show : true,
22830         /**
22831          * @event show
22832          * Fires when this field hide.
22833          * @param {Roo.bootstrap.MonthField} this
22834          * @param {Mixed} date The date value
22835          */
22836         hide : true,
22837         /**
22838          * @event select
22839          * Fires when select a date.
22840          * @param {Roo.bootstrap.MonthField} this
22841          * @param {String} oldvalue The old value
22842          * @param {String} newvalue The new value
22843          */
22844         select : true
22845     });
22846 };
22847
22848 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22849     
22850     onRender: function(ct, position)
22851     {
22852         
22853         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22854         
22855         this.language = this.language || 'en';
22856         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22857         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22858         
22859         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22860         this.isInline = false;
22861         this.isInput = true;
22862         this.component = this.el.select('.add-on', true).first() || false;
22863         this.component = (this.component && this.component.length === 0) ? false : this.component;
22864         this.hasInput = this.component && this.inputEL().length;
22865         
22866         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22867         
22868         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22869         
22870         this.picker().on('mousedown', this.onMousedown, this);
22871         this.picker().on('click', this.onClick, this);
22872         
22873         this.picker().addClass('datepicker-dropdown');
22874         
22875         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22876             v.setStyle('width', '189px');
22877         });
22878         
22879         this.fillMonths();
22880         
22881         this.update();
22882         
22883         if(this.isInline) {
22884             this.show();
22885         }
22886         
22887     },
22888     
22889     setValue: function(v, suppressEvent)
22890     {   
22891         var o = this.getValue();
22892         
22893         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22894         
22895         this.update();
22896
22897         if(suppressEvent !== true){
22898             this.fireEvent('select', this, o, v);
22899         }
22900         
22901     },
22902     
22903     getValue: function()
22904     {
22905         return this.value;
22906     },
22907     
22908     onClick: function(e) 
22909     {
22910         e.stopPropagation();
22911         e.preventDefault();
22912         
22913         var target = e.getTarget();
22914         
22915         if(target.nodeName.toLowerCase() === 'i'){
22916             target = Roo.get(target).dom.parentNode;
22917         }
22918         
22919         var nodeName = target.nodeName;
22920         var className = target.className;
22921         var html = target.innerHTML;
22922         
22923         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22924             return;
22925         }
22926         
22927         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22928         
22929         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22930         
22931         this.hide();
22932                         
22933     },
22934     
22935     picker : function()
22936     {
22937         return this.pickerEl;
22938     },
22939     
22940     fillMonths: function()
22941     {    
22942         var i = 0;
22943         var months = this.picker().select('>.datepicker-months td', true).first();
22944         
22945         months.dom.innerHTML = '';
22946         
22947         while (i < 12) {
22948             var month = {
22949                 tag: 'span',
22950                 cls: 'month',
22951                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22952             };
22953             
22954             months.createChild(month);
22955         }
22956         
22957     },
22958     
22959     update: function()
22960     {
22961         var _this = this;
22962         
22963         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22964             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22965         }
22966         
22967         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22968             e.removeClass('active');
22969             
22970             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22971                 e.addClass('active');
22972             }
22973         })
22974     },
22975     
22976     place: function()
22977     {
22978         if(this.isInline) {
22979             return;
22980         }
22981         
22982         this.picker().removeClass(['bottom', 'top']);
22983         
22984         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22985             /*
22986              * place to the top of element!
22987              *
22988              */
22989             
22990             this.picker().addClass('top');
22991             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22992             
22993             return;
22994         }
22995         
22996         this.picker().addClass('bottom');
22997         
22998         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22999     },
23000     
23001     onFocus : function()
23002     {
23003         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23004         this.show();
23005     },
23006     
23007     onBlur : function()
23008     {
23009         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23010         
23011         var d = this.inputEl().getValue();
23012         
23013         this.setValue(d);
23014                 
23015         this.hide();
23016     },
23017     
23018     show : function()
23019     {
23020         this.picker().show();
23021         this.picker().select('>.datepicker-months', true).first().show();
23022         this.update();
23023         this.place();
23024         
23025         this.fireEvent('show', this, this.date);
23026     },
23027     
23028     hide : function()
23029     {
23030         if(this.isInline) {
23031             return;
23032         }
23033         this.picker().hide();
23034         this.fireEvent('hide', this, this.date);
23035         
23036     },
23037     
23038     onMousedown: function(e)
23039     {
23040         e.stopPropagation();
23041         e.preventDefault();
23042     },
23043     
23044     keyup: function(e)
23045     {
23046         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23047         this.update();
23048     },
23049
23050     fireKey: function(e)
23051     {
23052         if (!this.picker().isVisible()){
23053             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23054                 this.show();
23055             }
23056             return;
23057         }
23058         
23059         var dir;
23060         
23061         switch(e.keyCode){
23062             case 27: // escape
23063                 this.hide();
23064                 e.preventDefault();
23065                 break;
23066             case 37: // left
23067             case 39: // right
23068                 dir = e.keyCode == 37 ? -1 : 1;
23069                 
23070                 this.vIndex = this.vIndex + dir;
23071                 
23072                 if(this.vIndex < 0){
23073                     this.vIndex = 0;
23074                 }
23075                 
23076                 if(this.vIndex > 11){
23077                     this.vIndex = 11;
23078                 }
23079                 
23080                 if(isNaN(this.vIndex)){
23081                     this.vIndex = 0;
23082                 }
23083                 
23084                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23085                 
23086                 break;
23087             case 38: // up
23088             case 40: // down
23089                 
23090                 dir = e.keyCode == 38 ? -1 : 1;
23091                 
23092                 this.vIndex = this.vIndex + dir * 4;
23093                 
23094                 if(this.vIndex < 0){
23095                     this.vIndex = 0;
23096                 }
23097                 
23098                 if(this.vIndex > 11){
23099                     this.vIndex = 11;
23100                 }
23101                 
23102                 if(isNaN(this.vIndex)){
23103                     this.vIndex = 0;
23104                 }
23105                 
23106                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23107                 break;
23108                 
23109             case 13: // enter
23110                 
23111                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23112                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23113                 }
23114                 
23115                 this.hide();
23116                 e.preventDefault();
23117                 break;
23118             case 9: // tab
23119                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23120                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23121                 }
23122                 this.hide();
23123                 break;
23124             case 16: // shift
23125             case 17: // ctrl
23126             case 18: // alt
23127                 break;
23128             default :
23129                 this.hide();
23130                 
23131         }
23132     },
23133     
23134     remove: function() 
23135     {
23136         this.picker().remove();
23137     }
23138    
23139 });
23140
23141 Roo.apply(Roo.bootstrap.MonthField,  {
23142     
23143     content : {
23144         tag: 'tbody',
23145         cn: [
23146         {
23147             tag: 'tr',
23148             cn: [
23149             {
23150                 tag: 'td',
23151                 colspan: '7'
23152             }
23153             ]
23154         }
23155         ]
23156     },
23157     
23158     dates:{
23159         en: {
23160             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23161             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23162         }
23163     }
23164 });
23165
23166 Roo.apply(Roo.bootstrap.MonthField,  {
23167   
23168     template : {
23169         tag: 'div',
23170         cls: 'datepicker dropdown-menu roo-dynamic',
23171         cn: [
23172             {
23173                 tag: 'div',
23174                 cls: 'datepicker-months',
23175                 cn: [
23176                 {
23177                     tag: 'table',
23178                     cls: 'table-condensed',
23179                     cn:[
23180                         Roo.bootstrap.DateField.content
23181                     ]
23182                 }
23183                 ]
23184             }
23185         ]
23186     }
23187 });
23188
23189  
23190
23191  
23192  /*
23193  * - LGPL
23194  *
23195  * CheckBox
23196  * 
23197  */
23198
23199 /**
23200  * @class Roo.bootstrap.CheckBox
23201  * @extends Roo.bootstrap.Input
23202  * Bootstrap CheckBox class
23203  * 
23204  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23205  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23206  * @cfg {String} boxLabel The text that appears beside the checkbox
23207  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23208  * @cfg {Boolean} checked initnal the element
23209  * @cfg {Boolean} inline inline the element (default false)
23210  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23211  * @cfg {String} tooltip label tooltip
23212  * 
23213  * @constructor
23214  * Create a new CheckBox
23215  * @param {Object} config The config object
23216  */
23217
23218 Roo.bootstrap.CheckBox = function(config){
23219     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23220    
23221     this.addEvents({
23222         /**
23223         * @event check
23224         * Fires when the element is checked or unchecked.
23225         * @param {Roo.bootstrap.CheckBox} this This input
23226         * @param {Boolean} checked The new checked value
23227         */
23228        check : true,
23229        /**
23230         * @event click
23231         * Fires when the element is click.
23232         * @param {Roo.bootstrap.CheckBox} this This input
23233         */
23234        click : true
23235     });
23236     
23237 };
23238
23239 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23240   
23241     inputType: 'checkbox',
23242     inputValue: 1,
23243     valueOff: 0,
23244     boxLabel: false,
23245     checked: false,
23246     weight : false,
23247     inline: false,
23248     tooltip : '',
23249     
23250     // checkbox success does not make any sense really.. 
23251     invalidClass : "",
23252     validClass : "",
23253     
23254     
23255     getAutoCreate : function()
23256     {
23257         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23258         
23259         var id = Roo.id();
23260         
23261         var cfg = {};
23262         
23263         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23264         
23265         if(this.inline){
23266             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23267         }
23268         
23269         var input =  {
23270             tag: 'input',
23271             id : id,
23272             type : this.inputType,
23273             value : this.inputValue,
23274             cls : 'roo-' + this.inputType, //'form-box',
23275             placeholder : this.placeholder || ''
23276             
23277         };
23278         
23279         if(this.inputType != 'radio'){
23280             var hidden =  {
23281                 tag: 'input',
23282                 type : 'hidden',
23283                 cls : 'roo-hidden-value',
23284                 value : this.checked ? this.inputValue : this.valueOff
23285             };
23286         }
23287         
23288             
23289         if (this.weight) { // Validity check?
23290             cfg.cls += " " + this.inputType + "-" + this.weight;
23291         }
23292         
23293         if (this.disabled) {
23294             input.disabled=true;
23295         }
23296         
23297         if(this.checked){
23298             input.checked = this.checked;
23299         }
23300         
23301         if (this.name) {
23302             
23303             input.name = this.name;
23304             
23305             if(this.inputType != 'radio'){
23306                 hidden.name = this.name;
23307                 input.name = '_hidden_' + this.name;
23308             }
23309         }
23310         
23311         if (this.size) {
23312             input.cls += ' input-' + this.size;
23313         }
23314         
23315         var settings=this;
23316         
23317         ['xs','sm','md','lg'].map(function(size){
23318             if (settings[size]) {
23319                 cfg.cls += ' col-' + size + '-' + settings[size];
23320             }
23321         });
23322         
23323         var inputblock = input;
23324          
23325         if (this.before || this.after) {
23326             
23327             inputblock = {
23328                 cls : 'input-group',
23329                 cn :  [] 
23330             };
23331             
23332             if (this.before) {
23333                 inputblock.cn.push({
23334                     tag :'span',
23335                     cls : 'input-group-addon',
23336                     html : this.before
23337                 });
23338             }
23339             
23340             inputblock.cn.push(input);
23341             
23342             if(this.inputType != 'radio'){
23343                 inputblock.cn.push(hidden);
23344             }
23345             
23346             if (this.after) {
23347                 inputblock.cn.push({
23348                     tag :'span',
23349                     cls : 'input-group-addon',
23350                     html : this.after
23351                 });
23352             }
23353             
23354         }
23355         var boxLabelCfg = false;
23356         
23357         if(this.boxLabel){
23358            
23359             boxLabelCfg = {
23360                 tag: 'label',
23361                 //'for': id, // box label is handled by onclick - so no for...
23362                 cls: 'box-label',
23363                 html: this.boxLabel
23364             };
23365             if(this.tooltip){
23366                 boxLabelCfg.tooltip = this.tooltip;
23367             }
23368              
23369         }
23370         
23371         
23372         if (align ==='left' && this.fieldLabel.length) {
23373 //                Roo.log("left and has label");
23374             cfg.cn = [
23375                 {
23376                     tag: 'label',
23377                     'for' :  id,
23378                     cls : 'control-label',
23379                     html : this.fieldLabel
23380                 },
23381                 {
23382                     cls : "", 
23383                     cn: [
23384                         inputblock
23385                     ]
23386                 }
23387             ];
23388             
23389             if (boxLabelCfg) {
23390                 cfg.cn[1].cn.push(boxLabelCfg);
23391             }
23392             
23393             if(this.labelWidth > 12){
23394                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23395             }
23396             
23397             if(this.labelWidth < 13 && this.labelmd == 0){
23398                 this.labelmd = this.labelWidth;
23399             }
23400             
23401             if(this.labellg > 0){
23402                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23403                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23404             }
23405             
23406             if(this.labelmd > 0){
23407                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23408                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23409             }
23410             
23411             if(this.labelsm > 0){
23412                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23413                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23414             }
23415             
23416             if(this.labelxs > 0){
23417                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23418                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23419             }
23420             
23421         } else if ( this.fieldLabel.length) {
23422 //                Roo.log(" label");
23423                 cfg.cn = [
23424                    
23425                     {
23426                         tag: this.boxLabel ? 'span' : 'label',
23427                         'for': id,
23428                         cls: 'control-label box-input-label',
23429                         //cls : 'input-group-addon',
23430                         html : this.fieldLabel
23431                     },
23432                     
23433                     inputblock
23434                     
23435                 ];
23436                 if (boxLabelCfg) {
23437                     cfg.cn.push(boxLabelCfg);
23438                 }
23439
23440         } else {
23441             
23442 //                Roo.log(" no label && no align");
23443                 cfg.cn = [  inputblock ] ;
23444                 if (boxLabelCfg) {
23445                     cfg.cn.push(boxLabelCfg);
23446                 }
23447
23448                 
23449         }
23450         
23451        
23452         
23453         if(this.inputType != 'radio'){
23454             cfg.cn.push(hidden);
23455         }
23456         
23457         return cfg;
23458         
23459     },
23460     
23461     /**
23462      * return the real input element.
23463      */
23464     inputEl: function ()
23465     {
23466         return this.el.select('input.roo-' + this.inputType,true).first();
23467     },
23468     hiddenEl: function ()
23469     {
23470         return this.el.select('input.roo-hidden-value',true).first();
23471     },
23472     
23473     labelEl: function()
23474     {
23475         return this.el.select('label.control-label',true).first();
23476     },
23477     /* depricated... */
23478     
23479     label: function()
23480     {
23481         return this.labelEl();
23482     },
23483     
23484     boxLabelEl: function()
23485     {
23486         return this.el.select('label.box-label',true).first();
23487     },
23488     
23489     initEvents : function()
23490     {
23491 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23492         
23493         this.inputEl().on('click', this.onClick,  this);
23494         
23495         if (this.boxLabel) { 
23496             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23497         }
23498         
23499         this.startValue = this.getValue();
23500         
23501         if(this.groupId){
23502             Roo.bootstrap.CheckBox.register(this);
23503         }
23504     },
23505     
23506     onClick : function(e)
23507     {   
23508         if(this.fireEvent('click', this, e) !== false){
23509             this.setChecked(!this.checked);
23510         }
23511         
23512     },
23513     
23514     setChecked : function(state,suppressEvent)
23515     {
23516         this.startValue = this.getValue();
23517
23518         if(this.inputType == 'radio'){
23519             
23520             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23521                 e.dom.checked = false;
23522             });
23523             
23524             this.inputEl().dom.checked = true;
23525             
23526             this.inputEl().dom.value = this.inputValue;
23527             
23528             if(suppressEvent !== true){
23529                 this.fireEvent('check', this, true);
23530             }
23531             
23532             this.validate();
23533             
23534             return;
23535         }
23536         
23537         this.checked = state;
23538         
23539         this.inputEl().dom.checked = state;
23540         
23541         
23542         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23543         
23544         if(suppressEvent !== true){
23545             this.fireEvent('check', this, state);
23546         }
23547         
23548         this.validate();
23549     },
23550     
23551     getValue : function()
23552     {
23553         if(this.inputType == 'radio'){
23554             return this.getGroupValue();
23555         }
23556         
23557         return this.hiddenEl().dom.value;
23558         
23559     },
23560     
23561     getGroupValue : function()
23562     {
23563         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23564             return '';
23565         }
23566         
23567         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23568     },
23569     
23570     setValue : function(v,suppressEvent)
23571     {
23572         if(this.inputType == 'radio'){
23573             this.setGroupValue(v, suppressEvent);
23574             return;
23575         }
23576         
23577         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23578         
23579         this.validate();
23580     },
23581     
23582     setGroupValue : function(v, suppressEvent)
23583     {
23584         this.startValue = this.getValue();
23585         
23586         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23587             e.dom.checked = false;
23588             
23589             if(e.dom.value == v){
23590                 e.dom.checked = true;
23591             }
23592         });
23593         
23594         if(suppressEvent !== true){
23595             this.fireEvent('check', this, true);
23596         }
23597
23598         this.validate();
23599         
23600         return;
23601     },
23602     
23603     validate : function()
23604     {
23605         if(this.getVisibilityEl().hasClass('hidden')){
23606             return true;
23607         }
23608         
23609         if(
23610                 this.disabled || 
23611                 (this.inputType == 'radio' && this.validateRadio()) ||
23612                 (this.inputType == 'checkbox' && this.validateCheckbox())
23613         ){
23614             this.markValid();
23615             return true;
23616         }
23617         
23618         this.markInvalid();
23619         return false;
23620     },
23621     
23622     validateRadio : function()
23623     {
23624         if(this.getVisibilityEl().hasClass('hidden')){
23625             return true;
23626         }
23627         
23628         if(this.allowBlank){
23629             return true;
23630         }
23631         
23632         var valid = false;
23633         
23634         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23635             if(!e.dom.checked){
23636                 return;
23637             }
23638             
23639             valid = true;
23640             
23641             return false;
23642         });
23643         
23644         return valid;
23645     },
23646     
23647     validateCheckbox : function()
23648     {
23649         if(!this.groupId){
23650             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23651             //return (this.getValue() == this.inputValue) ? true : false;
23652         }
23653         
23654         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23655         
23656         if(!group){
23657             return false;
23658         }
23659         
23660         var r = false;
23661         
23662         for(var i in group){
23663             if(group[i].el.isVisible(true)){
23664                 r = false;
23665                 break;
23666             }
23667             
23668             r = true;
23669         }
23670         
23671         for(var i in group){
23672             if(r){
23673                 break;
23674             }
23675             
23676             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23677         }
23678         
23679         return r;
23680     },
23681     
23682     /**
23683      * Mark this field as valid
23684      */
23685     markValid : function()
23686     {
23687         var _this = this;
23688         
23689         this.fireEvent('valid', this);
23690         
23691         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23692         
23693         if(this.groupId){
23694             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23695         }
23696         
23697         if(label){
23698             label.markValid();
23699         }
23700
23701         if(this.inputType == 'radio'){
23702             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23703                 var fg = e.findParent('.form-group', false, true);
23704                 if (Roo.bootstrap.version == 3) {
23705                     fg.removeClass([_this.invalidClass, _this.validClass]);
23706                     fg.addClass(_this.validClass);
23707                 } else {
23708                     fg.removeClass(['is-valid', 'is-invalid']);
23709                     fg.addClass('is-valid');
23710                 }
23711             });
23712             
23713             return;
23714         }
23715
23716         if(!this.groupId){
23717             var fg = this.el.findParent('.form-group', false, true);
23718             if (Roo.bootstrap.version == 3) {
23719                 fg.removeClass([this.invalidClass, this.validClass]);
23720                 fg.addClass(this.validClass);
23721             } else {
23722                 fg.removeClass(['is-valid', 'is-invalid']);
23723                 fg.addClass('is-valid');
23724             }
23725             return;
23726         }
23727         
23728         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23729         
23730         if(!group){
23731             return;
23732         }
23733         
23734         for(var i in group){
23735             var fg = group[i].el.findParent('.form-group', false, true);
23736             if (Roo.bootstrap.version == 3) {
23737                 fg.removeClass([this.invalidClass, this.validClass]);
23738                 fg.addClass(this.validClass);
23739             } else {
23740                 fg.removeClass(['is-valid', 'is-invalid']);
23741                 fg.addClass('is-valid');
23742             }
23743         }
23744     },
23745     
23746      /**
23747      * Mark this field as invalid
23748      * @param {String} msg The validation message
23749      */
23750     markInvalid : function(msg)
23751     {
23752         if(this.allowBlank){
23753             return;
23754         }
23755         
23756         var _this = this;
23757         
23758         this.fireEvent('invalid', this, msg);
23759         
23760         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23761         
23762         if(this.groupId){
23763             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23764         }
23765         
23766         if(label){
23767             label.markInvalid();
23768         }
23769             
23770         if(this.inputType == 'radio'){
23771             
23772             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23773                 var fg = e.findParent('.form-group', false, true);
23774                 if (Roo.bootstrap.version == 3) {
23775                     fg.removeClass([_this.invalidClass, _this.validClass]);
23776                     fg.addClass(_this.invalidClass);
23777                 } else {
23778                     fg.removeClass(['is-invalid', 'is-valid']);
23779                     fg.addClass('is-invalid');
23780                 }
23781             });
23782             
23783             return;
23784         }
23785         
23786         if(!this.groupId){
23787             var fg = this.el.findParent('.form-group', false, true);
23788             if (Roo.bootstrap.version == 3) {
23789                 fg.removeClass([_this.invalidClass, _this.validClass]);
23790                 fg.addClass(_this.invalidClass);
23791             } else {
23792                 fg.removeClass(['is-invalid', 'is-valid']);
23793                 fg.addClass('is-invalid');
23794             }
23795             return;
23796         }
23797         
23798         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23799         
23800         if(!group){
23801             return;
23802         }
23803         
23804         for(var i in group){
23805             var fg = group[i].el.findParent('.form-group', false, true);
23806             if (Roo.bootstrap.version == 3) {
23807                 fg.removeClass([_this.invalidClass, _this.validClass]);
23808                 fg.addClass(_this.invalidClass);
23809             } else {
23810                 fg.removeClass(['is-invalid', 'is-valid']);
23811                 fg.addClass('is-invalid');
23812             }
23813         }
23814         
23815     },
23816     
23817     clearInvalid : function()
23818     {
23819         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23820         
23821         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23822         
23823         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23824         
23825         if (label && label.iconEl) {
23826             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23827             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23828         }
23829     },
23830     
23831     disable : function()
23832     {
23833         if(this.inputType != 'radio'){
23834             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23835             return;
23836         }
23837         
23838         var _this = this;
23839         
23840         if(this.rendered){
23841             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23842                 _this.getActionEl().addClass(this.disabledClass);
23843                 e.dom.disabled = true;
23844             });
23845         }
23846         
23847         this.disabled = true;
23848         this.fireEvent("disable", this);
23849         return this;
23850     },
23851
23852     enable : function()
23853     {
23854         if(this.inputType != 'radio'){
23855             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23856             return;
23857         }
23858         
23859         var _this = this;
23860         
23861         if(this.rendered){
23862             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23863                 _this.getActionEl().removeClass(this.disabledClass);
23864                 e.dom.disabled = false;
23865             });
23866         }
23867         
23868         this.disabled = false;
23869         this.fireEvent("enable", this);
23870         return this;
23871     },
23872     
23873     setBoxLabel : function(v)
23874     {
23875         this.boxLabel = v;
23876         
23877         if(this.rendered){
23878             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23879         }
23880     }
23881
23882 });
23883
23884 Roo.apply(Roo.bootstrap.CheckBox, {
23885     
23886     groups: {},
23887     
23888      /**
23889     * register a CheckBox Group
23890     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23891     */
23892     register : function(checkbox)
23893     {
23894         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23895             this.groups[checkbox.groupId] = {};
23896         }
23897         
23898         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23899             return;
23900         }
23901         
23902         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23903         
23904     },
23905     /**
23906     * fetch a CheckBox Group based on the group ID
23907     * @param {string} the group ID
23908     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23909     */
23910     get: function(groupId) {
23911         if (typeof(this.groups[groupId]) == 'undefined') {
23912             return false;
23913         }
23914         
23915         return this.groups[groupId] ;
23916     }
23917     
23918     
23919 });
23920 /*
23921  * - LGPL
23922  *
23923  * RadioItem
23924  * 
23925  */
23926
23927 /**
23928  * @class Roo.bootstrap.Radio
23929  * @extends Roo.bootstrap.Component
23930  * Bootstrap Radio class
23931  * @cfg {String} boxLabel - the label associated
23932  * @cfg {String} value - the value of radio
23933  * 
23934  * @constructor
23935  * Create a new Radio
23936  * @param {Object} config The config object
23937  */
23938 Roo.bootstrap.Radio = function(config){
23939     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23940     
23941 };
23942
23943 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23944     
23945     boxLabel : '',
23946     
23947     value : '',
23948     
23949     getAutoCreate : function()
23950     {
23951         var cfg = {
23952             tag : 'div',
23953             cls : 'form-group radio',
23954             cn : [
23955                 {
23956                     tag : 'label',
23957                     cls : 'box-label',
23958                     html : this.boxLabel
23959                 }
23960             ]
23961         };
23962         
23963         return cfg;
23964     },
23965     
23966     initEvents : function() 
23967     {
23968         this.parent().register(this);
23969         
23970         this.el.on('click', this.onClick, this);
23971         
23972     },
23973     
23974     onClick : function(e)
23975     {
23976         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23977             this.setChecked(true);
23978         }
23979     },
23980     
23981     setChecked : function(state, suppressEvent)
23982     {
23983         this.parent().setValue(this.value, suppressEvent);
23984         
23985     },
23986     
23987     setBoxLabel : function(v)
23988     {
23989         this.boxLabel = v;
23990         
23991         if(this.rendered){
23992             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23993         }
23994     }
23995     
23996 });
23997  
23998
23999  /*
24000  * - LGPL
24001  *
24002  * Input
24003  * 
24004  */
24005
24006 /**
24007  * @class Roo.bootstrap.SecurePass
24008  * @extends Roo.bootstrap.Input
24009  * Bootstrap SecurePass class
24010  *
24011  * 
24012  * @constructor
24013  * Create a new SecurePass
24014  * @param {Object} config The config object
24015  */
24016  
24017 Roo.bootstrap.SecurePass = function (config) {
24018     // these go here, so the translation tool can replace them..
24019     this.errors = {
24020         PwdEmpty: "Please type a password, and then retype it to confirm.",
24021         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24022         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24023         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24024         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24025         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24026         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24027         TooWeak: "Your password is Too Weak."
24028     },
24029     this.meterLabel = "Password strength:";
24030     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24031     this.meterClass = [
24032         "roo-password-meter-tooweak", 
24033         "roo-password-meter-weak", 
24034         "roo-password-meter-medium", 
24035         "roo-password-meter-strong", 
24036         "roo-password-meter-grey"
24037     ];
24038     
24039     this.errors = {};
24040     
24041     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24042 }
24043
24044 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24045     /**
24046      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24047      * {
24048      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24049      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24050      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24051      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24052      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24053      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24054      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24055      * })
24056      */
24057     // private
24058     
24059     meterWidth: 300,
24060     errorMsg :'',    
24061     errors: false,
24062     imageRoot: '/',
24063     /**
24064      * @cfg {String/Object} Label for the strength meter (defaults to
24065      * 'Password strength:')
24066      */
24067     // private
24068     meterLabel: '',
24069     /**
24070      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24071      * ['Weak', 'Medium', 'Strong'])
24072      */
24073     // private    
24074     pwdStrengths: false,    
24075     // private
24076     strength: 0,
24077     // private
24078     _lastPwd: null,
24079     // private
24080     kCapitalLetter: 0,
24081     kSmallLetter: 1,
24082     kDigit: 2,
24083     kPunctuation: 3,
24084     
24085     insecure: false,
24086     // private
24087     initEvents: function ()
24088     {
24089         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24090
24091         if (this.el.is('input[type=password]') && Roo.isSafari) {
24092             this.el.on('keydown', this.SafariOnKeyDown, this);
24093         }
24094
24095         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24096     },
24097     // private
24098     onRender: function (ct, position)
24099     {
24100         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24101         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24102         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24103
24104         this.trigger.createChild({
24105                    cn: [
24106                     {
24107                     //id: 'PwdMeter',
24108                     tag: 'div',
24109                     cls: 'roo-password-meter-grey col-xs-12',
24110                     style: {
24111                         //width: 0,
24112                         //width: this.meterWidth + 'px'                                                
24113                         }
24114                     },
24115                     {                            
24116                          cls: 'roo-password-meter-text'                          
24117                     }
24118                 ]            
24119         });
24120
24121          
24122         if (this.hideTrigger) {
24123             this.trigger.setDisplayed(false);
24124         }
24125         this.setSize(this.width || '', this.height || '');
24126     },
24127     // private
24128     onDestroy: function ()
24129     {
24130         if (this.trigger) {
24131             this.trigger.removeAllListeners();
24132             this.trigger.remove();
24133         }
24134         if (this.wrap) {
24135             this.wrap.remove();
24136         }
24137         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24138     },
24139     // private
24140     checkStrength: function ()
24141     {
24142         var pwd = this.inputEl().getValue();
24143         if (pwd == this._lastPwd) {
24144             return;
24145         }
24146
24147         var strength;
24148         if (this.ClientSideStrongPassword(pwd)) {
24149             strength = 3;
24150         } else if (this.ClientSideMediumPassword(pwd)) {
24151             strength = 2;
24152         } else if (this.ClientSideWeakPassword(pwd)) {
24153             strength = 1;
24154         } else {
24155             strength = 0;
24156         }
24157         
24158         Roo.log('strength1: ' + strength);
24159         
24160         //var pm = this.trigger.child('div/div/div').dom;
24161         var pm = this.trigger.child('div/div');
24162         pm.removeClass(this.meterClass);
24163         pm.addClass(this.meterClass[strength]);
24164                 
24165         
24166         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24167                 
24168         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24169         
24170         this._lastPwd = pwd;
24171     },
24172     reset: function ()
24173     {
24174         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24175         
24176         this._lastPwd = '';
24177         
24178         var pm = this.trigger.child('div/div');
24179         pm.removeClass(this.meterClass);
24180         pm.addClass('roo-password-meter-grey');        
24181         
24182         
24183         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24184         
24185         pt.innerHTML = '';
24186         this.inputEl().dom.type='password';
24187     },
24188     // private
24189     validateValue: function (value)
24190     {
24191         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24192             return false;
24193         }
24194         if (value.length == 0) {
24195             if (this.allowBlank) {
24196                 this.clearInvalid();
24197                 return true;
24198             }
24199
24200             this.markInvalid(this.errors.PwdEmpty);
24201             this.errorMsg = this.errors.PwdEmpty;
24202             return false;
24203         }
24204         
24205         if(this.insecure){
24206             return true;
24207         }
24208         
24209         if (!value.match(/[\x21-\x7e]+/)) {
24210             this.markInvalid(this.errors.PwdBadChar);
24211             this.errorMsg = this.errors.PwdBadChar;
24212             return false;
24213         }
24214         if (value.length < 6) {
24215             this.markInvalid(this.errors.PwdShort);
24216             this.errorMsg = this.errors.PwdShort;
24217             return false;
24218         }
24219         if (value.length > 16) {
24220             this.markInvalid(this.errors.PwdLong);
24221             this.errorMsg = this.errors.PwdLong;
24222             return false;
24223         }
24224         var strength;
24225         if (this.ClientSideStrongPassword(value)) {
24226             strength = 3;
24227         } else if (this.ClientSideMediumPassword(value)) {
24228             strength = 2;
24229         } else if (this.ClientSideWeakPassword(value)) {
24230             strength = 1;
24231         } else {
24232             strength = 0;
24233         }
24234
24235         
24236         if (strength < 2) {
24237             //this.markInvalid(this.errors.TooWeak);
24238             this.errorMsg = this.errors.TooWeak;
24239             //return false;
24240         }
24241         
24242         
24243         console.log('strength2: ' + strength);
24244         
24245         //var pm = this.trigger.child('div/div/div').dom;
24246         
24247         var pm = this.trigger.child('div/div');
24248         pm.removeClass(this.meterClass);
24249         pm.addClass(this.meterClass[strength]);
24250                 
24251         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24252                 
24253         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24254         
24255         this.errorMsg = ''; 
24256         return true;
24257     },
24258     // private
24259     CharacterSetChecks: function (type)
24260     {
24261         this.type = type;
24262         this.fResult = false;
24263     },
24264     // private
24265     isctype: function (character, type)
24266     {
24267         switch (type) {  
24268             case this.kCapitalLetter:
24269                 if (character >= 'A' && character <= 'Z') {
24270                     return true;
24271                 }
24272                 break;
24273             
24274             case this.kSmallLetter:
24275                 if (character >= 'a' && character <= 'z') {
24276                     return true;
24277                 }
24278                 break;
24279             
24280             case this.kDigit:
24281                 if (character >= '0' && character <= '9') {
24282                     return true;
24283                 }
24284                 break;
24285             
24286             case this.kPunctuation:
24287                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24288                     return true;
24289                 }
24290                 break;
24291             
24292             default:
24293                 return false;
24294         }
24295
24296     },
24297     // private
24298     IsLongEnough: function (pwd, size)
24299     {
24300         return !(pwd == null || isNaN(size) || pwd.length < size);
24301     },
24302     // private
24303     SpansEnoughCharacterSets: function (word, nb)
24304     {
24305         if (!this.IsLongEnough(word, nb))
24306         {
24307             return false;
24308         }
24309
24310         var characterSetChecks = new Array(
24311             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24312             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24313         );
24314         
24315         for (var index = 0; index < word.length; ++index) {
24316             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24317                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24318                     characterSetChecks[nCharSet].fResult = true;
24319                     break;
24320                 }
24321             }
24322         }
24323
24324         var nCharSets = 0;
24325         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24326             if (characterSetChecks[nCharSet].fResult) {
24327                 ++nCharSets;
24328             }
24329         }
24330
24331         if (nCharSets < nb) {
24332             return false;
24333         }
24334         return true;
24335     },
24336     // private
24337     ClientSideStrongPassword: function (pwd)
24338     {
24339         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24340     },
24341     // private
24342     ClientSideMediumPassword: function (pwd)
24343     {
24344         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24345     },
24346     // private
24347     ClientSideWeakPassword: function (pwd)
24348     {
24349         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24350     }
24351           
24352 })//<script type="text/javascript">
24353
24354 /*
24355  * Based  Ext JS Library 1.1.1
24356  * Copyright(c) 2006-2007, Ext JS, LLC.
24357  * LGPL
24358  *
24359  */
24360  
24361 /**
24362  * @class Roo.HtmlEditorCore
24363  * @extends Roo.Component
24364  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24365  *
24366  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24367  */
24368
24369 Roo.HtmlEditorCore = function(config){
24370     
24371     
24372     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24373     
24374     
24375     this.addEvents({
24376         /**
24377          * @event initialize
24378          * Fires when the editor is fully initialized (including the iframe)
24379          * @param {Roo.HtmlEditorCore} this
24380          */
24381         initialize: true,
24382         /**
24383          * @event activate
24384          * Fires when the editor is first receives the focus. Any insertion must wait
24385          * until after this event.
24386          * @param {Roo.HtmlEditorCore} this
24387          */
24388         activate: true,
24389          /**
24390          * @event beforesync
24391          * Fires before the textarea is updated with content from the editor iframe. Return false
24392          * to cancel the sync.
24393          * @param {Roo.HtmlEditorCore} this
24394          * @param {String} html
24395          */
24396         beforesync: true,
24397          /**
24398          * @event beforepush
24399          * Fires before the iframe editor is updated with content from the textarea. Return false
24400          * to cancel the push.
24401          * @param {Roo.HtmlEditorCore} this
24402          * @param {String} html
24403          */
24404         beforepush: true,
24405          /**
24406          * @event sync
24407          * Fires when the textarea is updated with content from the editor iframe.
24408          * @param {Roo.HtmlEditorCore} this
24409          * @param {String} html
24410          */
24411         sync: true,
24412          /**
24413          * @event push
24414          * Fires when the iframe editor is updated with content from the textarea.
24415          * @param {Roo.HtmlEditorCore} this
24416          * @param {String} html
24417          */
24418         push: true,
24419         
24420         /**
24421          * @event editorevent
24422          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24423          * @param {Roo.HtmlEditorCore} this
24424          */
24425         editorevent: true
24426         
24427     });
24428     
24429     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24430     
24431     // defaults : white / black...
24432     this.applyBlacklists();
24433     
24434     
24435     
24436 };
24437
24438
24439 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24440
24441
24442      /**
24443      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24444      */
24445     
24446     owner : false,
24447     
24448      /**
24449      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24450      *                        Roo.resizable.
24451      */
24452     resizable : false,
24453      /**
24454      * @cfg {Number} height (in pixels)
24455      */   
24456     height: 300,
24457    /**
24458      * @cfg {Number} width (in pixels)
24459      */   
24460     width: 500,
24461     
24462     /**
24463      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24464      * 
24465      */
24466     stylesheets: false,
24467     
24468     // id of frame..
24469     frameId: false,
24470     
24471     // private properties
24472     validationEvent : false,
24473     deferHeight: true,
24474     initialized : false,
24475     activated : false,
24476     sourceEditMode : false,
24477     onFocus : Roo.emptyFn,
24478     iframePad:3,
24479     hideMode:'offsets',
24480     
24481     clearUp: true,
24482     
24483     // blacklist + whitelisted elements..
24484     black: false,
24485     white: false,
24486      
24487     bodyCls : '',
24488
24489     /**
24490      * Protected method that will not generally be called directly. It
24491      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24492      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24493      */
24494     getDocMarkup : function(){
24495         // body styles..
24496         var st = '';
24497         
24498         // inherit styels from page...?? 
24499         if (this.stylesheets === false) {
24500             
24501             Roo.get(document.head).select('style').each(function(node) {
24502                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24503             });
24504             
24505             Roo.get(document.head).select('link').each(function(node) { 
24506                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24507             });
24508             
24509         } else if (!this.stylesheets.length) {
24510                 // simple..
24511                 st = '<style type="text/css">' +
24512                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24513                    '</style>';
24514         } else {
24515             for (var i in this.stylesheets) { 
24516                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24517             }
24518             
24519         }
24520         
24521         st +=  '<style type="text/css">' +
24522             'IMG { cursor: pointer } ' +
24523         '</style>';
24524
24525         var cls = 'roo-htmleditor-body';
24526         
24527         if(this.bodyCls.length){
24528             cls += ' ' + this.bodyCls;
24529         }
24530         
24531         return '<html><head>' + st  +
24532             //<style type="text/css">' +
24533             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24534             //'</style>' +
24535             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24536     },
24537
24538     // private
24539     onRender : function(ct, position)
24540     {
24541         var _t = this;
24542         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24543         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24544         
24545         
24546         this.el.dom.style.border = '0 none';
24547         this.el.dom.setAttribute('tabIndex', -1);
24548         this.el.addClass('x-hidden hide');
24549         
24550         
24551         
24552         if(Roo.isIE){ // fix IE 1px bogus margin
24553             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24554         }
24555        
24556         
24557         this.frameId = Roo.id();
24558         
24559          
24560         
24561         var iframe = this.owner.wrap.createChild({
24562             tag: 'iframe',
24563             cls: 'form-control', // bootstrap..
24564             id: this.frameId,
24565             name: this.frameId,
24566             frameBorder : 'no',
24567             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24568         }, this.el
24569         );
24570         
24571         
24572         this.iframe = iframe.dom;
24573
24574          this.assignDocWin();
24575         
24576         this.doc.designMode = 'on';
24577        
24578         this.doc.open();
24579         this.doc.write(this.getDocMarkup());
24580         this.doc.close();
24581
24582         
24583         var task = { // must defer to wait for browser to be ready
24584             run : function(){
24585                 //console.log("run task?" + this.doc.readyState);
24586                 this.assignDocWin();
24587                 if(this.doc.body || this.doc.readyState == 'complete'){
24588                     try {
24589                         this.doc.designMode="on";
24590                     } catch (e) {
24591                         return;
24592                     }
24593                     Roo.TaskMgr.stop(task);
24594                     this.initEditor.defer(10, this);
24595                 }
24596             },
24597             interval : 10,
24598             duration: 10000,
24599             scope: this
24600         };
24601         Roo.TaskMgr.start(task);
24602
24603     },
24604
24605     // private
24606     onResize : function(w, h)
24607     {
24608          Roo.log('resize: ' +w + ',' + h );
24609         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24610         if(!this.iframe){
24611             return;
24612         }
24613         if(typeof w == 'number'){
24614             
24615             this.iframe.style.width = w + 'px';
24616         }
24617         if(typeof h == 'number'){
24618             
24619             this.iframe.style.height = h + 'px';
24620             if(this.doc){
24621                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24622             }
24623         }
24624         
24625     },
24626
24627     /**
24628      * Toggles the editor between standard and source edit mode.
24629      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24630      */
24631     toggleSourceEdit : function(sourceEditMode){
24632         
24633         this.sourceEditMode = sourceEditMode === true;
24634         
24635         if(this.sourceEditMode){
24636  
24637             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24638             
24639         }else{
24640             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24641             //this.iframe.className = '';
24642             this.deferFocus();
24643         }
24644         //this.setSize(this.owner.wrap.getSize());
24645         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24646     },
24647
24648     
24649   
24650
24651     /**
24652      * Protected method that will not generally be called directly. If you need/want
24653      * custom HTML cleanup, this is the method you should override.
24654      * @param {String} html The HTML to be cleaned
24655      * return {String} The cleaned HTML
24656      */
24657     cleanHtml : function(html){
24658         html = String(html);
24659         if(html.length > 5){
24660             if(Roo.isSafari){ // strip safari nonsense
24661                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24662             }
24663         }
24664         if(html == '&nbsp;'){
24665             html = '';
24666         }
24667         return html;
24668     },
24669
24670     /**
24671      * HTML Editor -> Textarea
24672      * Protected method that will not generally be called directly. Syncs the contents
24673      * of the editor iframe with the textarea.
24674      */
24675     syncValue : function(){
24676         if(this.initialized){
24677             var bd = (this.doc.body || this.doc.documentElement);
24678             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24679             var html = bd.innerHTML;
24680             if(Roo.isSafari){
24681                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24682                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24683                 if(m && m[1]){
24684                     html = '<div style="'+m[0]+'">' + html + '</div>';
24685                 }
24686             }
24687             html = this.cleanHtml(html);
24688             // fix up the special chars.. normaly like back quotes in word...
24689             // however we do not want to do this with chinese..
24690             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24691                 
24692                 var cc = match.charCodeAt();
24693
24694                 // Get the character value, handling surrogate pairs
24695                 if (match.length == 2) {
24696                     // It's a surrogate pair, calculate the Unicode code point
24697                     var high = match.charCodeAt(0) - 0xD800;
24698                     var low  = match.charCodeAt(1) - 0xDC00;
24699                     cc = (high * 0x400) + low + 0x10000;
24700                 }  else if (
24701                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24702                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24703                     (cc >= 0xf900 && cc < 0xfb00 )
24704                 ) {
24705                         return match;
24706                 }  
24707          
24708                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24709                 return "&#" + cc + ";";
24710                 
24711                 
24712             });
24713             
24714             
24715              
24716             if(this.owner.fireEvent('beforesync', this, html) !== false){
24717                 this.el.dom.value = html;
24718                 this.owner.fireEvent('sync', this, html);
24719             }
24720         }
24721     },
24722
24723     /**
24724      * Protected method that will not generally be called directly. Pushes the value of the textarea
24725      * into the iframe editor.
24726      */
24727     pushValue : function(){
24728         if(this.initialized){
24729             var v = this.el.dom.value.trim();
24730             
24731 //            if(v.length < 1){
24732 //                v = '&#160;';
24733 //            }
24734             
24735             if(this.owner.fireEvent('beforepush', this, v) !== false){
24736                 var d = (this.doc.body || this.doc.documentElement);
24737                 d.innerHTML = v;
24738                 this.cleanUpPaste();
24739                 this.el.dom.value = d.innerHTML;
24740                 this.owner.fireEvent('push', this, v);
24741             }
24742         }
24743     },
24744
24745     // private
24746     deferFocus : function(){
24747         this.focus.defer(10, this);
24748     },
24749
24750     // doc'ed in Field
24751     focus : function(){
24752         if(this.win && !this.sourceEditMode){
24753             this.win.focus();
24754         }else{
24755             this.el.focus();
24756         }
24757     },
24758     
24759     assignDocWin: function()
24760     {
24761         var iframe = this.iframe;
24762         
24763          if(Roo.isIE){
24764             this.doc = iframe.contentWindow.document;
24765             this.win = iframe.contentWindow;
24766         } else {
24767 //            if (!Roo.get(this.frameId)) {
24768 //                return;
24769 //            }
24770 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24771 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24772             
24773             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24774                 return;
24775             }
24776             
24777             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24778             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24779         }
24780     },
24781     
24782     // private
24783     initEditor : function(){
24784         //console.log("INIT EDITOR");
24785         this.assignDocWin();
24786         
24787         
24788         
24789         this.doc.designMode="on";
24790         this.doc.open();
24791         this.doc.write(this.getDocMarkup());
24792         this.doc.close();
24793         
24794         var dbody = (this.doc.body || this.doc.documentElement);
24795         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24796         // this copies styles from the containing element into thsi one..
24797         // not sure why we need all of this..
24798         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24799         
24800         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24801         //ss['background-attachment'] = 'fixed'; // w3c
24802         dbody.bgProperties = 'fixed'; // ie
24803         //Roo.DomHelper.applyStyles(dbody, ss);
24804         Roo.EventManager.on(this.doc, {
24805             //'mousedown': this.onEditorEvent,
24806             'mouseup': this.onEditorEvent,
24807             'dblclick': this.onEditorEvent,
24808             'click': this.onEditorEvent,
24809             'keyup': this.onEditorEvent,
24810             buffer:100,
24811             scope: this
24812         });
24813         if(Roo.isGecko){
24814             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24815         }
24816         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24817             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24818         }
24819         this.initialized = true;
24820
24821         this.owner.fireEvent('initialize', this);
24822         this.pushValue();
24823     },
24824
24825     // private
24826     onDestroy : function(){
24827         
24828         
24829         
24830         if(this.rendered){
24831             
24832             //for (var i =0; i < this.toolbars.length;i++) {
24833             //    // fixme - ask toolbars for heights?
24834             //    this.toolbars[i].onDestroy();
24835            // }
24836             
24837             //this.wrap.dom.innerHTML = '';
24838             //this.wrap.remove();
24839         }
24840     },
24841
24842     // private
24843     onFirstFocus : function(){
24844         
24845         this.assignDocWin();
24846         
24847         
24848         this.activated = true;
24849          
24850     
24851         if(Roo.isGecko){ // prevent silly gecko errors
24852             this.win.focus();
24853             var s = this.win.getSelection();
24854             if(!s.focusNode || s.focusNode.nodeType != 3){
24855                 var r = s.getRangeAt(0);
24856                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24857                 r.collapse(true);
24858                 this.deferFocus();
24859             }
24860             try{
24861                 this.execCmd('useCSS', true);
24862                 this.execCmd('styleWithCSS', false);
24863             }catch(e){}
24864         }
24865         this.owner.fireEvent('activate', this);
24866     },
24867
24868     // private
24869     adjustFont: function(btn){
24870         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24871         //if(Roo.isSafari){ // safari
24872         //    adjust *= 2;
24873        // }
24874         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24875         if(Roo.isSafari){ // safari
24876             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24877             v =  (v < 10) ? 10 : v;
24878             v =  (v > 48) ? 48 : v;
24879             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24880             
24881         }
24882         
24883         
24884         v = Math.max(1, v+adjust);
24885         
24886         this.execCmd('FontSize', v  );
24887     },
24888
24889     onEditorEvent : function(e)
24890     {
24891         this.owner.fireEvent('editorevent', this, e);
24892       //  this.updateToolbar();
24893         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24894     },
24895
24896     insertTag : function(tg)
24897     {
24898         // could be a bit smarter... -> wrap the current selected tRoo..
24899         if (tg.toLowerCase() == 'span' ||
24900             tg.toLowerCase() == 'code' ||
24901             tg.toLowerCase() == 'sup' ||
24902             tg.toLowerCase() == 'sub' 
24903             ) {
24904             
24905             range = this.createRange(this.getSelection());
24906             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24907             wrappingNode.appendChild(range.extractContents());
24908             range.insertNode(wrappingNode);
24909
24910             return;
24911             
24912             
24913             
24914         }
24915         this.execCmd("formatblock",   tg);
24916         
24917     },
24918     
24919     insertText : function(txt)
24920     {
24921         
24922         
24923         var range = this.createRange();
24924         range.deleteContents();
24925                //alert(Sender.getAttribute('label'));
24926                
24927         range.insertNode(this.doc.createTextNode(txt));
24928     } ,
24929     
24930      
24931
24932     /**
24933      * Executes a Midas editor command on the editor document and performs necessary focus and
24934      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24935      * @param {String} cmd The Midas command
24936      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24937      */
24938     relayCmd : function(cmd, value){
24939         this.win.focus();
24940         this.execCmd(cmd, value);
24941         this.owner.fireEvent('editorevent', this);
24942         //this.updateToolbar();
24943         this.owner.deferFocus();
24944     },
24945
24946     /**
24947      * Executes a Midas editor command directly on the editor document.
24948      * For visual commands, you should use {@link #relayCmd} instead.
24949      * <b>This should only be called after the editor is initialized.</b>
24950      * @param {String} cmd The Midas command
24951      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24952      */
24953     execCmd : function(cmd, value){
24954         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24955         this.syncValue();
24956     },
24957  
24958  
24959    
24960     /**
24961      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24962      * to insert tRoo.
24963      * @param {String} text | dom node.. 
24964      */
24965     insertAtCursor : function(text)
24966     {
24967         
24968         if(!this.activated){
24969             return;
24970         }
24971         /*
24972         if(Roo.isIE){
24973             this.win.focus();
24974             var r = this.doc.selection.createRange();
24975             if(r){
24976                 r.collapse(true);
24977                 r.pasteHTML(text);
24978                 this.syncValue();
24979                 this.deferFocus();
24980             
24981             }
24982             return;
24983         }
24984         */
24985         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24986             this.win.focus();
24987             
24988             
24989             // from jquery ui (MIT licenced)
24990             var range, node;
24991             var win = this.win;
24992             
24993             if (win.getSelection && win.getSelection().getRangeAt) {
24994                 range = win.getSelection().getRangeAt(0);
24995                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24996                 range.insertNode(node);
24997             } else if (win.document.selection && win.document.selection.createRange) {
24998                 // no firefox support
24999                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25000                 win.document.selection.createRange().pasteHTML(txt);
25001             } else {
25002                 // no firefox support
25003                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25004                 this.execCmd('InsertHTML', txt);
25005             } 
25006             
25007             this.syncValue();
25008             
25009             this.deferFocus();
25010         }
25011     },
25012  // private
25013     mozKeyPress : function(e){
25014         if(e.ctrlKey){
25015             var c = e.getCharCode(), cmd;
25016           
25017             if(c > 0){
25018                 c = String.fromCharCode(c).toLowerCase();
25019                 switch(c){
25020                     case 'b':
25021                         cmd = 'bold';
25022                         break;
25023                     case 'i':
25024                         cmd = 'italic';
25025                         break;
25026                     
25027                     case 'u':
25028                         cmd = 'underline';
25029                         break;
25030                     
25031                     case 'v':
25032                         this.cleanUpPaste.defer(100, this);
25033                         return;
25034                         
25035                 }
25036                 if(cmd){
25037                     this.win.focus();
25038                     this.execCmd(cmd);
25039                     this.deferFocus();
25040                     e.preventDefault();
25041                 }
25042                 
25043             }
25044         }
25045     },
25046
25047     // private
25048     fixKeys : function(){ // load time branching for fastest keydown performance
25049         if(Roo.isIE){
25050             return function(e){
25051                 var k = e.getKey(), r;
25052                 if(k == e.TAB){
25053                     e.stopEvent();
25054                     r = this.doc.selection.createRange();
25055                     if(r){
25056                         r.collapse(true);
25057                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25058                         this.deferFocus();
25059                     }
25060                     return;
25061                 }
25062                 
25063                 if(k == e.ENTER){
25064                     r = this.doc.selection.createRange();
25065                     if(r){
25066                         var target = r.parentElement();
25067                         if(!target || target.tagName.toLowerCase() != 'li'){
25068                             e.stopEvent();
25069                             r.pasteHTML('<br />');
25070                             r.collapse(false);
25071                             r.select();
25072                         }
25073                     }
25074                 }
25075                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25076                     this.cleanUpPaste.defer(100, this);
25077                     return;
25078                 }
25079                 
25080                 
25081             };
25082         }else if(Roo.isOpera){
25083             return function(e){
25084                 var k = e.getKey();
25085                 if(k == e.TAB){
25086                     e.stopEvent();
25087                     this.win.focus();
25088                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25089                     this.deferFocus();
25090                 }
25091                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25092                     this.cleanUpPaste.defer(100, this);
25093                     return;
25094                 }
25095                 
25096             };
25097         }else if(Roo.isSafari){
25098             return function(e){
25099                 var k = e.getKey();
25100                 
25101                 if(k == e.TAB){
25102                     e.stopEvent();
25103                     this.execCmd('InsertText','\t');
25104                     this.deferFocus();
25105                     return;
25106                 }
25107                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25108                     this.cleanUpPaste.defer(100, this);
25109                     return;
25110                 }
25111                 
25112              };
25113         }
25114     }(),
25115     
25116     getAllAncestors: function()
25117     {
25118         var p = this.getSelectedNode();
25119         var a = [];
25120         if (!p) {
25121             a.push(p); // push blank onto stack..
25122             p = this.getParentElement();
25123         }
25124         
25125         
25126         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25127             a.push(p);
25128             p = p.parentNode;
25129         }
25130         a.push(this.doc.body);
25131         return a;
25132     },
25133     lastSel : false,
25134     lastSelNode : false,
25135     
25136     
25137     getSelection : function() 
25138     {
25139         this.assignDocWin();
25140         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25141     },
25142     
25143     getSelectedNode: function() 
25144     {
25145         // this may only work on Gecko!!!
25146         
25147         // should we cache this!!!!
25148         
25149         
25150         
25151          
25152         var range = this.createRange(this.getSelection()).cloneRange();
25153         
25154         if (Roo.isIE) {
25155             var parent = range.parentElement();
25156             while (true) {
25157                 var testRange = range.duplicate();
25158                 testRange.moveToElementText(parent);
25159                 if (testRange.inRange(range)) {
25160                     break;
25161                 }
25162                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25163                     break;
25164                 }
25165                 parent = parent.parentElement;
25166             }
25167             return parent;
25168         }
25169         
25170         // is ancestor a text element.
25171         var ac =  range.commonAncestorContainer;
25172         if (ac.nodeType == 3) {
25173             ac = ac.parentNode;
25174         }
25175         
25176         var ar = ac.childNodes;
25177          
25178         var nodes = [];
25179         var other_nodes = [];
25180         var has_other_nodes = false;
25181         for (var i=0;i<ar.length;i++) {
25182             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25183                 continue;
25184             }
25185             // fullly contained node.
25186             
25187             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25188                 nodes.push(ar[i]);
25189                 continue;
25190             }
25191             
25192             // probably selected..
25193             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25194                 other_nodes.push(ar[i]);
25195                 continue;
25196             }
25197             // outer..
25198             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25199                 continue;
25200             }
25201             
25202             
25203             has_other_nodes = true;
25204         }
25205         if (!nodes.length && other_nodes.length) {
25206             nodes= other_nodes;
25207         }
25208         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25209             return false;
25210         }
25211         
25212         return nodes[0];
25213     },
25214     createRange: function(sel)
25215     {
25216         // this has strange effects when using with 
25217         // top toolbar - not sure if it's a great idea.
25218         //this.editor.contentWindow.focus();
25219         if (typeof sel != "undefined") {
25220             try {
25221                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25222             } catch(e) {
25223                 return this.doc.createRange();
25224             }
25225         } else {
25226             return this.doc.createRange();
25227         }
25228     },
25229     getParentElement: function()
25230     {
25231         
25232         this.assignDocWin();
25233         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25234         
25235         var range = this.createRange(sel);
25236          
25237         try {
25238             var p = range.commonAncestorContainer;
25239             while (p.nodeType == 3) { // text node
25240                 p = p.parentNode;
25241             }
25242             return p;
25243         } catch (e) {
25244             return null;
25245         }
25246     
25247     },
25248     /***
25249      *
25250      * Range intersection.. the hard stuff...
25251      *  '-1' = before
25252      *  '0' = hits..
25253      *  '1' = after.
25254      *         [ -- selected range --- ]
25255      *   [fail]                        [fail]
25256      *
25257      *    basically..
25258      *      if end is before start or  hits it. fail.
25259      *      if start is after end or hits it fail.
25260      *
25261      *   if either hits (but other is outside. - then it's not 
25262      *   
25263      *    
25264      **/
25265     
25266     
25267     // @see http://www.thismuchiknow.co.uk/?p=64.
25268     rangeIntersectsNode : function(range, node)
25269     {
25270         var nodeRange = node.ownerDocument.createRange();
25271         try {
25272             nodeRange.selectNode(node);
25273         } catch (e) {
25274             nodeRange.selectNodeContents(node);
25275         }
25276     
25277         var rangeStartRange = range.cloneRange();
25278         rangeStartRange.collapse(true);
25279     
25280         var rangeEndRange = range.cloneRange();
25281         rangeEndRange.collapse(false);
25282     
25283         var nodeStartRange = nodeRange.cloneRange();
25284         nodeStartRange.collapse(true);
25285     
25286         var nodeEndRange = nodeRange.cloneRange();
25287         nodeEndRange.collapse(false);
25288     
25289         return rangeStartRange.compareBoundaryPoints(
25290                  Range.START_TO_START, nodeEndRange) == -1 &&
25291                rangeEndRange.compareBoundaryPoints(
25292                  Range.START_TO_START, nodeStartRange) == 1;
25293         
25294          
25295     },
25296     rangeCompareNode : function(range, node)
25297     {
25298         var nodeRange = node.ownerDocument.createRange();
25299         try {
25300             nodeRange.selectNode(node);
25301         } catch (e) {
25302             nodeRange.selectNodeContents(node);
25303         }
25304         
25305         
25306         range.collapse(true);
25307     
25308         nodeRange.collapse(true);
25309      
25310         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25311         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25312          
25313         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25314         
25315         var nodeIsBefore   =  ss == 1;
25316         var nodeIsAfter    = ee == -1;
25317         
25318         if (nodeIsBefore && nodeIsAfter) {
25319             return 0; // outer
25320         }
25321         if (!nodeIsBefore && nodeIsAfter) {
25322             return 1; //right trailed.
25323         }
25324         
25325         if (nodeIsBefore && !nodeIsAfter) {
25326             return 2;  // left trailed.
25327         }
25328         // fully contined.
25329         return 3;
25330     },
25331
25332     // private? - in a new class?
25333     cleanUpPaste :  function()
25334     {
25335         // cleans up the whole document..
25336         Roo.log('cleanuppaste');
25337         
25338         this.cleanUpChildren(this.doc.body);
25339         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25340         if (clean != this.doc.body.innerHTML) {
25341             this.doc.body.innerHTML = clean;
25342         }
25343         
25344     },
25345     
25346     cleanWordChars : function(input) {// change the chars to hex code
25347         var he = Roo.HtmlEditorCore;
25348         
25349         var output = input;
25350         Roo.each(he.swapCodes, function(sw) { 
25351             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25352             
25353             output = output.replace(swapper, sw[1]);
25354         });
25355         
25356         return output;
25357     },
25358     
25359     
25360     cleanUpChildren : function (n)
25361     {
25362         if (!n.childNodes.length) {
25363             return;
25364         }
25365         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25366            this.cleanUpChild(n.childNodes[i]);
25367         }
25368     },
25369     
25370     
25371         
25372     
25373     cleanUpChild : function (node)
25374     {
25375         var ed = this;
25376         //console.log(node);
25377         if (node.nodeName == "#text") {
25378             // clean up silly Windows -- stuff?
25379             return; 
25380         }
25381         if (node.nodeName == "#comment") {
25382             node.parentNode.removeChild(node);
25383             // clean up silly Windows -- stuff?
25384             return; 
25385         }
25386         var lcname = node.tagName.toLowerCase();
25387         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25388         // whitelist of tags..
25389         
25390         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25391             // remove node.
25392             node.parentNode.removeChild(node);
25393             return;
25394             
25395         }
25396         
25397         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25398         
25399         // spans with no attributes - just remove them..
25400         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25401             remove_keep_children = true;
25402         }
25403         
25404         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25405         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25406         
25407         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25408         //    remove_keep_children = true;
25409         //}
25410         
25411         if (remove_keep_children) {
25412             this.cleanUpChildren(node);
25413             // inserts everything just before this node...
25414             while (node.childNodes.length) {
25415                 var cn = node.childNodes[0];
25416                 node.removeChild(cn);
25417                 node.parentNode.insertBefore(cn, node);
25418             }
25419             node.parentNode.removeChild(node);
25420             return;
25421         }
25422         
25423         if (!node.attributes || !node.attributes.length) {
25424             
25425           
25426             
25427             
25428             this.cleanUpChildren(node);
25429             return;
25430         }
25431         
25432         function cleanAttr(n,v)
25433         {
25434             
25435             if (v.match(/^\./) || v.match(/^\//)) {
25436                 return;
25437             }
25438             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25439                 return;
25440             }
25441             if (v.match(/^#/)) {
25442                 return;
25443             }
25444             if (v.match(/^\{/)) { // allow template editing.
25445                 return;
25446             }
25447 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25448             node.removeAttribute(n);
25449             
25450         }
25451         
25452         var cwhite = this.cwhite;
25453         var cblack = this.cblack;
25454             
25455         function cleanStyle(n,v)
25456         {
25457             if (v.match(/expression/)) { //XSS?? should we even bother..
25458                 node.removeAttribute(n);
25459                 return;
25460             }
25461             
25462             var parts = v.split(/;/);
25463             var clean = [];
25464             
25465             Roo.each(parts, function(p) {
25466                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25467                 if (!p.length) {
25468                     return true;
25469                 }
25470                 var l = p.split(':').shift().replace(/\s+/g,'');
25471                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25472                 
25473                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25474 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25475                     //node.removeAttribute(n);
25476                     return true;
25477                 }
25478                 //Roo.log()
25479                 // only allow 'c whitelisted system attributes'
25480                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25481 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25482                     //node.removeAttribute(n);
25483                     return true;
25484                 }
25485                 
25486                 
25487                  
25488                 
25489                 clean.push(p);
25490                 return true;
25491             });
25492             if (clean.length) { 
25493                 node.setAttribute(n, clean.join(';'));
25494             } else {
25495                 node.removeAttribute(n);
25496             }
25497             
25498         }
25499         
25500         
25501         for (var i = node.attributes.length-1; i > -1 ; i--) {
25502             var a = node.attributes[i];
25503             //console.log(a);
25504             
25505             if (a.name.toLowerCase().substr(0,2)=='on')  {
25506                 node.removeAttribute(a.name);
25507                 continue;
25508             }
25509             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25510                 node.removeAttribute(a.name);
25511                 continue;
25512             }
25513             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25514                 cleanAttr(a.name,a.value); // fixme..
25515                 continue;
25516             }
25517             if (a.name == 'style') {
25518                 cleanStyle(a.name,a.value);
25519                 continue;
25520             }
25521             /// clean up MS crap..
25522             // tecnically this should be a list of valid class'es..
25523             
25524             
25525             if (a.name == 'class') {
25526                 if (a.value.match(/^Mso/)) {
25527                     node.removeAttribute('class');
25528                 }
25529                 
25530                 if (a.value.match(/^body$/)) {
25531                     node.removeAttribute('class');
25532                 }
25533                 continue;
25534             }
25535             
25536             // style cleanup!?
25537             // class cleanup?
25538             
25539         }
25540         
25541         
25542         this.cleanUpChildren(node);
25543         
25544         
25545     },
25546     
25547     /**
25548      * Clean up MS wordisms...
25549      */
25550     cleanWord : function(node)
25551     {
25552         if (!node) {
25553             this.cleanWord(this.doc.body);
25554             return;
25555         }
25556         
25557         if(
25558                 node.nodeName == 'SPAN' &&
25559                 !node.hasAttributes() &&
25560                 node.childNodes.length == 1 &&
25561                 node.firstChild.nodeName == "#text"  
25562         ) {
25563             var textNode = node.firstChild;
25564             node.removeChild(textNode);
25565             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25566                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25567             }
25568             node.parentNode.insertBefore(textNode, node);
25569             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25570                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25571             }
25572             node.parentNode.removeChild(node);
25573         }
25574         
25575         if (node.nodeName == "#text") {
25576             // clean up silly Windows -- stuff?
25577             return; 
25578         }
25579         if (node.nodeName == "#comment") {
25580             node.parentNode.removeChild(node);
25581             // clean up silly Windows -- stuff?
25582             return; 
25583         }
25584         
25585         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25586             node.parentNode.removeChild(node);
25587             return;
25588         }
25589         //Roo.log(node.tagName);
25590         // remove - but keep children..
25591         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25592             //Roo.log('-- removed');
25593             while (node.childNodes.length) {
25594                 var cn = node.childNodes[0];
25595                 node.removeChild(cn);
25596                 node.parentNode.insertBefore(cn, node);
25597                 // move node to parent - and clean it..
25598                 this.cleanWord(cn);
25599             }
25600             node.parentNode.removeChild(node);
25601             /// no need to iterate chidlren = it's got none..
25602             //this.iterateChildren(node, this.cleanWord);
25603             return;
25604         }
25605         // clean styles
25606         if (node.className.length) {
25607             
25608             var cn = node.className.split(/\W+/);
25609             var cna = [];
25610             Roo.each(cn, function(cls) {
25611                 if (cls.match(/Mso[a-zA-Z]+/)) {
25612                     return;
25613                 }
25614                 cna.push(cls);
25615             });
25616             node.className = cna.length ? cna.join(' ') : '';
25617             if (!cna.length) {
25618                 node.removeAttribute("class");
25619             }
25620         }
25621         
25622         if (node.hasAttribute("lang")) {
25623             node.removeAttribute("lang");
25624         }
25625         
25626         if (node.hasAttribute("style")) {
25627             
25628             var styles = node.getAttribute("style").split(";");
25629             var nstyle = [];
25630             Roo.each(styles, function(s) {
25631                 if (!s.match(/:/)) {
25632                     return;
25633                 }
25634                 var kv = s.split(":");
25635                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25636                     return;
25637                 }
25638                 // what ever is left... we allow.
25639                 nstyle.push(s);
25640             });
25641             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25642             if (!nstyle.length) {
25643                 node.removeAttribute('style');
25644             }
25645         }
25646         this.iterateChildren(node, this.cleanWord);
25647         
25648         
25649         
25650     },
25651     /**
25652      * iterateChildren of a Node, calling fn each time, using this as the scole..
25653      * @param {DomNode} node node to iterate children of.
25654      * @param {Function} fn method of this class to call on each item.
25655      */
25656     iterateChildren : function(node, fn)
25657     {
25658         if (!node.childNodes.length) {
25659                 return;
25660         }
25661         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25662            fn.call(this, node.childNodes[i])
25663         }
25664     },
25665     
25666     
25667     /**
25668      * cleanTableWidths.
25669      *
25670      * Quite often pasting from word etc.. results in tables with column and widths.
25671      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25672      *
25673      */
25674     cleanTableWidths : function(node)
25675     {
25676          
25677          
25678         if (!node) {
25679             this.cleanTableWidths(this.doc.body);
25680             return;
25681         }
25682         
25683         // ignore list...
25684         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25685             return; 
25686         }
25687         Roo.log(node.tagName);
25688         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25689             this.iterateChildren(node, this.cleanTableWidths);
25690             return;
25691         }
25692         if (node.hasAttribute('width')) {
25693             node.removeAttribute('width');
25694         }
25695         
25696          
25697         if (node.hasAttribute("style")) {
25698             // pretty basic...
25699             
25700             var styles = node.getAttribute("style").split(";");
25701             var nstyle = [];
25702             Roo.each(styles, function(s) {
25703                 if (!s.match(/:/)) {
25704                     return;
25705                 }
25706                 var kv = s.split(":");
25707                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25708                     return;
25709                 }
25710                 // what ever is left... we allow.
25711                 nstyle.push(s);
25712             });
25713             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25714             if (!nstyle.length) {
25715                 node.removeAttribute('style');
25716             }
25717         }
25718         
25719         this.iterateChildren(node, this.cleanTableWidths);
25720         
25721         
25722     },
25723     
25724     
25725     
25726     
25727     domToHTML : function(currentElement, depth, nopadtext) {
25728         
25729         depth = depth || 0;
25730         nopadtext = nopadtext || false;
25731     
25732         if (!currentElement) {
25733             return this.domToHTML(this.doc.body);
25734         }
25735         
25736         //Roo.log(currentElement);
25737         var j;
25738         var allText = false;
25739         var nodeName = currentElement.nodeName;
25740         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25741         
25742         if  (nodeName == '#text') {
25743             
25744             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25745         }
25746         
25747         
25748         var ret = '';
25749         if (nodeName != 'BODY') {
25750              
25751             var i = 0;
25752             // Prints the node tagName, such as <A>, <IMG>, etc
25753             if (tagName) {
25754                 var attr = [];
25755                 for(i = 0; i < currentElement.attributes.length;i++) {
25756                     // quoting?
25757                     var aname = currentElement.attributes.item(i).name;
25758                     if (!currentElement.attributes.item(i).value.length) {
25759                         continue;
25760                     }
25761                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25762                 }
25763                 
25764                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25765             } 
25766             else {
25767                 
25768                 // eack
25769             }
25770         } else {
25771             tagName = false;
25772         }
25773         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25774             return ret;
25775         }
25776         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25777             nopadtext = true;
25778         }
25779         
25780         
25781         // Traverse the tree
25782         i = 0;
25783         var currentElementChild = currentElement.childNodes.item(i);
25784         var allText = true;
25785         var innerHTML  = '';
25786         lastnode = '';
25787         while (currentElementChild) {
25788             // Formatting code (indent the tree so it looks nice on the screen)
25789             var nopad = nopadtext;
25790             if (lastnode == 'SPAN') {
25791                 nopad  = true;
25792             }
25793             // text
25794             if  (currentElementChild.nodeName == '#text') {
25795                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25796                 toadd = nopadtext ? toadd : toadd.trim();
25797                 if (!nopad && toadd.length > 80) {
25798                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25799                 }
25800                 innerHTML  += toadd;
25801                 
25802                 i++;
25803                 currentElementChild = currentElement.childNodes.item(i);
25804                 lastNode = '';
25805                 continue;
25806             }
25807             allText = false;
25808             
25809             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25810                 
25811             // Recursively traverse the tree structure of the child node
25812             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25813             lastnode = currentElementChild.nodeName;
25814             i++;
25815             currentElementChild=currentElement.childNodes.item(i);
25816         }
25817         
25818         ret += innerHTML;
25819         
25820         if (!allText) {
25821                 // The remaining code is mostly for formatting the tree
25822             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25823         }
25824         
25825         
25826         if (tagName) {
25827             ret+= "</"+tagName+">";
25828         }
25829         return ret;
25830         
25831     },
25832         
25833     applyBlacklists : function()
25834     {
25835         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25836         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25837         
25838         this.white = [];
25839         this.black = [];
25840         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25841             if (b.indexOf(tag) > -1) {
25842                 return;
25843             }
25844             this.white.push(tag);
25845             
25846         }, this);
25847         
25848         Roo.each(w, function(tag) {
25849             if (b.indexOf(tag) > -1) {
25850                 return;
25851             }
25852             if (this.white.indexOf(tag) > -1) {
25853                 return;
25854             }
25855             this.white.push(tag);
25856             
25857         }, this);
25858         
25859         
25860         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25861             if (w.indexOf(tag) > -1) {
25862                 return;
25863             }
25864             this.black.push(tag);
25865             
25866         }, this);
25867         
25868         Roo.each(b, function(tag) {
25869             if (w.indexOf(tag) > -1) {
25870                 return;
25871             }
25872             if (this.black.indexOf(tag) > -1) {
25873                 return;
25874             }
25875             this.black.push(tag);
25876             
25877         }, this);
25878         
25879         
25880         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25881         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25882         
25883         this.cwhite = [];
25884         this.cblack = [];
25885         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25886             if (b.indexOf(tag) > -1) {
25887                 return;
25888             }
25889             this.cwhite.push(tag);
25890             
25891         }, this);
25892         
25893         Roo.each(w, function(tag) {
25894             if (b.indexOf(tag) > -1) {
25895                 return;
25896             }
25897             if (this.cwhite.indexOf(tag) > -1) {
25898                 return;
25899             }
25900             this.cwhite.push(tag);
25901             
25902         }, this);
25903         
25904         
25905         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25906             if (w.indexOf(tag) > -1) {
25907                 return;
25908             }
25909             this.cblack.push(tag);
25910             
25911         }, this);
25912         
25913         Roo.each(b, function(tag) {
25914             if (w.indexOf(tag) > -1) {
25915                 return;
25916             }
25917             if (this.cblack.indexOf(tag) > -1) {
25918                 return;
25919             }
25920             this.cblack.push(tag);
25921             
25922         }, this);
25923     },
25924     
25925     setStylesheets : function(stylesheets)
25926     {
25927         if(typeof(stylesheets) == 'string'){
25928             Roo.get(this.iframe.contentDocument.head).createChild({
25929                 tag : 'link',
25930                 rel : 'stylesheet',
25931                 type : 'text/css',
25932                 href : stylesheets
25933             });
25934             
25935             return;
25936         }
25937         var _this = this;
25938      
25939         Roo.each(stylesheets, function(s) {
25940             if(!s.length){
25941                 return;
25942             }
25943             
25944             Roo.get(_this.iframe.contentDocument.head).createChild({
25945                 tag : 'link',
25946                 rel : 'stylesheet',
25947                 type : 'text/css',
25948                 href : s
25949             });
25950         });
25951
25952         
25953     },
25954     
25955     removeStylesheets : function()
25956     {
25957         var _this = this;
25958         
25959         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25960             s.remove();
25961         });
25962     },
25963     
25964     setStyle : function(style)
25965     {
25966         Roo.get(this.iframe.contentDocument.head).createChild({
25967             tag : 'style',
25968             type : 'text/css',
25969             html : style
25970         });
25971
25972         return;
25973     }
25974     
25975     // hide stuff that is not compatible
25976     /**
25977      * @event blur
25978      * @hide
25979      */
25980     /**
25981      * @event change
25982      * @hide
25983      */
25984     /**
25985      * @event focus
25986      * @hide
25987      */
25988     /**
25989      * @event specialkey
25990      * @hide
25991      */
25992     /**
25993      * @cfg {String} fieldClass @hide
25994      */
25995     /**
25996      * @cfg {String} focusClass @hide
25997      */
25998     /**
25999      * @cfg {String} autoCreate @hide
26000      */
26001     /**
26002      * @cfg {String} inputType @hide
26003      */
26004     /**
26005      * @cfg {String} invalidClass @hide
26006      */
26007     /**
26008      * @cfg {String} invalidText @hide
26009      */
26010     /**
26011      * @cfg {String} msgFx @hide
26012      */
26013     /**
26014      * @cfg {String} validateOnBlur @hide
26015      */
26016 });
26017
26018 Roo.HtmlEditorCore.white = [
26019         'area', 'br', 'img', 'input', 'hr', 'wbr',
26020         
26021        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26022        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26023        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26024        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26025        'table',   'ul',         'xmp', 
26026        
26027        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26028       'thead',   'tr', 
26029      
26030       'dir', 'menu', 'ol', 'ul', 'dl',
26031        
26032       'embed',  'object'
26033 ];
26034
26035
26036 Roo.HtmlEditorCore.black = [
26037     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26038         'applet', // 
26039         'base',   'basefont', 'bgsound', 'blink',  'body', 
26040         'frame',  'frameset', 'head',    'html',   'ilayer', 
26041         'iframe', 'layer',  'link',     'meta',    'object',   
26042         'script', 'style' ,'title',  'xml' // clean later..
26043 ];
26044 Roo.HtmlEditorCore.clean = [
26045     'script', 'style', 'title', 'xml'
26046 ];
26047 Roo.HtmlEditorCore.remove = [
26048     'font'
26049 ];
26050 // attributes..
26051
26052 Roo.HtmlEditorCore.ablack = [
26053     'on'
26054 ];
26055     
26056 Roo.HtmlEditorCore.aclean = [ 
26057     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26058 ];
26059
26060 // protocols..
26061 Roo.HtmlEditorCore.pwhite= [
26062         'http',  'https',  'mailto'
26063 ];
26064
26065 // white listed style attributes.
26066 Roo.HtmlEditorCore.cwhite= [
26067       //  'text-align', /// default is to allow most things..
26068       
26069          
26070 //        'font-size'//??
26071 ];
26072
26073 // black listed style attributes.
26074 Roo.HtmlEditorCore.cblack= [
26075       //  'font-size' -- this can be set by the project 
26076 ];
26077
26078
26079 Roo.HtmlEditorCore.swapCodes   =[ 
26080     [    8211, "&#8211;" ], 
26081     [    8212, "&#8212;" ], 
26082     [    8216,  "'" ],  
26083     [    8217, "'" ],  
26084     [    8220, '"' ],  
26085     [    8221, '"' ],  
26086     [    8226, "*" ],  
26087     [    8230, "..." ]
26088 ]; 
26089
26090     /*
26091  * - LGPL
26092  *
26093  * HtmlEditor
26094  * 
26095  */
26096
26097 /**
26098  * @class Roo.bootstrap.HtmlEditor
26099  * @extends Roo.bootstrap.TextArea
26100  * Bootstrap HtmlEditor class
26101
26102  * @constructor
26103  * Create a new HtmlEditor
26104  * @param {Object} config The config object
26105  */
26106
26107 Roo.bootstrap.HtmlEditor = function(config){
26108     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26109     if (!this.toolbars) {
26110         this.toolbars = [];
26111     }
26112     
26113     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26114     this.addEvents({
26115             /**
26116              * @event initialize
26117              * Fires when the editor is fully initialized (including the iframe)
26118              * @param {HtmlEditor} this
26119              */
26120             initialize: true,
26121             /**
26122              * @event activate
26123              * Fires when the editor is first receives the focus. Any insertion must wait
26124              * until after this event.
26125              * @param {HtmlEditor} this
26126              */
26127             activate: true,
26128              /**
26129              * @event beforesync
26130              * Fires before the textarea is updated with content from the editor iframe. Return false
26131              * to cancel the sync.
26132              * @param {HtmlEditor} this
26133              * @param {String} html
26134              */
26135             beforesync: true,
26136              /**
26137              * @event beforepush
26138              * Fires before the iframe editor is updated with content from the textarea. Return false
26139              * to cancel the push.
26140              * @param {HtmlEditor} this
26141              * @param {String} html
26142              */
26143             beforepush: true,
26144              /**
26145              * @event sync
26146              * Fires when the textarea is updated with content from the editor iframe.
26147              * @param {HtmlEditor} this
26148              * @param {String} html
26149              */
26150             sync: true,
26151              /**
26152              * @event push
26153              * Fires when the iframe editor is updated with content from the textarea.
26154              * @param {HtmlEditor} this
26155              * @param {String} html
26156              */
26157             push: true,
26158              /**
26159              * @event editmodechange
26160              * Fires when the editor switches edit modes
26161              * @param {HtmlEditor} this
26162              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26163              */
26164             editmodechange: true,
26165             /**
26166              * @event editorevent
26167              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26168              * @param {HtmlEditor} this
26169              */
26170             editorevent: true,
26171             /**
26172              * @event firstfocus
26173              * Fires when on first focus - needed by toolbars..
26174              * @param {HtmlEditor} this
26175              */
26176             firstfocus: true,
26177             /**
26178              * @event autosave
26179              * Auto save the htmlEditor value as a file into Events
26180              * @param {HtmlEditor} this
26181              */
26182             autosave: true,
26183             /**
26184              * @event savedpreview
26185              * preview the saved version of htmlEditor
26186              * @param {HtmlEditor} this
26187              */
26188             savedpreview: true
26189         });
26190 };
26191
26192
26193 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26194     
26195     
26196       /**
26197      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26198      */
26199     toolbars : false,
26200     
26201      /**
26202     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26203     */
26204     btns : [],
26205    
26206      /**
26207      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26208      *                        Roo.resizable.
26209      */
26210     resizable : false,
26211      /**
26212      * @cfg {Number} height (in pixels)
26213      */   
26214     height: 300,
26215    /**
26216      * @cfg {Number} width (in pixels)
26217      */   
26218     width: false,
26219     
26220     /**
26221      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26222      * 
26223      */
26224     stylesheets: false,
26225     
26226     // id of frame..
26227     frameId: false,
26228     
26229     // private properties
26230     validationEvent : false,
26231     deferHeight: true,
26232     initialized : false,
26233     activated : false,
26234     
26235     onFocus : Roo.emptyFn,
26236     iframePad:3,
26237     hideMode:'offsets',
26238     
26239     tbContainer : false,
26240     
26241     bodyCls : '',
26242     
26243     toolbarContainer :function() {
26244         return this.wrap.select('.x-html-editor-tb',true).first();
26245     },
26246
26247     /**
26248      * Protected method that will not generally be called directly. It
26249      * is called when the editor creates its toolbar. Override this method if you need to
26250      * add custom toolbar buttons.
26251      * @param {HtmlEditor} editor
26252      */
26253     createToolbar : function(){
26254         Roo.log('renewing');
26255         Roo.log("create toolbars");
26256         
26257         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26258         this.toolbars[0].render(this.toolbarContainer());
26259         
26260         return;
26261         
26262 //        if (!editor.toolbars || !editor.toolbars.length) {
26263 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26264 //        }
26265 //        
26266 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26267 //            editor.toolbars[i] = Roo.factory(
26268 //                    typeof(editor.toolbars[i]) == 'string' ?
26269 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26270 //                Roo.bootstrap.HtmlEditor);
26271 //            editor.toolbars[i].init(editor);
26272 //        }
26273     },
26274
26275      
26276     // private
26277     onRender : function(ct, position)
26278     {
26279        // Roo.log("Call onRender: " + this.xtype);
26280         var _t = this;
26281         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26282       
26283         this.wrap = this.inputEl().wrap({
26284             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26285         });
26286         
26287         this.editorcore.onRender(ct, position);
26288          
26289         if (this.resizable) {
26290             this.resizeEl = new Roo.Resizable(this.wrap, {
26291                 pinned : true,
26292                 wrap: true,
26293                 dynamic : true,
26294                 minHeight : this.height,
26295                 height: this.height,
26296                 handles : this.resizable,
26297                 width: this.width,
26298                 listeners : {
26299                     resize : function(r, w, h) {
26300                         _t.onResize(w,h); // -something
26301                     }
26302                 }
26303             });
26304             
26305         }
26306         this.createToolbar(this);
26307        
26308         
26309         if(!this.width && this.resizable){
26310             this.setSize(this.wrap.getSize());
26311         }
26312         if (this.resizeEl) {
26313             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26314             // should trigger onReize..
26315         }
26316         
26317     },
26318
26319     // private
26320     onResize : function(w, h)
26321     {
26322         Roo.log('resize: ' +w + ',' + h );
26323         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26324         var ew = false;
26325         var eh = false;
26326         
26327         if(this.inputEl() ){
26328             if(typeof w == 'number'){
26329                 var aw = w - this.wrap.getFrameWidth('lr');
26330                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26331                 ew = aw;
26332             }
26333             if(typeof h == 'number'){
26334                  var tbh = -11;  // fixme it needs to tool bar size!
26335                 for (var i =0; i < this.toolbars.length;i++) {
26336                     // fixme - ask toolbars for heights?
26337                     tbh += this.toolbars[i].el.getHeight();
26338                     //if (this.toolbars[i].footer) {
26339                     //    tbh += this.toolbars[i].footer.el.getHeight();
26340                     //}
26341                 }
26342               
26343                 
26344                 
26345                 
26346                 
26347                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26348                 ah -= 5; // knock a few pixes off for look..
26349                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26350                 var eh = ah;
26351             }
26352         }
26353         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26354         this.editorcore.onResize(ew,eh);
26355         
26356     },
26357
26358     /**
26359      * Toggles the editor between standard and source edit mode.
26360      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26361      */
26362     toggleSourceEdit : function(sourceEditMode)
26363     {
26364         this.editorcore.toggleSourceEdit(sourceEditMode);
26365         
26366         if(this.editorcore.sourceEditMode){
26367             Roo.log('editor - showing textarea');
26368             
26369 //            Roo.log('in');
26370 //            Roo.log(this.syncValue());
26371             this.syncValue();
26372             this.inputEl().removeClass(['hide', 'x-hidden']);
26373             this.inputEl().dom.removeAttribute('tabIndex');
26374             this.inputEl().focus();
26375         }else{
26376             Roo.log('editor - hiding textarea');
26377 //            Roo.log('out')
26378 //            Roo.log(this.pushValue()); 
26379             this.pushValue();
26380             
26381             this.inputEl().addClass(['hide', 'x-hidden']);
26382             this.inputEl().dom.setAttribute('tabIndex', -1);
26383             //this.deferFocus();
26384         }
26385          
26386         if(this.resizable){
26387             this.setSize(this.wrap.getSize());
26388         }
26389         
26390         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26391     },
26392  
26393     // private (for BoxComponent)
26394     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26395
26396     // private (for BoxComponent)
26397     getResizeEl : function(){
26398         return this.wrap;
26399     },
26400
26401     // private (for BoxComponent)
26402     getPositionEl : function(){
26403         return this.wrap;
26404     },
26405
26406     // private
26407     initEvents : function(){
26408         this.originalValue = this.getValue();
26409     },
26410
26411 //    /**
26412 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26413 //     * @method
26414 //     */
26415 //    markInvalid : Roo.emptyFn,
26416 //    /**
26417 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26418 //     * @method
26419 //     */
26420 //    clearInvalid : Roo.emptyFn,
26421
26422     setValue : function(v){
26423         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26424         this.editorcore.pushValue();
26425     },
26426
26427      
26428     // private
26429     deferFocus : function(){
26430         this.focus.defer(10, this);
26431     },
26432
26433     // doc'ed in Field
26434     focus : function(){
26435         this.editorcore.focus();
26436         
26437     },
26438       
26439
26440     // private
26441     onDestroy : function(){
26442         
26443         
26444         
26445         if(this.rendered){
26446             
26447             for (var i =0; i < this.toolbars.length;i++) {
26448                 // fixme - ask toolbars for heights?
26449                 this.toolbars[i].onDestroy();
26450             }
26451             
26452             this.wrap.dom.innerHTML = '';
26453             this.wrap.remove();
26454         }
26455     },
26456
26457     // private
26458     onFirstFocus : function(){
26459         //Roo.log("onFirstFocus");
26460         this.editorcore.onFirstFocus();
26461          for (var i =0; i < this.toolbars.length;i++) {
26462             this.toolbars[i].onFirstFocus();
26463         }
26464         
26465     },
26466     
26467     // private
26468     syncValue : function()
26469     {   
26470         this.editorcore.syncValue();
26471     },
26472     
26473     pushValue : function()
26474     {   
26475         this.editorcore.pushValue();
26476     }
26477      
26478     
26479     // hide stuff that is not compatible
26480     /**
26481      * @event blur
26482      * @hide
26483      */
26484     /**
26485      * @event change
26486      * @hide
26487      */
26488     /**
26489      * @event focus
26490      * @hide
26491      */
26492     /**
26493      * @event specialkey
26494      * @hide
26495      */
26496     /**
26497      * @cfg {String} fieldClass @hide
26498      */
26499     /**
26500      * @cfg {String} focusClass @hide
26501      */
26502     /**
26503      * @cfg {String} autoCreate @hide
26504      */
26505     /**
26506      * @cfg {String} inputType @hide
26507      */
26508      
26509     /**
26510      * @cfg {String} invalidText @hide
26511      */
26512     /**
26513      * @cfg {String} msgFx @hide
26514      */
26515     /**
26516      * @cfg {String} validateOnBlur @hide
26517      */
26518 });
26519  
26520     
26521    
26522    
26523    
26524       
26525 Roo.namespace('Roo.bootstrap.htmleditor');
26526 /**
26527  * @class Roo.bootstrap.HtmlEditorToolbar1
26528  * Basic Toolbar
26529  * 
26530  * @example
26531  * Usage:
26532  *
26533  new Roo.bootstrap.HtmlEditor({
26534     ....
26535     toolbars : [
26536         new Roo.bootstrap.HtmlEditorToolbar1({
26537             disable : { fonts: 1 , format: 1, ..., ... , ...],
26538             btns : [ .... ]
26539         })
26540     }
26541      
26542  * 
26543  * @cfg {Object} disable List of elements to disable..
26544  * @cfg {Array} btns List of additional buttons.
26545  * 
26546  * 
26547  * NEEDS Extra CSS? 
26548  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26549  */
26550  
26551 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26552 {
26553     
26554     Roo.apply(this, config);
26555     
26556     // default disabled, based on 'good practice'..
26557     this.disable = this.disable || {};
26558     Roo.applyIf(this.disable, {
26559         fontSize : true,
26560         colors : true,
26561         specialElements : true
26562     });
26563     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26564     
26565     this.editor = config.editor;
26566     this.editorcore = config.editor.editorcore;
26567     
26568     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26569     
26570     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26571     // dont call parent... till later.
26572 }
26573 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26574      
26575     bar : true,
26576     
26577     editor : false,
26578     editorcore : false,
26579     
26580     
26581     formats : [
26582         "p" ,  
26583         "h1","h2","h3","h4","h5","h6", 
26584         "pre", "code", 
26585         "abbr", "acronym", "address", "cite", "samp", "var",
26586         'div','span'
26587     ],
26588     
26589     onRender : function(ct, position)
26590     {
26591        // Roo.log("Call onRender: " + this.xtype);
26592         
26593        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26594        Roo.log(this.el);
26595        this.el.dom.style.marginBottom = '0';
26596        var _this = this;
26597        var editorcore = this.editorcore;
26598        var editor= this.editor;
26599        
26600        var children = [];
26601        var btn = function(id,cmd , toggle, handler, html){
26602        
26603             var  event = toggle ? 'toggle' : 'click';
26604        
26605             var a = {
26606                 size : 'sm',
26607                 xtype: 'Button',
26608                 xns: Roo.bootstrap,
26609                 //glyphicon : id,
26610                 fa: id,
26611                 cmd : id || cmd,
26612                 enableToggle:toggle !== false,
26613                 html : html || '',
26614                 pressed : toggle ? false : null,
26615                 listeners : {}
26616             };
26617             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26618                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26619             };
26620             children.push(a);
26621             return a;
26622        }
26623        
26624     //    var cb_box = function...
26625         
26626         var style = {
26627                 xtype: 'Button',
26628                 size : 'sm',
26629                 xns: Roo.bootstrap,
26630                 fa : 'font',
26631                 //html : 'submit'
26632                 menu : {
26633                     xtype: 'Menu',
26634                     xns: Roo.bootstrap,
26635                     items:  []
26636                 }
26637         };
26638         Roo.each(this.formats, function(f) {
26639             style.menu.items.push({
26640                 xtype :'MenuItem',
26641                 xns: Roo.bootstrap,
26642                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26643                 tagname : f,
26644                 listeners : {
26645                     click : function()
26646                     {
26647                         editorcore.insertTag(this.tagname);
26648                         editor.focus();
26649                     }
26650                 }
26651                 
26652             });
26653         });
26654         children.push(style);   
26655         
26656         btn('bold',false,true);
26657         btn('italic',false,true);
26658         btn('align-left', 'justifyleft',true);
26659         btn('align-center', 'justifycenter',true);
26660         btn('align-right' , 'justifyright',true);
26661         btn('link', false, false, function(btn) {
26662             //Roo.log("create link?");
26663             var url = prompt(this.createLinkText, this.defaultLinkValue);
26664             if(url && url != 'http:/'+'/'){
26665                 this.editorcore.relayCmd('createlink', url);
26666             }
26667         }),
26668         btn('list','insertunorderedlist',true);
26669         btn('pencil', false,true, function(btn){
26670                 Roo.log(this);
26671                 this.toggleSourceEdit(btn.pressed);
26672         });
26673         
26674         if (this.editor.btns.length > 0) {
26675             for (var i = 0; i<this.editor.btns.length; i++) {
26676                 children.push(this.editor.btns[i]);
26677             }
26678         }
26679         
26680         /*
26681         var cog = {
26682                 xtype: 'Button',
26683                 size : 'sm',
26684                 xns: Roo.bootstrap,
26685                 glyphicon : 'cog',
26686                 //html : 'submit'
26687                 menu : {
26688                     xtype: 'Menu',
26689                     xns: Roo.bootstrap,
26690                     items:  []
26691                 }
26692         };
26693         
26694         cog.menu.items.push({
26695             xtype :'MenuItem',
26696             xns: Roo.bootstrap,
26697             html : Clean styles,
26698             tagname : f,
26699             listeners : {
26700                 click : function()
26701                 {
26702                     editorcore.insertTag(this.tagname);
26703                     editor.focus();
26704                 }
26705             }
26706             
26707         });
26708        */
26709         
26710          
26711        this.xtype = 'NavSimplebar';
26712         
26713         for(var i=0;i< children.length;i++) {
26714             
26715             this.buttons.add(this.addxtypeChild(children[i]));
26716             
26717         }
26718         
26719         editor.on('editorevent', this.updateToolbar, this);
26720     },
26721     onBtnClick : function(id)
26722     {
26723        this.editorcore.relayCmd(id);
26724        this.editorcore.focus();
26725     },
26726     
26727     /**
26728      * Protected method that will not generally be called directly. It triggers
26729      * a toolbar update by reading the markup state of the current selection in the editor.
26730      */
26731     updateToolbar: function(){
26732
26733         if(!this.editorcore.activated){
26734             this.editor.onFirstFocus(); // is this neeed?
26735             return;
26736         }
26737
26738         var btns = this.buttons; 
26739         var doc = this.editorcore.doc;
26740         btns.get('bold').setActive(doc.queryCommandState('bold'));
26741         btns.get('italic').setActive(doc.queryCommandState('italic'));
26742         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26743         
26744         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26745         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26746         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26747         
26748         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26749         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26750          /*
26751         
26752         var ans = this.editorcore.getAllAncestors();
26753         if (this.formatCombo) {
26754             
26755             
26756             var store = this.formatCombo.store;
26757             this.formatCombo.setValue("");
26758             for (var i =0; i < ans.length;i++) {
26759                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26760                     // select it..
26761                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26762                     break;
26763                 }
26764             }
26765         }
26766         
26767         
26768         
26769         // hides menus... - so this cant be on a menu...
26770         Roo.bootstrap.MenuMgr.hideAll();
26771         */
26772         Roo.bootstrap.MenuMgr.hideAll();
26773         //this.editorsyncValue();
26774     },
26775     onFirstFocus: function() {
26776         this.buttons.each(function(item){
26777            item.enable();
26778         });
26779     },
26780     toggleSourceEdit : function(sourceEditMode){
26781         
26782           
26783         if(sourceEditMode){
26784             Roo.log("disabling buttons");
26785            this.buttons.each( function(item){
26786                 if(item.cmd != 'pencil'){
26787                     item.disable();
26788                 }
26789             });
26790           
26791         }else{
26792             Roo.log("enabling buttons");
26793             if(this.editorcore.initialized){
26794                 this.buttons.each( function(item){
26795                     item.enable();
26796                 });
26797             }
26798             
26799         }
26800         Roo.log("calling toggole on editor");
26801         // tell the editor that it's been pressed..
26802         this.editor.toggleSourceEdit(sourceEditMode);
26803        
26804     }
26805 });
26806
26807
26808
26809
26810  
26811 /*
26812  * - LGPL
26813  */
26814
26815 /**
26816  * @class Roo.bootstrap.Markdown
26817  * @extends Roo.bootstrap.TextArea
26818  * Bootstrap Showdown editable area
26819  * @cfg {string} content
26820  * 
26821  * @constructor
26822  * Create a new Showdown
26823  */
26824
26825 Roo.bootstrap.Markdown = function(config){
26826     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26827    
26828 };
26829
26830 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26831     
26832     editing :false,
26833     
26834     initEvents : function()
26835     {
26836         
26837         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26838         this.markdownEl = this.el.createChild({
26839             cls : 'roo-markdown-area'
26840         });
26841         this.inputEl().addClass('d-none');
26842         if (this.getValue() == '') {
26843             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26844             
26845         } else {
26846             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26847         }
26848         this.markdownEl.on('click', this.toggleTextEdit, this);
26849         this.on('blur', this.toggleTextEdit, this);
26850         this.on('specialkey', this.resizeTextArea, this);
26851     },
26852     
26853     toggleTextEdit : function()
26854     {
26855         var sh = this.markdownEl.getHeight();
26856         this.inputEl().addClass('d-none');
26857         this.markdownEl.addClass('d-none');
26858         if (!this.editing) {
26859             // show editor?
26860             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26861             this.inputEl().removeClass('d-none');
26862             this.inputEl().focus();
26863             this.editing = true;
26864             return;
26865         }
26866         // show showdown...
26867         this.updateMarkdown();
26868         this.markdownEl.removeClass('d-none');
26869         this.editing = false;
26870         return;
26871     },
26872     updateMarkdown : function()
26873     {
26874         if (this.getValue() == '') {
26875             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26876             return;
26877         }
26878  
26879         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26880     },
26881     
26882     resizeTextArea: function () {
26883         
26884         var sh = 100;
26885         Roo.log([sh, this.getValue().split("\n").length * 30]);
26886         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26887     },
26888     setValue : function(val)
26889     {
26890         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26891         if (!this.editing) {
26892             this.updateMarkdown();
26893         }
26894         
26895     },
26896     focus : function()
26897     {
26898         if (!this.editing) {
26899             this.toggleTextEdit();
26900         }
26901         
26902     }
26903
26904
26905 });
26906 /**
26907  * @class Roo.bootstrap.Table.AbstractSelectionModel
26908  * @extends Roo.util.Observable
26909  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26910  * implemented by descendant classes.  This class should not be directly instantiated.
26911  * @constructor
26912  */
26913 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26914     this.locked = false;
26915     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26916 };
26917
26918
26919 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26920     /** @ignore Called by the grid automatically. Do not call directly. */
26921     init : function(grid){
26922         this.grid = grid;
26923         this.initEvents();
26924     },
26925
26926     /**
26927      * Locks the selections.
26928      */
26929     lock : function(){
26930         this.locked = true;
26931     },
26932
26933     /**
26934      * Unlocks the selections.
26935      */
26936     unlock : function(){
26937         this.locked = false;
26938     },
26939
26940     /**
26941      * Returns true if the selections are locked.
26942      * @return {Boolean}
26943      */
26944     isLocked : function(){
26945         return this.locked;
26946     },
26947     
26948     
26949     initEvents : function ()
26950     {
26951         
26952     }
26953 });
26954 /**
26955  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26956  * @class Roo.bootstrap.Table.RowSelectionModel
26957  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26958  * It supports multiple selections and keyboard selection/navigation. 
26959  * @constructor
26960  * @param {Object} config
26961  */
26962
26963 Roo.bootstrap.Table.RowSelectionModel = function(config){
26964     Roo.apply(this, config);
26965     this.selections = new Roo.util.MixedCollection(false, function(o){
26966         return o.id;
26967     });
26968
26969     this.last = false;
26970     this.lastActive = false;
26971
26972     this.addEvents({
26973         /**
26974              * @event selectionchange
26975              * Fires when the selection changes
26976              * @param {SelectionModel} this
26977              */
26978             "selectionchange" : true,
26979         /**
26980              * @event afterselectionchange
26981              * Fires after the selection changes (eg. by key press or clicking)
26982              * @param {SelectionModel} this
26983              */
26984             "afterselectionchange" : true,
26985         /**
26986              * @event beforerowselect
26987              * Fires when a row is selected being selected, return false to cancel.
26988              * @param {SelectionModel} this
26989              * @param {Number} rowIndex The selected index
26990              * @param {Boolean} keepExisting False if other selections will be cleared
26991              */
26992             "beforerowselect" : true,
26993         /**
26994              * @event rowselect
26995              * Fires when a row is selected.
26996              * @param {SelectionModel} this
26997              * @param {Number} rowIndex The selected index
26998              * @param {Roo.data.Record} r The record
26999              */
27000             "rowselect" : true,
27001         /**
27002              * @event rowdeselect
27003              * Fires when a row is deselected.
27004              * @param {SelectionModel} this
27005              * @param {Number} rowIndex The selected index
27006              */
27007         "rowdeselect" : true
27008     });
27009     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27010     this.locked = false;
27011  };
27012
27013 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27014     /**
27015      * @cfg {Boolean} singleSelect
27016      * True to allow selection of only one row at a time (defaults to false)
27017      */
27018     singleSelect : false,
27019
27020     // private
27021     initEvents : function()
27022     {
27023
27024         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27025         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27026         //}else{ // allow click to work like normal
27027          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27028         //}
27029         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27030         this.grid.on("rowclick", this.handleMouseDown, this);
27031         
27032         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27033             "up" : function(e){
27034                 if(!e.shiftKey){
27035                     this.selectPrevious(e.shiftKey);
27036                 }else if(this.last !== false && this.lastActive !== false){
27037                     var last = this.last;
27038                     this.selectRange(this.last,  this.lastActive-1);
27039                     this.grid.getView().focusRow(this.lastActive);
27040                     if(last !== false){
27041                         this.last = last;
27042                     }
27043                 }else{
27044                     this.selectFirstRow();
27045                 }
27046                 this.fireEvent("afterselectionchange", this);
27047             },
27048             "down" : function(e){
27049                 if(!e.shiftKey){
27050                     this.selectNext(e.shiftKey);
27051                 }else if(this.last !== false && this.lastActive !== false){
27052                     var last = this.last;
27053                     this.selectRange(this.last,  this.lastActive+1);
27054                     this.grid.getView().focusRow(this.lastActive);
27055                     if(last !== false){
27056                         this.last = last;
27057                     }
27058                 }else{
27059                     this.selectFirstRow();
27060                 }
27061                 this.fireEvent("afterselectionchange", this);
27062             },
27063             scope: this
27064         });
27065         this.grid.store.on('load', function(){
27066             this.selections.clear();
27067         },this);
27068         /*
27069         var view = this.grid.view;
27070         view.on("refresh", this.onRefresh, this);
27071         view.on("rowupdated", this.onRowUpdated, this);
27072         view.on("rowremoved", this.onRemove, this);
27073         */
27074     },
27075
27076     // private
27077     onRefresh : function()
27078     {
27079         var ds = this.grid.store, i, v = this.grid.view;
27080         var s = this.selections;
27081         s.each(function(r){
27082             if((i = ds.indexOfId(r.id)) != -1){
27083                 v.onRowSelect(i);
27084             }else{
27085                 s.remove(r);
27086             }
27087         });
27088     },
27089
27090     // private
27091     onRemove : function(v, index, r){
27092         this.selections.remove(r);
27093     },
27094
27095     // private
27096     onRowUpdated : function(v, index, r){
27097         if(this.isSelected(r)){
27098             v.onRowSelect(index);
27099         }
27100     },
27101
27102     /**
27103      * Select records.
27104      * @param {Array} records The records to select
27105      * @param {Boolean} keepExisting (optional) True to keep existing selections
27106      */
27107     selectRecords : function(records, keepExisting)
27108     {
27109         if(!keepExisting){
27110             this.clearSelections();
27111         }
27112             var ds = this.grid.store;
27113         for(var i = 0, len = records.length; i < len; i++){
27114             this.selectRow(ds.indexOf(records[i]), true);
27115         }
27116     },
27117
27118     /**
27119      * Gets the number of selected rows.
27120      * @return {Number}
27121      */
27122     getCount : function(){
27123         return this.selections.length;
27124     },
27125
27126     /**
27127      * Selects the first row in the grid.
27128      */
27129     selectFirstRow : function(){
27130         this.selectRow(0);
27131     },
27132
27133     /**
27134      * Select the last row.
27135      * @param {Boolean} keepExisting (optional) True to keep existing selections
27136      */
27137     selectLastRow : function(keepExisting){
27138         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27139         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27140     },
27141
27142     /**
27143      * Selects the row immediately following the last selected row.
27144      * @param {Boolean} keepExisting (optional) True to keep existing selections
27145      */
27146     selectNext : function(keepExisting)
27147     {
27148             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27149             this.selectRow(this.last+1, keepExisting);
27150             this.grid.getView().focusRow(this.last);
27151         }
27152     },
27153
27154     /**
27155      * Selects the row that precedes the last selected row.
27156      * @param {Boolean} keepExisting (optional) True to keep existing selections
27157      */
27158     selectPrevious : function(keepExisting){
27159         if(this.last){
27160             this.selectRow(this.last-1, keepExisting);
27161             this.grid.getView().focusRow(this.last);
27162         }
27163     },
27164
27165     /**
27166      * Returns the selected records
27167      * @return {Array} Array of selected records
27168      */
27169     getSelections : function(){
27170         return [].concat(this.selections.items);
27171     },
27172
27173     /**
27174      * Returns the first selected record.
27175      * @return {Record}
27176      */
27177     getSelected : function(){
27178         return this.selections.itemAt(0);
27179     },
27180
27181
27182     /**
27183      * Clears all selections.
27184      */
27185     clearSelections : function(fast)
27186     {
27187         if(this.locked) {
27188             return;
27189         }
27190         if(fast !== true){
27191                 var ds = this.grid.store;
27192             var s = this.selections;
27193             s.each(function(r){
27194                 this.deselectRow(ds.indexOfId(r.id));
27195             }, this);
27196             s.clear();
27197         }else{
27198             this.selections.clear();
27199         }
27200         this.last = false;
27201     },
27202
27203
27204     /**
27205      * Selects all rows.
27206      */
27207     selectAll : function(){
27208         if(this.locked) {
27209             return;
27210         }
27211         this.selections.clear();
27212         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27213             this.selectRow(i, true);
27214         }
27215     },
27216
27217     /**
27218      * Returns True if there is a selection.
27219      * @return {Boolean}
27220      */
27221     hasSelection : function(){
27222         return this.selections.length > 0;
27223     },
27224
27225     /**
27226      * Returns True if the specified row is selected.
27227      * @param {Number/Record} record The record or index of the record to check
27228      * @return {Boolean}
27229      */
27230     isSelected : function(index){
27231             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27232         return (r && this.selections.key(r.id) ? true : false);
27233     },
27234
27235     /**
27236      * Returns True if the specified record id is selected.
27237      * @param {String} id The id of record to check
27238      * @return {Boolean}
27239      */
27240     isIdSelected : function(id){
27241         return (this.selections.key(id) ? true : false);
27242     },
27243
27244
27245     // private
27246     handleMouseDBClick : function(e, t){
27247         
27248     },
27249     // private
27250     handleMouseDown : function(e, t)
27251     {
27252             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27253         if(this.isLocked() || rowIndex < 0 ){
27254             return;
27255         };
27256         if(e.shiftKey && this.last !== false){
27257             var last = this.last;
27258             this.selectRange(last, rowIndex, e.ctrlKey);
27259             this.last = last; // reset the last
27260             t.focus();
27261     
27262         }else{
27263             var isSelected = this.isSelected(rowIndex);
27264             //Roo.log("select row:" + rowIndex);
27265             if(isSelected){
27266                 this.deselectRow(rowIndex);
27267             } else {
27268                         this.selectRow(rowIndex, true);
27269             }
27270     
27271             /*
27272                 if(e.button !== 0 && isSelected){
27273                 alert('rowIndex 2: ' + rowIndex);
27274                     view.focusRow(rowIndex);
27275                 }else if(e.ctrlKey && isSelected){
27276                     this.deselectRow(rowIndex);
27277                 }else if(!isSelected){
27278                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27279                     view.focusRow(rowIndex);
27280                 }
27281             */
27282         }
27283         this.fireEvent("afterselectionchange", this);
27284     },
27285     // private
27286     handleDragableRowClick :  function(grid, rowIndex, e) 
27287     {
27288         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27289             this.selectRow(rowIndex, false);
27290             grid.view.focusRow(rowIndex);
27291              this.fireEvent("afterselectionchange", this);
27292         }
27293     },
27294     
27295     /**
27296      * Selects multiple rows.
27297      * @param {Array} rows Array of the indexes of the row to select
27298      * @param {Boolean} keepExisting (optional) True to keep existing selections
27299      */
27300     selectRows : function(rows, keepExisting){
27301         if(!keepExisting){
27302             this.clearSelections();
27303         }
27304         for(var i = 0, len = rows.length; i < len; i++){
27305             this.selectRow(rows[i], true);
27306         }
27307     },
27308
27309     /**
27310      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27311      * @param {Number} startRow The index of the first row in the range
27312      * @param {Number} endRow The index of the last row in the range
27313      * @param {Boolean} keepExisting (optional) True to retain existing selections
27314      */
27315     selectRange : function(startRow, endRow, keepExisting){
27316         if(this.locked) {
27317             return;
27318         }
27319         if(!keepExisting){
27320             this.clearSelections();
27321         }
27322         if(startRow <= endRow){
27323             for(var i = startRow; i <= endRow; i++){
27324                 this.selectRow(i, true);
27325             }
27326         }else{
27327             for(var i = startRow; i >= endRow; i--){
27328                 this.selectRow(i, true);
27329             }
27330         }
27331     },
27332
27333     /**
27334      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27335      * @param {Number} startRow The index of the first row in the range
27336      * @param {Number} endRow The index of the last row in the range
27337      */
27338     deselectRange : function(startRow, endRow, preventViewNotify){
27339         if(this.locked) {
27340             return;
27341         }
27342         for(var i = startRow; i <= endRow; i++){
27343             this.deselectRow(i, preventViewNotify);
27344         }
27345     },
27346
27347     /**
27348      * Selects a row.
27349      * @param {Number} row The index of the row to select
27350      * @param {Boolean} keepExisting (optional) True to keep existing selections
27351      */
27352     selectRow : function(index, keepExisting, preventViewNotify)
27353     {
27354             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27355             return;
27356         }
27357         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27358             if(!keepExisting || this.singleSelect){
27359                 this.clearSelections();
27360             }
27361             
27362             var r = this.grid.store.getAt(index);
27363             //console.log('selectRow - record id :' + r.id);
27364             
27365             this.selections.add(r);
27366             this.last = this.lastActive = index;
27367             if(!preventViewNotify){
27368                 var proxy = new Roo.Element(
27369                                 this.grid.getRowDom(index)
27370                 );
27371                 proxy.addClass('bg-info info');
27372             }
27373             this.fireEvent("rowselect", this, index, r);
27374             this.fireEvent("selectionchange", this);
27375         }
27376     },
27377
27378     /**
27379      * Deselects a row.
27380      * @param {Number} row The index of the row to deselect
27381      */
27382     deselectRow : function(index, preventViewNotify)
27383     {
27384         if(this.locked) {
27385             return;
27386         }
27387         if(this.last == index){
27388             this.last = false;
27389         }
27390         if(this.lastActive == index){
27391             this.lastActive = false;
27392         }
27393         
27394         var r = this.grid.store.getAt(index);
27395         if (!r) {
27396             return;
27397         }
27398         
27399         this.selections.remove(r);
27400         //.console.log('deselectRow - record id :' + r.id);
27401         if(!preventViewNotify){
27402         
27403             var proxy = new Roo.Element(
27404                 this.grid.getRowDom(index)
27405             );
27406             proxy.removeClass('bg-info info');
27407         }
27408         this.fireEvent("rowdeselect", this, index);
27409         this.fireEvent("selectionchange", this);
27410     },
27411
27412     // private
27413     restoreLast : function(){
27414         if(this._last){
27415             this.last = this._last;
27416         }
27417     },
27418
27419     // private
27420     acceptsNav : function(row, col, cm){
27421         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27422     },
27423
27424     // private
27425     onEditorKey : function(field, e){
27426         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27427         if(k == e.TAB){
27428             e.stopEvent();
27429             ed.completeEdit();
27430             if(e.shiftKey){
27431                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27432             }else{
27433                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27434             }
27435         }else if(k == e.ENTER && !e.ctrlKey){
27436             e.stopEvent();
27437             ed.completeEdit();
27438             if(e.shiftKey){
27439                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27440             }else{
27441                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27442             }
27443         }else if(k == e.ESC){
27444             ed.cancelEdit();
27445         }
27446         if(newCell){
27447             g.startEditing(newCell[0], newCell[1]);
27448         }
27449     }
27450 });
27451 /*
27452  * Based on:
27453  * Ext JS Library 1.1.1
27454  * Copyright(c) 2006-2007, Ext JS, LLC.
27455  *
27456  * Originally Released Under LGPL - original licence link has changed is not relivant.
27457  *
27458  * Fork - LGPL
27459  * <script type="text/javascript">
27460  */
27461  
27462 /**
27463  * @class Roo.bootstrap.PagingToolbar
27464  * @extends Roo.bootstrap.NavSimplebar
27465  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27466  * @constructor
27467  * Create a new PagingToolbar
27468  * @param {Object} config The config object
27469  * @param {Roo.data.Store} store
27470  */
27471 Roo.bootstrap.PagingToolbar = function(config)
27472 {
27473     // old args format still supported... - xtype is prefered..
27474         // created from xtype...
27475     
27476     this.ds = config.dataSource;
27477     
27478     if (config.store && !this.ds) {
27479         this.store= Roo.factory(config.store, Roo.data);
27480         this.ds = this.store;
27481         this.ds.xmodule = this.xmodule || false;
27482     }
27483     
27484     this.toolbarItems = [];
27485     if (config.items) {
27486         this.toolbarItems = config.items;
27487     }
27488     
27489     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27490     
27491     this.cursor = 0;
27492     
27493     if (this.ds) { 
27494         this.bind(this.ds);
27495     }
27496     
27497     if (Roo.bootstrap.version == 4) {
27498         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27499     } else {
27500         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27501     }
27502     
27503 };
27504
27505 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27506     /**
27507      * @cfg {Roo.data.Store} dataSource
27508      * The underlying data store providing the paged data
27509      */
27510     /**
27511      * @cfg {String/HTMLElement/Element} container
27512      * container The id or element that will contain the toolbar
27513      */
27514     /**
27515      * @cfg {Boolean} displayInfo
27516      * True to display the displayMsg (defaults to false)
27517      */
27518     /**
27519      * @cfg {Number} pageSize
27520      * The number of records to display per page (defaults to 20)
27521      */
27522     pageSize: 20,
27523     /**
27524      * @cfg {String} displayMsg
27525      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27526      */
27527     displayMsg : 'Displaying {0} - {1} of {2}',
27528     /**
27529      * @cfg {String} emptyMsg
27530      * The message to display when no records are found (defaults to "No data to display")
27531      */
27532     emptyMsg : 'No data to display',
27533     /**
27534      * Customizable piece of the default paging text (defaults to "Page")
27535      * @type String
27536      */
27537     beforePageText : "Page",
27538     /**
27539      * Customizable piece of the default paging text (defaults to "of %0")
27540      * @type String
27541      */
27542     afterPageText : "of {0}",
27543     /**
27544      * Customizable piece of the default paging text (defaults to "First Page")
27545      * @type String
27546      */
27547     firstText : "First Page",
27548     /**
27549      * Customizable piece of the default paging text (defaults to "Previous Page")
27550      * @type String
27551      */
27552     prevText : "Previous Page",
27553     /**
27554      * Customizable piece of the default paging text (defaults to "Next Page")
27555      * @type String
27556      */
27557     nextText : "Next Page",
27558     /**
27559      * Customizable piece of the default paging text (defaults to "Last Page")
27560      * @type String
27561      */
27562     lastText : "Last Page",
27563     /**
27564      * Customizable piece of the default paging text (defaults to "Refresh")
27565      * @type String
27566      */
27567     refreshText : "Refresh",
27568
27569     buttons : false,
27570     // private
27571     onRender : function(ct, position) 
27572     {
27573         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27574         this.navgroup.parentId = this.id;
27575         this.navgroup.onRender(this.el, null);
27576         // add the buttons to the navgroup
27577         
27578         if(this.displayInfo){
27579             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27580             this.displayEl = this.el.select('.x-paging-info', true).first();
27581 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27582 //            this.displayEl = navel.el.select('span',true).first();
27583         }
27584         
27585         var _this = this;
27586         
27587         if(this.buttons){
27588             Roo.each(_this.buttons, function(e){ // this might need to use render????
27589                Roo.factory(e).render(_this.el);
27590             });
27591         }
27592             
27593         Roo.each(_this.toolbarItems, function(e) {
27594             _this.navgroup.addItem(e);
27595         });
27596         
27597         
27598         this.first = this.navgroup.addItem({
27599             tooltip: this.firstText,
27600             cls: "prev btn-outline-secondary",
27601             html : ' <i class="fa fa-step-backward"></i>',
27602             disabled: true,
27603             preventDefault: true,
27604             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27605         });
27606         
27607         this.prev =  this.navgroup.addItem({
27608             tooltip: this.prevText,
27609             cls: "prev btn-outline-secondary",
27610             html : ' <i class="fa fa-backward"></i>',
27611             disabled: true,
27612             preventDefault: true,
27613             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27614         });
27615     //this.addSeparator();
27616         
27617         
27618         var field = this.navgroup.addItem( {
27619             tagtype : 'span',
27620             cls : 'x-paging-position  btn-outline-secondary',
27621              disabled: true,
27622             html : this.beforePageText  +
27623                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27624                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27625          } ); //?? escaped?
27626         
27627         this.field = field.el.select('input', true).first();
27628         this.field.on("keydown", this.onPagingKeydown, this);
27629         this.field.on("focus", function(){this.dom.select();});
27630     
27631     
27632         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27633         //this.field.setHeight(18);
27634         //this.addSeparator();
27635         this.next = this.navgroup.addItem({
27636             tooltip: this.nextText,
27637             cls: "next btn-outline-secondary",
27638             html : ' <i class="fa fa-forward"></i>',
27639             disabled: true,
27640             preventDefault: true,
27641             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27642         });
27643         this.last = this.navgroup.addItem({
27644             tooltip: this.lastText,
27645             html : ' <i class="fa fa-step-forward"></i>',
27646             cls: "next btn-outline-secondary",
27647             disabled: true,
27648             preventDefault: true,
27649             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27650         });
27651     //this.addSeparator();
27652         this.loading = this.navgroup.addItem({
27653             tooltip: this.refreshText,
27654             cls: "btn-outline-secondary",
27655             html : ' <i class="fa fa-refresh"></i>',
27656             preventDefault: true,
27657             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27658         });
27659         
27660     },
27661
27662     // private
27663     updateInfo : function(){
27664         if(this.displayEl){
27665             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27666             var msg = count == 0 ?
27667                 this.emptyMsg :
27668                 String.format(
27669                     this.displayMsg,
27670                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27671                 );
27672             this.displayEl.update(msg);
27673         }
27674     },
27675
27676     // private
27677     onLoad : function(ds, r, o)
27678     {
27679         this.cursor = o.params && o.params.start ? o.params.start : 0;
27680         
27681         var d = this.getPageData(),
27682             ap = d.activePage,
27683             ps = d.pages;
27684         
27685         
27686         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27687         this.field.dom.value = ap;
27688         this.first.setDisabled(ap == 1);
27689         this.prev.setDisabled(ap == 1);
27690         this.next.setDisabled(ap == ps);
27691         this.last.setDisabled(ap == ps);
27692         this.loading.enable();
27693         this.updateInfo();
27694     },
27695
27696     // private
27697     getPageData : function(){
27698         var total = this.ds.getTotalCount();
27699         return {
27700             total : total,
27701             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27702             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27703         };
27704     },
27705
27706     // private
27707     onLoadError : function(){
27708         this.loading.enable();
27709     },
27710
27711     // private
27712     onPagingKeydown : function(e){
27713         var k = e.getKey();
27714         var d = this.getPageData();
27715         if(k == e.RETURN){
27716             var v = this.field.dom.value, pageNum;
27717             if(!v || isNaN(pageNum = parseInt(v, 10))){
27718                 this.field.dom.value = d.activePage;
27719                 return;
27720             }
27721             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27722             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27723             e.stopEvent();
27724         }
27725         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))
27726         {
27727           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27728           this.field.dom.value = pageNum;
27729           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27730           e.stopEvent();
27731         }
27732         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27733         {
27734           var v = this.field.dom.value, pageNum; 
27735           var increment = (e.shiftKey) ? 10 : 1;
27736           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27737                 increment *= -1;
27738           }
27739           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27740             this.field.dom.value = d.activePage;
27741             return;
27742           }
27743           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27744           {
27745             this.field.dom.value = parseInt(v, 10) + increment;
27746             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27747             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27748           }
27749           e.stopEvent();
27750         }
27751     },
27752
27753     // private
27754     beforeLoad : function(){
27755         if(this.loading){
27756             this.loading.disable();
27757         }
27758     },
27759
27760     // private
27761     onClick : function(which){
27762         
27763         var ds = this.ds;
27764         if (!ds) {
27765             return;
27766         }
27767         
27768         switch(which){
27769             case "first":
27770                 ds.load({params:{start: 0, limit: this.pageSize}});
27771             break;
27772             case "prev":
27773                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27774             break;
27775             case "next":
27776                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27777             break;
27778             case "last":
27779                 var total = ds.getTotalCount();
27780                 var extra = total % this.pageSize;
27781                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27782                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27783             break;
27784             case "refresh":
27785                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27786             break;
27787         }
27788     },
27789
27790     /**
27791      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27792      * @param {Roo.data.Store} store The data store to unbind
27793      */
27794     unbind : function(ds){
27795         ds.un("beforeload", this.beforeLoad, this);
27796         ds.un("load", this.onLoad, this);
27797         ds.un("loadexception", this.onLoadError, this);
27798         ds.un("remove", this.updateInfo, this);
27799         ds.un("add", this.updateInfo, this);
27800         this.ds = undefined;
27801     },
27802
27803     /**
27804      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27805      * @param {Roo.data.Store} store The data store to bind
27806      */
27807     bind : function(ds){
27808         ds.on("beforeload", this.beforeLoad, this);
27809         ds.on("load", this.onLoad, this);
27810         ds.on("loadexception", this.onLoadError, this);
27811         ds.on("remove", this.updateInfo, this);
27812         ds.on("add", this.updateInfo, this);
27813         this.ds = ds;
27814     }
27815 });/*
27816  * - LGPL
27817  *
27818  * element
27819  * 
27820  */
27821
27822 /**
27823  * @class Roo.bootstrap.MessageBar
27824  * @extends Roo.bootstrap.Component
27825  * Bootstrap MessageBar class
27826  * @cfg {String} html contents of the MessageBar
27827  * @cfg {String} weight (info | success | warning | danger) default info
27828  * @cfg {String} beforeClass insert the bar before the given class
27829  * @cfg {Boolean} closable (true | false) default false
27830  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27831  * 
27832  * @constructor
27833  * Create a new Element
27834  * @param {Object} config The config object
27835  */
27836
27837 Roo.bootstrap.MessageBar = function(config){
27838     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27839 };
27840
27841 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27842     
27843     html: '',
27844     weight: 'info',
27845     closable: false,
27846     fixed: false,
27847     beforeClass: 'bootstrap-sticky-wrap',
27848     
27849     getAutoCreate : function(){
27850         
27851         var cfg = {
27852             tag: 'div',
27853             cls: 'alert alert-dismissable alert-' + this.weight,
27854             cn: [
27855                 {
27856                     tag: 'span',
27857                     cls: 'message',
27858                     html: this.html || ''
27859                 }
27860             ]
27861         };
27862         
27863         if(this.fixed){
27864             cfg.cls += ' alert-messages-fixed';
27865         }
27866         
27867         if(this.closable){
27868             cfg.cn.push({
27869                 tag: 'button',
27870                 cls: 'close',
27871                 html: 'x'
27872             });
27873         }
27874         
27875         return cfg;
27876     },
27877     
27878     onRender : function(ct, position)
27879     {
27880         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27881         
27882         if(!this.el){
27883             var cfg = Roo.apply({},  this.getAutoCreate());
27884             cfg.id = Roo.id();
27885             
27886             if (this.cls) {
27887                 cfg.cls += ' ' + this.cls;
27888             }
27889             if (this.style) {
27890                 cfg.style = this.style;
27891             }
27892             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27893             
27894             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27895         }
27896         
27897         this.el.select('>button.close').on('click', this.hide, this);
27898         
27899     },
27900     
27901     show : function()
27902     {
27903         if (!this.rendered) {
27904             this.render();
27905         }
27906         
27907         this.el.show();
27908         
27909         this.fireEvent('show', this);
27910         
27911     },
27912     
27913     hide : function()
27914     {
27915         if (!this.rendered) {
27916             this.render();
27917         }
27918         
27919         this.el.hide();
27920         
27921         this.fireEvent('hide', this);
27922     },
27923     
27924     update : function()
27925     {
27926 //        var e = this.el.dom.firstChild;
27927 //        
27928 //        if(this.closable){
27929 //            e = e.nextSibling;
27930 //        }
27931 //        
27932 //        e.data = this.html || '';
27933
27934         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27935     }
27936    
27937 });
27938
27939  
27940
27941      /*
27942  * - LGPL
27943  *
27944  * Graph
27945  * 
27946  */
27947
27948
27949 /**
27950  * @class Roo.bootstrap.Graph
27951  * @extends Roo.bootstrap.Component
27952  * Bootstrap Graph class
27953 > Prameters
27954  -sm {number} sm 4
27955  -md {number} md 5
27956  @cfg {String} graphtype  bar | vbar | pie
27957  @cfg {number} g_x coodinator | centre x (pie)
27958  @cfg {number} g_y coodinator | centre y (pie)
27959  @cfg {number} g_r radius (pie)
27960  @cfg {number} g_height height of the chart (respected by all elements in the set)
27961  @cfg {number} g_width width of the chart (respected by all elements in the set)
27962  @cfg {Object} title The title of the chart
27963     
27964  -{Array}  values
27965  -opts (object) options for the chart 
27966      o {
27967      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27968      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27969      o vgutter (number)
27970      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.
27971      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27972      o to
27973      o stretch (boolean)
27974      o }
27975  -opts (object) options for the pie
27976      o{
27977      o cut
27978      o startAngle (number)
27979      o endAngle (number)
27980      } 
27981  *
27982  * @constructor
27983  * Create a new Input
27984  * @param {Object} config The config object
27985  */
27986
27987 Roo.bootstrap.Graph = function(config){
27988     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27989     
27990     this.addEvents({
27991         // img events
27992         /**
27993          * @event click
27994          * The img click event for the img.
27995          * @param {Roo.EventObject} e
27996          */
27997         "click" : true
27998     });
27999 };
28000
28001 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28002     
28003     sm: 4,
28004     md: 5,
28005     graphtype: 'bar',
28006     g_height: 250,
28007     g_width: 400,
28008     g_x: 50,
28009     g_y: 50,
28010     g_r: 30,
28011     opts:{
28012         //g_colors: this.colors,
28013         g_type: 'soft',
28014         g_gutter: '20%'
28015
28016     },
28017     title : false,
28018
28019     getAutoCreate : function(){
28020         
28021         var cfg = {
28022             tag: 'div',
28023             html : null
28024         };
28025         
28026         
28027         return  cfg;
28028     },
28029
28030     onRender : function(ct,position){
28031         
28032         
28033         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28034         
28035         if (typeof(Raphael) == 'undefined') {
28036             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28037             return;
28038         }
28039         
28040         this.raphael = Raphael(this.el.dom);
28041         
28042                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28043                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28044                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28045                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28046                 /*
28047                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28048                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28049                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28050                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28051                 
28052                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28053                 r.barchart(330, 10, 300, 220, data1);
28054                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28055                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28056                 */
28057                 
28058                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28059                 // r.barchart(30, 30, 560, 250,  xdata, {
28060                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28061                 //     axis : "0 0 1 1",
28062                 //     axisxlabels :  xdata
28063                 //     //yvalues : cols,
28064                    
28065                 // });
28066 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28067 //        
28068 //        this.load(null,xdata,{
28069 //                axis : "0 0 1 1",
28070 //                axisxlabels :  xdata
28071 //                });
28072
28073     },
28074
28075     load : function(graphtype,xdata,opts)
28076     {
28077         this.raphael.clear();
28078         if(!graphtype) {
28079             graphtype = this.graphtype;
28080         }
28081         if(!opts){
28082             opts = this.opts;
28083         }
28084         var r = this.raphael,
28085             fin = function () {
28086                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28087             },
28088             fout = function () {
28089                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28090             },
28091             pfin = function() {
28092                 this.sector.stop();
28093                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28094
28095                 if (this.label) {
28096                     this.label[0].stop();
28097                     this.label[0].attr({ r: 7.5 });
28098                     this.label[1].attr({ "font-weight": 800 });
28099                 }
28100             },
28101             pfout = function() {
28102                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28103
28104                 if (this.label) {
28105                     this.label[0].animate({ r: 5 }, 500, "bounce");
28106                     this.label[1].attr({ "font-weight": 400 });
28107                 }
28108             };
28109
28110         switch(graphtype){
28111             case 'bar':
28112                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28113                 break;
28114             case 'hbar':
28115                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28116                 break;
28117             case 'pie':
28118 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28119 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28120 //            
28121                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28122                 
28123                 break;
28124
28125         }
28126         
28127         if(this.title){
28128             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28129         }
28130         
28131     },
28132     
28133     setTitle: function(o)
28134     {
28135         this.title = o;
28136     },
28137     
28138     initEvents: function() {
28139         
28140         if(!this.href){
28141             this.el.on('click', this.onClick, this);
28142         }
28143     },
28144     
28145     onClick : function(e)
28146     {
28147         Roo.log('img onclick');
28148         this.fireEvent('click', this, e);
28149     }
28150    
28151 });
28152
28153  
28154 /*
28155  * - LGPL
28156  *
28157  * numberBox
28158  * 
28159  */
28160 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28161
28162 /**
28163  * @class Roo.bootstrap.dash.NumberBox
28164  * @extends Roo.bootstrap.Component
28165  * Bootstrap NumberBox class
28166  * @cfg {String} headline Box headline
28167  * @cfg {String} content Box content
28168  * @cfg {String} icon Box icon
28169  * @cfg {String} footer Footer text
28170  * @cfg {String} fhref Footer href
28171  * 
28172  * @constructor
28173  * Create a new NumberBox
28174  * @param {Object} config The config object
28175  */
28176
28177
28178 Roo.bootstrap.dash.NumberBox = function(config){
28179     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28180     
28181 };
28182
28183 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28184     
28185     headline : '',
28186     content : '',
28187     icon : '',
28188     footer : '',
28189     fhref : '',
28190     ficon : '',
28191     
28192     getAutoCreate : function(){
28193         
28194         var cfg = {
28195             tag : 'div',
28196             cls : 'small-box ',
28197             cn : [
28198                 {
28199                     tag : 'div',
28200                     cls : 'inner',
28201                     cn :[
28202                         {
28203                             tag : 'h3',
28204                             cls : 'roo-headline',
28205                             html : this.headline
28206                         },
28207                         {
28208                             tag : 'p',
28209                             cls : 'roo-content',
28210                             html : this.content
28211                         }
28212                     ]
28213                 }
28214             ]
28215         };
28216         
28217         if(this.icon){
28218             cfg.cn.push({
28219                 tag : 'div',
28220                 cls : 'icon',
28221                 cn :[
28222                     {
28223                         tag : 'i',
28224                         cls : 'ion ' + this.icon
28225                     }
28226                 ]
28227             });
28228         }
28229         
28230         if(this.footer){
28231             var footer = {
28232                 tag : 'a',
28233                 cls : 'small-box-footer',
28234                 href : this.fhref || '#',
28235                 html : this.footer
28236             };
28237             
28238             cfg.cn.push(footer);
28239             
28240         }
28241         
28242         return  cfg;
28243     },
28244
28245     onRender : function(ct,position){
28246         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28247
28248
28249        
28250                 
28251     },
28252
28253     setHeadline: function (value)
28254     {
28255         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28256     },
28257     
28258     setFooter: function (value, href)
28259     {
28260         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28261         
28262         if(href){
28263             this.el.select('a.small-box-footer',true).first().attr('href', href);
28264         }
28265         
28266     },
28267
28268     setContent: function (value)
28269     {
28270         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28271     },
28272
28273     initEvents: function() 
28274     {   
28275         
28276     }
28277     
28278 });
28279
28280  
28281 /*
28282  * - LGPL
28283  *
28284  * TabBox
28285  * 
28286  */
28287 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28288
28289 /**
28290  * @class Roo.bootstrap.dash.TabBox
28291  * @extends Roo.bootstrap.Component
28292  * Bootstrap TabBox class
28293  * @cfg {String} title Title of the TabBox
28294  * @cfg {String} icon Icon of the TabBox
28295  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28296  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28297  * 
28298  * @constructor
28299  * Create a new TabBox
28300  * @param {Object} config The config object
28301  */
28302
28303
28304 Roo.bootstrap.dash.TabBox = function(config){
28305     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28306     this.addEvents({
28307         // raw events
28308         /**
28309          * @event addpane
28310          * When a pane is added
28311          * @param {Roo.bootstrap.dash.TabPane} pane
28312          */
28313         "addpane" : true,
28314         /**
28315          * @event activatepane
28316          * When a pane is activated
28317          * @param {Roo.bootstrap.dash.TabPane} pane
28318          */
28319         "activatepane" : true
28320         
28321          
28322     });
28323     
28324     this.panes = [];
28325 };
28326
28327 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28328
28329     title : '',
28330     icon : false,
28331     showtabs : true,
28332     tabScrollable : false,
28333     
28334     getChildContainer : function()
28335     {
28336         return this.el.select('.tab-content', true).first();
28337     },
28338     
28339     getAutoCreate : function(){
28340         
28341         var header = {
28342             tag: 'li',
28343             cls: 'pull-left header',
28344             html: this.title,
28345             cn : []
28346         };
28347         
28348         if(this.icon){
28349             header.cn.push({
28350                 tag: 'i',
28351                 cls: 'fa ' + this.icon
28352             });
28353         }
28354         
28355         var h = {
28356             tag: 'ul',
28357             cls: 'nav nav-tabs pull-right',
28358             cn: [
28359                 header
28360             ]
28361         };
28362         
28363         if(this.tabScrollable){
28364             h = {
28365                 tag: 'div',
28366                 cls: 'tab-header',
28367                 cn: [
28368                     {
28369                         tag: 'ul',
28370                         cls: 'nav nav-tabs pull-right',
28371                         cn: [
28372                             header
28373                         ]
28374                     }
28375                 ]
28376             };
28377         }
28378         
28379         var cfg = {
28380             tag: 'div',
28381             cls: 'nav-tabs-custom',
28382             cn: [
28383                 h,
28384                 {
28385                     tag: 'div',
28386                     cls: 'tab-content no-padding',
28387                     cn: []
28388                 }
28389             ]
28390         };
28391
28392         return  cfg;
28393     },
28394     initEvents : function()
28395     {
28396         //Roo.log('add add pane handler');
28397         this.on('addpane', this.onAddPane, this);
28398     },
28399      /**
28400      * Updates the box title
28401      * @param {String} html to set the title to.
28402      */
28403     setTitle : function(value)
28404     {
28405         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28406     },
28407     onAddPane : function(pane)
28408     {
28409         this.panes.push(pane);
28410         //Roo.log('addpane');
28411         //Roo.log(pane);
28412         // tabs are rendere left to right..
28413         if(!this.showtabs){
28414             return;
28415         }
28416         
28417         var ctr = this.el.select('.nav-tabs', true).first();
28418          
28419          
28420         var existing = ctr.select('.nav-tab',true);
28421         var qty = existing.getCount();;
28422         
28423         
28424         var tab = ctr.createChild({
28425             tag : 'li',
28426             cls : 'nav-tab' + (qty ? '' : ' active'),
28427             cn : [
28428                 {
28429                     tag : 'a',
28430                     href:'#',
28431                     html : pane.title
28432                 }
28433             ]
28434         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28435         pane.tab = tab;
28436         
28437         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28438         if (!qty) {
28439             pane.el.addClass('active');
28440         }
28441         
28442                 
28443     },
28444     onTabClick : function(ev,un,ob,pane)
28445     {
28446         //Roo.log('tab - prev default');
28447         ev.preventDefault();
28448         
28449         
28450         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28451         pane.tab.addClass('active');
28452         //Roo.log(pane.title);
28453         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28454         // technically we should have a deactivate event.. but maybe add later.
28455         // and it should not de-activate the selected tab...
28456         this.fireEvent('activatepane', pane);
28457         pane.el.addClass('active');
28458         pane.fireEvent('activate');
28459         
28460         
28461     },
28462     
28463     getActivePane : function()
28464     {
28465         var r = false;
28466         Roo.each(this.panes, function(p) {
28467             if(p.el.hasClass('active')){
28468                 r = p;
28469                 return false;
28470             }
28471             
28472             return;
28473         });
28474         
28475         return r;
28476     }
28477     
28478     
28479 });
28480
28481  
28482 /*
28483  * - LGPL
28484  *
28485  * Tab pane
28486  * 
28487  */
28488 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28489 /**
28490  * @class Roo.bootstrap.TabPane
28491  * @extends Roo.bootstrap.Component
28492  * Bootstrap TabPane class
28493  * @cfg {Boolean} active (false | true) Default false
28494  * @cfg {String} title title of panel
28495
28496  * 
28497  * @constructor
28498  * Create a new TabPane
28499  * @param {Object} config The config object
28500  */
28501
28502 Roo.bootstrap.dash.TabPane = function(config){
28503     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28504     
28505     this.addEvents({
28506         // raw events
28507         /**
28508          * @event activate
28509          * When a pane is activated
28510          * @param {Roo.bootstrap.dash.TabPane} pane
28511          */
28512         "activate" : true
28513          
28514     });
28515 };
28516
28517 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28518     
28519     active : false,
28520     title : '',
28521     
28522     // the tabBox that this is attached to.
28523     tab : false,
28524      
28525     getAutoCreate : function() 
28526     {
28527         var cfg = {
28528             tag: 'div',
28529             cls: 'tab-pane'
28530         };
28531         
28532         if(this.active){
28533             cfg.cls += ' active';
28534         }
28535         
28536         return cfg;
28537     },
28538     initEvents  : function()
28539     {
28540         //Roo.log('trigger add pane handler');
28541         this.parent().fireEvent('addpane', this)
28542     },
28543     
28544      /**
28545      * Updates the tab title 
28546      * @param {String} html to set the title to.
28547      */
28548     setTitle: function(str)
28549     {
28550         if (!this.tab) {
28551             return;
28552         }
28553         this.title = str;
28554         this.tab.select('a', true).first().dom.innerHTML = str;
28555         
28556     }
28557     
28558     
28559     
28560 });
28561
28562  
28563
28564
28565  /*
28566  * - LGPL
28567  *
28568  * menu
28569  * 
28570  */
28571 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28572
28573 /**
28574  * @class Roo.bootstrap.menu.Menu
28575  * @extends Roo.bootstrap.Component
28576  * Bootstrap Menu class - container for Menu
28577  * @cfg {String} html Text of the menu
28578  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28579  * @cfg {String} icon Font awesome icon
28580  * @cfg {String} pos Menu align to (top | bottom) default bottom
28581  * 
28582  * 
28583  * @constructor
28584  * Create a new Menu
28585  * @param {Object} config The config object
28586  */
28587
28588
28589 Roo.bootstrap.menu.Menu = function(config){
28590     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28591     
28592     this.addEvents({
28593         /**
28594          * @event beforeshow
28595          * Fires before this menu is displayed
28596          * @param {Roo.bootstrap.menu.Menu} this
28597          */
28598         beforeshow : true,
28599         /**
28600          * @event beforehide
28601          * Fires before this menu is hidden
28602          * @param {Roo.bootstrap.menu.Menu} this
28603          */
28604         beforehide : true,
28605         /**
28606          * @event show
28607          * Fires after this menu is displayed
28608          * @param {Roo.bootstrap.menu.Menu} this
28609          */
28610         show : true,
28611         /**
28612          * @event hide
28613          * Fires after this menu is hidden
28614          * @param {Roo.bootstrap.menu.Menu} this
28615          */
28616         hide : true,
28617         /**
28618          * @event click
28619          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28620          * @param {Roo.bootstrap.menu.Menu} this
28621          * @param {Roo.EventObject} e
28622          */
28623         click : true
28624     });
28625     
28626 };
28627
28628 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28629     
28630     submenu : false,
28631     html : '',
28632     weight : 'default',
28633     icon : false,
28634     pos : 'bottom',
28635     
28636     
28637     getChildContainer : function() {
28638         if(this.isSubMenu){
28639             return this.el;
28640         }
28641         
28642         return this.el.select('ul.dropdown-menu', true).first();  
28643     },
28644     
28645     getAutoCreate : function()
28646     {
28647         var text = [
28648             {
28649                 tag : 'span',
28650                 cls : 'roo-menu-text',
28651                 html : this.html
28652             }
28653         ];
28654         
28655         if(this.icon){
28656             text.unshift({
28657                 tag : 'i',
28658                 cls : 'fa ' + this.icon
28659             })
28660         }
28661         
28662         
28663         var cfg = {
28664             tag : 'div',
28665             cls : 'btn-group',
28666             cn : [
28667                 {
28668                     tag : 'button',
28669                     cls : 'dropdown-button btn btn-' + this.weight,
28670                     cn : text
28671                 },
28672                 {
28673                     tag : 'button',
28674                     cls : 'dropdown-toggle btn btn-' + this.weight,
28675                     cn : [
28676                         {
28677                             tag : 'span',
28678                             cls : 'caret'
28679                         }
28680                     ]
28681                 },
28682                 {
28683                     tag : 'ul',
28684                     cls : 'dropdown-menu'
28685                 }
28686             ]
28687             
28688         };
28689         
28690         if(this.pos == 'top'){
28691             cfg.cls += ' dropup';
28692         }
28693         
28694         if(this.isSubMenu){
28695             cfg = {
28696                 tag : 'ul',
28697                 cls : 'dropdown-menu'
28698             }
28699         }
28700         
28701         return cfg;
28702     },
28703     
28704     onRender : function(ct, position)
28705     {
28706         this.isSubMenu = ct.hasClass('dropdown-submenu');
28707         
28708         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28709     },
28710     
28711     initEvents : function() 
28712     {
28713         if(this.isSubMenu){
28714             return;
28715         }
28716         
28717         this.hidden = true;
28718         
28719         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28720         this.triggerEl.on('click', this.onTriggerPress, this);
28721         
28722         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28723         this.buttonEl.on('click', this.onClick, this);
28724         
28725     },
28726     
28727     list : function()
28728     {
28729         if(this.isSubMenu){
28730             return this.el;
28731         }
28732         
28733         return this.el.select('ul.dropdown-menu', true).first();
28734     },
28735     
28736     onClick : function(e)
28737     {
28738         this.fireEvent("click", this, e);
28739     },
28740     
28741     onTriggerPress  : function(e)
28742     {   
28743         if (this.isVisible()) {
28744             this.hide();
28745         } else {
28746             this.show();
28747         }
28748     },
28749     
28750     isVisible : function(){
28751         return !this.hidden;
28752     },
28753     
28754     show : function()
28755     {
28756         this.fireEvent("beforeshow", this);
28757         
28758         this.hidden = false;
28759         this.el.addClass('open');
28760         
28761         Roo.get(document).on("mouseup", this.onMouseUp, this);
28762         
28763         this.fireEvent("show", this);
28764         
28765         
28766     },
28767     
28768     hide : function()
28769     {
28770         this.fireEvent("beforehide", this);
28771         
28772         this.hidden = true;
28773         this.el.removeClass('open');
28774         
28775         Roo.get(document).un("mouseup", this.onMouseUp);
28776         
28777         this.fireEvent("hide", this);
28778     },
28779     
28780     onMouseUp : function()
28781     {
28782         this.hide();
28783     }
28784     
28785 });
28786
28787  
28788  /*
28789  * - LGPL
28790  *
28791  * menu item
28792  * 
28793  */
28794 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28795
28796 /**
28797  * @class Roo.bootstrap.menu.Item
28798  * @extends Roo.bootstrap.Component
28799  * Bootstrap MenuItem class
28800  * @cfg {Boolean} submenu (true | false) default false
28801  * @cfg {String} html text of the item
28802  * @cfg {String} href the link
28803  * @cfg {Boolean} disable (true | false) default false
28804  * @cfg {Boolean} preventDefault (true | false) default true
28805  * @cfg {String} icon Font awesome icon
28806  * @cfg {String} pos Submenu align to (left | right) default right 
28807  * 
28808  * 
28809  * @constructor
28810  * Create a new Item
28811  * @param {Object} config The config object
28812  */
28813
28814
28815 Roo.bootstrap.menu.Item = function(config){
28816     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28817     this.addEvents({
28818         /**
28819          * @event mouseover
28820          * Fires when the mouse is hovering over this menu
28821          * @param {Roo.bootstrap.menu.Item} this
28822          * @param {Roo.EventObject} e
28823          */
28824         mouseover : true,
28825         /**
28826          * @event mouseout
28827          * Fires when the mouse exits this menu
28828          * @param {Roo.bootstrap.menu.Item} this
28829          * @param {Roo.EventObject} e
28830          */
28831         mouseout : true,
28832         // raw events
28833         /**
28834          * @event click
28835          * The raw click event for the entire grid.
28836          * @param {Roo.EventObject} e
28837          */
28838         click : true
28839     });
28840 };
28841
28842 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28843     
28844     submenu : false,
28845     href : '',
28846     html : '',
28847     preventDefault: true,
28848     disable : false,
28849     icon : false,
28850     pos : 'right',
28851     
28852     getAutoCreate : function()
28853     {
28854         var text = [
28855             {
28856                 tag : 'span',
28857                 cls : 'roo-menu-item-text',
28858                 html : this.html
28859             }
28860         ];
28861         
28862         if(this.icon){
28863             text.unshift({
28864                 tag : 'i',
28865                 cls : 'fa ' + this.icon
28866             })
28867         }
28868         
28869         var cfg = {
28870             tag : 'li',
28871             cn : [
28872                 {
28873                     tag : 'a',
28874                     href : this.href || '#',
28875                     cn : text
28876                 }
28877             ]
28878         };
28879         
28880         if(this.disable){
28881             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28882         }
28883         
28884         if(this.submenu){
28885             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28886             
28887             if(this.pos == 'left'){
28888                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28889             }
28890         }
28891         
28892         return cfg;
28893     },
28894     
28895     initEvents : function() 
28896     {
28897         this.el.on('mouseover', this.onMouseOver, this);
28898         this.el.on('mouseout', this.onMouseOut, this);
28899         
28900         this.el.select('a', true).first().on('click', this.onClick, this);
28901         
28902     },
28903     
28904     onClick : function(e)
28905     {
28906         if(this.preventDefault){
28907             e.preventDefault();
28908         }
28909         
28910         this.fireEvent("click", this, e);
28911     },
28912     
28913     onMouseOver : function(e)
28914     {
28915         if(this.submenu && this.pos == 'left'){
28916             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28917         }
28918         
28919         this.fireEvent("mouseover", this, e);
28920     },
28921     
28922     onMouseOut : function(e)
28923     {
28924         this.fireEvent("mouseout", this, e);
28925     }
28926 });
28927
28928  
28929
28930  /*
28931  * - LGPL
28932  *
28933  * menu separator
28934  * 
28935  */
28936 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28937
28938 /**
28939  * @class Roo.bootstrap.menu.Separator
28940  * @extends Roo.bootstrap.Component
28941  * Bootstrap Separator class
28942  * 
28943  * @constructor
28944  * Create a new Separator
28945  * @param {Object} config The config object
28946  */
28947
28948
28949 Roo.bootstrap.menu.Separator = function(config){
28950     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28951 };
28952
28953 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28954     
28955     getAutoCreate : function(){
28956         var cfg = {
28957             tag : 'li',
28958             cls: 'divider'
28959         };
28960         
28961         return cfg;
28962     }
28963    
28964 });
28965
28966  
28967
28968  /*
28969  * - LGPL
28970  *
28971  * Tooltip
28972  * 
28973  */
28974
28975 /**
28976  * @class Roo.bootstrap.Tooltip
28977  * Bootstrap Tooltip class
28978  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28979  * to determine which dom element triggers the tooltip.
28980  * 
28981  * It needs to add support for additional attributes like tooltip-position
28982  * 
28983  * @constructor
28984  * Create a new Toolti
28985  * @param {Object} config The config object
28986  */
28987
28988 Roo.bootstrap.Tooltip = function(config){
28989     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28990     
28991     this.alignment = Roo.bootstrap.Tooltip.alignment;
28992     
28993     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28994         this.alignment = config.alignment;
28995     }
28996     
28997 };
28998
28999 Roo.apply(Roo.bootstrap.Tooltip, {
29000     /**
29001      * @function init initialize tooltip monitoring.
29002      * @static
29003      */
29004     currentEl : false,
29005     currentTip : false,
29006     currentRegion : false,
29007     
29008     //  init : delay?
29009     
29010     init : function()
29011     {
29012         Roo.get(document).on('mouseover', this.enter ,this);
29013         Roo.get(document).on('mouseout', this.leave, this);
29014          
29015         
29016         this.currentTip = new Roo.bootstrap.Tooltip();
29017     },
29018     
29019     enter : function(ev)
29020     {
29021         var dom = ev.getTarget();
29022         
29023         //Roo.log(['enter',dom]);
29024         var el = Roo.fly(dom);
29025         if (this.currentEl) {
29026             //Roo.log(dom);
29027             //Roo.log(this.currentEl);
29028             //Roo.log(this.currentEl.contains(dom));
29029             if (this.currentEl == el) {
29030                 return;
29031             }
29032             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29033                 return;
29034             }
29035
29036         }
29037         
29038         if (this.currentTip.el) {
29039             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29040         }    
29041         //Roo.log(ev);
29042         
29043         if(!el || el.dom == document){
29044             return;
29045         }
29046         
29047         var bindEl = el;
29048         
29049         // you can not look for children, as if el is the body.. then everythign is the child..
29050         if (!el.attr('tooltip')) { //
29051             if (!el.select("[tooltip]").elements.length) {
29052                 return;
29053             }
29054             // is the mouse over this child...?
29055             bindEl = el.select("[tooltip]").first();
29056             var xy = ev.getXY();
29057             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29058                 //Roo.log("not in region.");
29059                 return;
29060             }
29061             //Roo.log("child element over..");
29062             
29063         }
29064         this.currentEl = bindEl;
29065         this.currentTip.bind(bindEl);
29066         this.currentRegion = Roo.lib.Region.getRegion(dom);
29067         this.currentTip.enter();
29068         
29069     },
29070     leave : function(ev)
29071     {
29072         var dom = ev.getTarget();
29073         //Roo.log(['leave',dom]);
29074         if (!this.currentEl) {
29075             return;
29076         }
29077         
29078         
29079         if (dom != this.currentEl.dom) {
29080             return;
29081         }
29082         var xy = ev.getXY();
29083         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29084             return;
29085         }
29086         // only activate leave if mouse cursor is outside... bounding box..
29087         
29088         
29089         
29090         
29091         if (this.currentTip) {
29092             this.currentTip.leave();
29093         }
29094         //Roo.log('clear currentEl');
29095         this.currentEl = false;
29096         
29097         
29098     },
29099     alignment : {
29100         'left' : ['r-l', [-2,0], 'right'],
29101         'right' : ['l-r', [2,0], 'left'],
29102         'bottom' : ['t-b', [0,2], 'top'],
29103         'top' : [ 'b-t', [0,-2], 'bottom']
29104     }
29105     
29106 });
29107
29108
29109 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29110     
29111     
29112     bindEl : false,
29113     
29114     delay : null, // can be { show : 300 , hide: 500}
29115     
29116     timeout : null,
29117     
29118     hoverState : null, //???
29119     
29120     placement : 'bottom', 
29121     
29122     alignment : false,
29123     
29124     getAutoCreate : function(){
29125     
29126         var cfg = {
29127            cls : 'tooltip',   
29128            role : 'tooltip',
29129            cn : [
29130                 {
29131                     cls : 'tooltip-arrow arrow'
29132                 },
29133                 {
29134                     cls : 'tooltip-inner'
29135                 }
29136            ]
29137         };
29138         
29139         return cfg;
29140     },
29141     bind : function(el)
29142     {
29143         this.bindEl = el;
29144     },
29145     
29146     initEvents : function()
29147     {
29148         this.arrowEl = this.el.select('.arrow', true).first();
29149         this.innerEl = this.el.select('.tooltip-inner', true).first();
29150     },
29151     
29152     enter : function () {
29153        
29154         if (this.timeout != null) {
29155             clearTimeout(this.timeout);
29156         }
29157         
29158         this.hoverState = 'in';
29159          //Roo.log("enter - show");
29160         if (!this.delay || !this.delay.show) {
29161             this.show();
29162             return;
29163         }
29164         var _t = this;
29165         this.timeout = setTimeout(function () {
29166             if (_t.hoverState == 'in') {
29167                 _t.show();
29168             }
29169         }, this.delay.show);
29170     },
29171     leave : function()
29172     {
29173         clearTimeout(this.timeout);
29174     
29175         this.hoverState = 'out';
29176          if (!this.delay || !this.delay.hide) {
29177             this.hide();
29178             return;
29179         }
29180        
29181         var _t = this;
29182         this.timeout = setTimeout(function () {
29183             //Roo.log("leave - timeout");
29184             
29185             if (_t.hoverState == 'out') {
29186                 _t.hide();
29187                 Roo.bootstrap.Tooltip.currentEl = false;
29188             }
29189         }, delay);
29190     },
29191     
29192     show : function (msg)
29193     {
29194         if (!this.el) {
29195             this.render(document.body);
29196         }
29197         // set content.
29198         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29199         
29200         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29201         
29202         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29203         
29204         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29205                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29206         
29207         var placement = typeof this.placement == 'function' ?
29208             this.placement.call(this, this.el, on_el) :
29209             this.placement;
29210             
29211         var autoToken = /\s?auto?\s?/i;
29212         var autoPlace = autoToken.test(placement);
29213         if (autoPlace) {
29214             placement = placement.replace(autoToken, '') || 'top';
29215         }
29216         
29217         //this.el.detach()
29218         //this.el.setXY([0,0]);
29219         this.el.show();
29220         //this.el.dom.style.display='block';
29221         
29222         //this.el.appendTo(on_el);
29223         
29224         var p = this.getPosition();
29225         var box = this.el.getBox();
29226         
29227         if (autoPlace) {
29228             // fixme..
29229         }
29230         
29231         var align = this.alignment[placement];
29232         
29233         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29234         
29235         if(placement == 'top' || placement == 'bottom'){
29236             if(xy[0] < 0){
29237                 placement = 'right';
29238             }
29239             
29240             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29241                 placement = 'left';
29242             }
29243             
29244             var scroll = Roo.select('body', true).first().getScroll();
29245             
29246             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29247                 placement = 'top';
29248             }
29249             
29250             align = this.alignment[placement];
29251             
29252             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29253             
29254         }
29255         
29256         this.el.alignTo(this.bindEl, align[0],align[1]);
29257         //var arrow = this.el.select('.arrow',true).first();
29258         //arrow.set(align[2], 
29259         
29260         this.el.addClass(placement);
29261         this.el.addClass("bs-tooltip-"+ placement);
29262         
29263         this.el.addClass('in fade show');
29264         
29265         this.hoverState = null;
29266         
29267         if (this.el.hasClass('fade')) {
29268             // fade it?
29269         }
29270         
29271         
29272         
29273         
29274         
29275     },
29276     hide : function()
29277     {
29278          
29279         if (!this.el) {
29280             return;
29281         }
29282         //this.el.setXY([0,0]);
29283         this.el.removeClass(['show', 'in']);
29284         //this.el.hide();
29285         
29286     }
29287     
29288 });
29289  
29290
29291  /*
29292  * - LGPL
29293  *
29294  * Location Picker
29295  * 
29296  */
29297
29298 /**
29299  * @class Roo.bootstrap.LocationPicker
29300  * @extends Roo.bootstrap.Component
29301  * Bootstrap LocationPicker class
29302  * @cfg {Number} latitude Position when init default 0
29303  * @cfg {Number} longitude Position when init default 0
29304  * @cfg {Number} zoom default 15
29305  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29306  * @cfg {Boolean} mapTypeControl default false
29307  * @cfg {Boolean} disableDoubleClickZoom default false
29308  * @cfg {Boolean} scrollwheel default true
29309  * @cfg {Boolean} streetViewControl default false
29310  * @cfg {Number} radius default 0
29311  * @cfg {String} locationName
29312  * @cfg {Boolean} draggable default true
29313  * @cfg {Boolean} enableAutocomplete default false
29314  * @cfg {Boolean} enableReverseGeocode default true
29315  * @cfg {String} markerTitle
29316  * 
29317  * @constructor
29318  * Create a new LocationPicker
29319  * @param {Object} config The config object
29320  */
29321
29322
29323 Roo.bootstrap.LocationPicker = function(config){
29324     
29325     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29326     
29327     this.addEvents({
29328         /**
29329          * @event initial
29330          * Fires when the picker initialized.
29331          * @param {Roo.bootstrap.LocationPicker} this
29332          * @param {Google Location} location
29333          */
29334         initial : true,
29335         /**
29336          * @event positionchanged
29337          * Fires when the picker position changed.
29338          * @param {Roo.bootstrap.LocationPicker} this
29339          * @param {Google Location} location
29340          */
29341         positionchanged : true,
29342         /**
29343          * @event resize
29344          * Fires when the map resize.
29345          * @param {Roo.bootstrap.LocationPicker} this
29346          */
29347         resize : true,
29348         /**
29349          * @event show
29350          * Fires when the map show.
29351          * @param {Roo.bootstrap.LocationPicker} this
29352          */
29353         show : true,
29354         /**
29355          * @event hide
29356          * Fires when the map hide.
29357          * @param {Roo.bootstrap.LocationPicker} this
29358          */
29359         hide : true,
29360         /**
29361          * @event mapClick
29362          * Fires when click the map.
29363          * @param {Roo.bootstrap.LocationPicker} this
29364          * @param {Map event} e
29365          */
29366         mapClick : true,
29367         /**
29368          * @event mapRightClick
29369          * Fires when right click the map.
29370          * @param {Roo.bootstrap.LocationPicker} this
29371          * @param {Map event} e
29372          */
29373         mapRightClick : true,
29374         /**
29375          * @event markerClick
29376          * Fires when click the marker.
29377          * @param {Roo.bootstrap.LocationPicker} this
29378          * @param {Map event} e
29379          */
29380         markerClick : true,
29381         /**
29382          * @event markerRightClick
29383          * Fires when right click the marker.
29384          * @param {Roo.bootstrap.LocationPicker} this
29385          * @param {Map event} e
29386          */
29387         markerRightClick : true,
29388         /**
29389          * @event OverlayViewDraw
29390          * Fires when OverlayView Draw
29391          * @param {Roo.bootstrap.LocationPicker} this
29392          */
29393         OverlayViewDraw : true,
29394         /**
29395          * @event OverlayViewOnAdd
29396          * Fires when OverlayView Draw
29397          * @param {Roo.bootstrap.LocationPicker} this
29398          */
29399         OverlayViewOnAdd : true,
29400         /**
29401          * @event OverlayViewOnRemove
29402          * Fires when OverlayView Draw
29403          * @param {Roo.bootstrap.LocationPicker} this
29404          */
29405         OverlayViewOnRemove : true,
29406         /**
29407          * @event OverlayViewShow
29408          * Fires when OverlayView Draw
29409          * @param {Roo.bootstrap.LocationPicker} this
29410          * @param {Pixel} cpx
29411          */
29412         OverlayViewShow : true,
29413         /**
29414          * @event OverlayViewHide
29415          * Fires when OverlayView Draw
29416          * @param {Roo.bootstrap.LocationPicker} this
29417          */
29418         OverlayViewHide : true,
29419         /**
29420          * @event loadexception
29421          * Fires when load google lib failed.
29422          * @param {Roo.bootstrap.LocationPicker} this
29423          */
29424         loadexception : true
29425     });
29426         
29427 };
29428
29429 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29430     
29431     gMapContext: false,
29432     
29433     latitude: 0,
29434     longitude: 0,
29435     zoom: 15,
29436     mapTypeId: false,
29437     mapTypeControl: false,
29438     disableDoubleClickZoom: false,
29439     scrollwheel: true,
29440     streetViewControl: false,
29441     radius: 0,
29442     locationName: '',
29443     draggable: true,
29444     enableAutocomplete: false,
29445     enableReverseGeocode: true,
29446     markerTitle: '',
29447     
29448     getAutoCreate: function()
29449     {
29450
29451         var cfg = {
29452             tag: 'div',
29453             cls: 'roo-location-picker'
29454         };
29455         
29456         return cfg
29457     },
29458     
29459     initEvents: function(ct, position)
29460     {       
29461         if(!this.el.getWidth() || this.isApplied()){
29462             return;
29463         }
29464         
29465         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29466         
29467         this.initial();
29468     },
29469     
29470     initial: function()
29471     {
29472         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29473             this.fireEvent('loadexception', this);
29474             return;
29475         }
29476         
29477         if(!this.mapTypeId){
29478             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29479         }
29480         
29481         this.gMapContext = this.GMapContext();
29482         
29483         this.initOverlayView();
29484         
29485         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29486         
29487         var _this = this;
29488                 
29489         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29490             _this.setPosition(_this.gMapContext.marker.position);
29491         });
29492         
29493         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29494             _this.fireEvent('mapClick', this, event);
29495             
29496         });
29497
29498         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29499             _this.fireEvent('mapRightClick', this, event);
29500             
29501         });
29502         
29503         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29504             _this.fireEvent('markerClick', this, event);
29505             
29506         });
29507
29508         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29509             _this.fireEvent('markerRightClick', this, event);
29510             
29511         });
29512         
29513         this.setPosition(this.gMapContext.location);
29514         
29515         this.fireEvent('initial', this, this.gMapContext.location);
29516     },
29517     
29518     initOverlayView: function()
29519     {
29520         var _this = this;
29521         
29522         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29523             
29524             draw: function()
29525             {
29526                 _this.fireEvent('OverlayViewDraw', _this);
29527             },
29528             
29529             onAdd: function()
29530             {
29531                 _this.fireEvent('OverlayViewOnAdd', _this);
29532             },
29533             
29534             onRemove: function()
29535             {
29536                 _this.fireEvent('OverlayViewOnRemove', _this);
29537             },
29538             
29539             show: function(cpx)
29540             {
29541                 _this.fireEvent('OverlayViewShow', _this, cpx);
29542             },
29543             
29544             hide: function()
29545             {
29546                 _this.fireEvent('OverlayViewHide', _this);
29547             }
29548             
29549         });
29550     },
29551     
29552     fromLatLngToContainerPixel: function(event)
29553     {
29554         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29555     },
29556     
29557     isApplied: function() 
29558     {
29559         return this.getGmapContext() == false ? false : true;
29560     },
29561     
29562     getGmapContext: function() 
29563     {
29564         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29565     },
29566     
29567     GMapContext: function() 
29568     {
29569         var position = new google.maps.LatLng(this.latitude, this.longitude);
29570         
29571         var _map = new google.maps.Map(this.el.dom, {
29572             center: position,
29573             zoom: this.zoom,
29574             mapTypeId: this.mapTypeId,
29575             mapTypeControl: this.mapTypeControl,
29576             disableDoubleClickZoom: this.disableDoubleClickZoom,
29577             scrollwheel: this.scrollwheel,
29578             streetViewControl: this.streetViewControl,
29579             locationName: this.locationName,
29580             draggable: this.draggable,
29581             enableAutocomplete: this.enableAutocomplete,
29582             enableReverseGeocode: this.enableReverseGeocode
29583         });
29584         
29585         var _marker = new google.maps.Marker({
29586             position: position,
29587             map: _map,
29588             title: this.markerTitle,
29589             draggable: this.draggable
29590         });
29591         
29592         return {
29593             map: _map,
29594             marker: _marker,
29595             circle: null,
29596             location: position,
29597             radius: this.radius,
29598             locationName: this.locationName,
29599             addressComponents: {
29600                 formatted_address: null,
29601                 addressLine1: null,
29602                 addressLine2: null,
29603                 streetName: null,
29604                 streetNumber: null,
29605                 city: null,
29606                 district: null,
29607                 state: null,
29608                 stateOrProvince: null
29609             },
29610             settings: this,
29611             domContainer: this.el.dom,
29612             geodecoder: new google.maps.Geocoder()
29613         };
29614     },
29615     
29616     drawCircle: function(center, radius, options) 
29617     {
29618         if (this.gMapContext.circle != null) {
29619             this.gMapContext.circle.setMap(null);
29620         }
29621         if (radius > 0) {
29622             radius *= 1;
29623             options = Roo.apply({}, options, {
29624                 strokeColor: "#0000FF",
29625                 strokeOpacity: .35,
29626                 strokeWeight: 2,
29627                 fillColor: "#0000FF",
29628                 fillOpacity: .2
29629             });
29630             
29631             options.map = this.gMapContext.map;
29632             options.radius = radius;
29633             options.center = center;
29634             this.gMapContext.circle = new google.maps.Circle(options);
29635             return this.gMapContext.circle;
29636         }
29637         
29638         return null;
29639     },
29640     
29641     setPosition: function(location) 
29642     {
29643         this.gMapContext.location = location;
29644         this.gMapContext.marker.setPosition(location);
29645         this.gMapContext.map.panTo(location);
29646         this.drawCircle(location, this.gMapContext.radius, {});
29647         
29648         var _this = this;
29649         
29650         if (this.gMapContext.settings.enableReverseGeocode) {
29651             this.gMapContext.geodecoder.geocode({
29652                 latLng: this.gMapContext.location
29653             }, function(results, status) {
29654                 
29655                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29656                     _this.gMapContext.locationName = results[0].formatted_address;
29657                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29658                     
29659                     _this.fireEvent('positionchanged', this, location);
29660                 }
29661             });
29662             
29663             return;
29664         }
29665         
29666         this.fireEvent('positionchanged', this, location);
29667     },
29668     
29669     resize: function()
29670     {
29671         google.maps.event.trigger(this.gMapContext.map, "resize");
29672         
29673         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29674         
29675         this.fireEvent('resize', this);
29676     },
29677     
29678     setPositionByLatLng: function(latitude, longitude)
29679     {
29680         this.setPosition(new google.maps.LatLng(latitude, longitude));
29681     },
29682     
29683     getCurrentPosition: function() 
29684     {
29685         return {
29686             latitude: this.gMapContext.location.lat(),
29687             longitude: this.gMapContext.location.lng()
29688         };
29689     },
29690     
29691     getAddressName: function() 
29692     {
29693         return this.gMapContext.locationName;
29694     },
29695     
29696     getAddressComponents: function() 
29697     {
29698         return this.gMapContext.addressComponents;
29699     },
29700     
29701     address_component_from_google_geocode: function(address_components) 
29702     {
29703         var result = {};
29704         
29705         for (var i = 0; i < address_components.length; i++) {
29706             var component = address_components[i];
29707             if (component.types.indexOf("postal_code") >= 0) {
29708                 result.postalCode = component.short_name;
29709             } else if (component.types.indexOf("street_number") >= 0) {
29710                 result.streetNumber = component.short_name;
29711             } else if (component.types.indexOf("route") >= 0) {
29712                 result.streetName = component.short_name;
29713             } else if (component.types.indexOf("neighborhood") >= 0) {
29714                 result.city = component.short_name;
29715             } else if (component.types.indexOf("locality") >= 0) {
29716                 result.city = component.short_name;
29717             } else if (component.types.indexOf("sublocality") >= 0) {
29718                 result.district = component.short_name;
29719             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29720                 result.stateOrProvince = component.short_name;
29721             } else if (component.types.indexOf("country") >= 0) {
29722                 result.country = component.short_name;
29723             }
29724         }
29725         
29726         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29727         result.addressLine2 = "";
29728         return result;
29729     },
29730     
29731     setZoomLevel: function(zoom)
29732     {
29733         this.gMapContext.map.setZoom(zoom);
29734     },
29735     
29736     show: function()
29737     {
29738         if(!this.el){
29739             return;
29740         }
29741         
29742         this.el.show();
29743         
29744         this.resize();
29745         
29746         this.fireEvent('show', this);
29747     },
29748     
29749     hide: function()
29750     {
29751         if(!this.el){
29752             return;
29753         }
29754         
29755         this.el.hide();
29756         
29757         this.fireEvent('hide', this);
29758     }
29759     
29760 });
29761
29762 Roo.apply(Roo.bootstrap.LocationPicker, {
29763     
29764     OverlayView : function(map, options)
29765     {
29766         options = options || {};
29767         
29768         this.setMap(map);
29769     }
29770     
29771     
29772 });/**
29773  * @class Roo.bootstrap.Alert
29774  * @extends Roo.bootstrap.Component
29775  * Bootstrap Alert class - shows an alert area box
29776  * eg
29777  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29778   Enter a valid email address
29779 </div>
29780  * @licence LGPL
29781  * @cfg {String} title The title of alert
29782  * @cfg {String} html The content of alert
29783  * @cfg {String} weight (  success | info | warning | danger )
29784  * @cfg {String} faicon font-awesomeicon
29785  * 
29786  * @constructor
29787  * Create a new alert
29788  * @param {Object} config The config object
29789  */
29790
29791
29792 Roo.bootstrap.Alert = function(config){
29793     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29794     
29795 };
29796
29797 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29798     
29799     title: '',
29800     html: '',
29801     weight: false,
29802     faicon: false,
29803     
29804     getAutoCreate : function()
29805     {
29806         
29807         var cfg = {
29808             tag : 'div',
29809             cls : 'alert',
29810             cn : [
29811                 {
29812                     tag : 'i',
29813                     cls : 'roo-alert-icon'
29814                     
29815                 },
29816                 {
29817                     tag : 'b',
29818                     cls : 'roo-alert-title',
29819                     html : this.title
29820                 },
29821                 {
29822                     tag : 'span',
29823                     cls : 'roo-alert-text',
29824                     html : this.html
29825                 }
29826             ]
29827         };
29828         
29829         if(this.faicon){
29830             cfg.cn[0].cls += ' fa ' + this.faicon;
29831         }
29832         
29833         if(this.weight){
29834             cfg.cls += ' alert-' + this.weight;
29835         }
29836         
29837         return cfg;
29838     },
29839     
29840     initEvents: function() 
29841     {
29842         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29843     },
29844     
29845     setTitle : function(str)
29846     {
29847         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29848     },
29849     
29850     setText : function(str)
29851     {
29852         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29853     },
29854     
29855     setWeight : function(weight)
29856     {
29857         if(this.weight){
29858             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29859         }
29860         
29861         this.weight = weight;
29862         
29863         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29864     },
29865     
29866     setIcon : function(icon)
29867     {
29868         if(this.faicon){
29869             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29870         }
29871         
29872         this.faicon = icon;
29873         
29874         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29875     },
29876     
29877     hide: function() 
29878     {
29879         this.el.hide();   
29880     },
29881     
29882     show: function() 
29883     {  
29884         this.el.show();   
29885     }
29886     
29887 });
29888
29889  
29890 /*
29891 * Licence: LGPL
29892 */
29893
29894 /**
29895  * @class Roo.bootstrap.UploadCropbox
29896  * @extends Roo.bootstrap.Component
29897  * Bootstrap UploadCropbox class
29898  * @cfg {String} emptyText show when image has been loaded
29899  * @cfg {String} rotateNotify show when image too small to rotate
29900  * @cfg {Number} errorTimeout default 3000
29901  * @cfg {Number} minWidth default 300
29902  * @cfg {Number} minHeight default 300
29903  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29904  * @cfg {Boolean} isDocument (true|false) default false
29905  * @cfg {String} url action url
29906  * @cfg {String} paramName default 'imageUpload'
29907  * @cfg {String} method default POST
29908  * @cfg {Boolean} loadMask (true|false) default true
29909  * @cfg {Boolean} loadingText default 'Loading...'
29910  * 
29911  * @constructor
29912  * Create a new UploadCropbox
29913  * @param {Object} config The config object
29914  */
29915
29916 Roo.bootstrap.UploadCropbox = function(config){
29917     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29918     
29919     this.addEvents({
29920         /**
29921          * @event beforeselectfile
29922          * Fire before select file
29923          * @param {Roo.bootstrap.UploadCropbox} this
29924          */
29925         "beforeselectfile" : true,
29926         /**
29927          * @event initial
29928          * Fire after initEvent
29929          * @param {Roo.bootstrap.UploadCropbox} this
29930          */
29931         "initial" : true,
29932         /**
29933          * @event crop
29934          * Fire after initEvent
29935          * @param {Roo.bootstrap.UploadCropbox} this
29936          * @param {String} data
29937          */
29938         "crop" : true,
29939         /**
29940          * @event prepare
29941          * Fire when preparing the file data
29942          * @param {Roo.bootstrap.UploadCropbox} this
29943          * @param {Object} file
29944          */
29945         "prepare" : true,
29946         /**
29947          * @event exception
29948          * Fire when get exception
29949          * @param {Roo.bootstrap.UploadCropbox} this
29950          * @param {XMLHttpRequest} xhr
29951          */
29952         "exception" : true,
29953         /**
29954          * @event beforeloadcanvas
29955          * Fire before load the canvas
29956          * @param {Roo.bootstrap.UploadCropbox} this
29957          * @param {String} src
29958          */
29959         "beforeloadcanvas" : true,
29960         /**
29961          * @event trash
29962          * Fire when trash image
29963          * @param {Roo.bootstrap.UploadCropbox} this
29964          */
29965         "trash" : true,
29966         /**
29967          * @event download
29968          * Fire when download the image
29969          * @param {Roo.bootstrap.UploadCropbox} this
29970          */
29971         "download" : true,
29972         /**
29973          * @event footerbuttonclick
29974          * Fire when footerbuttonclick
29975          * @param {Roo.bootstrap.UploadCropbox} this
29976          * @param {String} type
29977          */
29978         "footerbuttonclick" : true,
29979         /**
29980          * @event resize
29981          * Fire when resize
29982          * @param {Roo.bootstrap.UploadCropbox} this
29983          */
29984         "resize" : true,
29985         /**
29986          * @event rotate
29987          * Fire when rotate the image
29988          * @param {Roo.bootstrap.UploadCropbox} this
29989          * @param {String} pos
29990          */
29991         "rotate" : true,
29992         /**
29993          * @event inspect
29994          * Fire when inspect the file
29995          * @param {Roo.bootstrap.UploadCropbox} this
29996          * @param {Object} file
29997          */
29998         "inspect" : true,
29999         /**
30000          * @event upload
30001          * Fire when xhr upload the file
30002          * @param {Roo.bootstrap.UploadCropbox} this
30003          * @param {Object} data
30004          */
30005         "upload" : true,
30006         /**
30007          * @event arrange
30008          * Fire when arrange the file data
30009          * @param {Roo.bootstrap.UploadCropbox} this
30010          * @param {Object} formData
30011          */
30012         "arrange" : true
30013     });
30014     
30015     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30016 };
30017
30018 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30019     
30020     emptyText : 'Click to upload image',
30021     rotateNotify : 'Image is too small to rotate',
30022     errorTimeout : 3000,
30023     scale : 0,
30024     baseScale : 1,
30025     rotate : 0,
30026     dragable : false,
30027     pinching : false,
30028     mouseX : 0,
30029     mouseY : 0,
30030     cropData : false,
30031     minWidth : 300,
30032     minHeight : 300,
30033     file : false,
30034     exif : {},
30035     baseRotate : 1,
30036     cropType : 'image/jpeg',
30037     buttons : false,
30038     canvasLoaded : false,
30039     isDocument : false,
30040     method : 'POST',
30041     paramName : 'imageUpload',
30042     loadMask : true,
30043     loadingText : 'Loading...',
30044     maskEl : false,
30045     
30046     getAutoCreate : function()
30047     {
30048         var cfg = {
30049             tag : 'div',
30050             cls : 'roo-upload-cropbox',
30051             cn : [
30052                 {
30053                     tag : 'input',
30054                     cls : 'roo-upload-cropbox-selector',
30055                     type : 'file'
30056                 },
30057                 {
30058                     tag : 'div',
30059                     cls : 'roo-upload-cropbox-body',
30060                     style : 'cursor:pointer',
30061                     cn : [
30062                         {
30063                             tag : 'div',
30064                             cls : 'roo-upload-cropbox-preview'
30065                         },
30066                         {
30067                             tag : 'div',
30068                             cls : 'roo-upload-cropbox-thumb'
30069                         },
30070                         {
30071                             tag : 'div',
30072                             cls : 'roo-upload-cropbox-empty-notify',
30073                             html : this.emptyText
30074                         },
30075                         {
30076                             tag : 'div',
30077                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30078                             html : this.rotateNotify
30079                         }
30080                     ]
30081                 },
30082                 {
30083                     tag : 'div',
30084                     cls : 'roo-upload-cropbox-footer',
30085                     cn : {
30086                         tag : 'div',
30087                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30088                         cn : []
30089                     }
30090                 }
30091             ]
30092         };
30093         
30094         return cfg;
30095     },
30096     
30097     onRender : function(ct, position)
30098     {
30099         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30100         
30101         if (this.buttons.length) {
30102             
30103             Roo.each(this.buttons, function(bb) {
30104                 
30105                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30106                 
30107                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30108                 
30109             }, this);
30110         }
30111         
30112         if(this.loadMask){
30113             this.maskEl = this.el;
30114         }
30115     },
30116     
30117     initEvents : function()
30118     {
30119         this.urlAPI = (window.createObjectURL && window) || 
30120                                 (window.URL && URL.revokeObjectURL && URL) || 
30121                                 (window.webkitURL && webkitURL);
30122                         
30123         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30124         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30125         
30126         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30127         this.selectorEl.hide();
30128         
30129         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30130         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30131         
30132         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30133         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30134         this.thumbEl.hide();
30135         
30136         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30137         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30138         
30139         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30140         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30141         this.errorEl.hide();
30142         
30143         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30144         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30145         this.footerEl.hide();
30146         
30147         this.setThumbBoxSize();
30148         
30149         this.bind();
30150         
30151         this.resize();
30152         
30153         this.fireEvent('initial', this);
30154     },
30155
30156     bind : function()
30157     {
30158         var _this = this;
30159         
30160         window.addEventListener("resize", function() { _this.resize(); } );
30161         
30162         this.bodyEl.on('click', this.beforeSelectFile, this);
30163         
30164         if(Roo.isTouch){
30165             this.bodyEl.on('touchstart', this.onTouchStart, this);
30166             this.bodyEl.on('touchmove', this.onTouchMove, this);
30167             this.bodyEl.on('touchend', this.onTouchEnd, this);
30168         }
30169         
30170         if(!Roo.isTouch){
30171             this.bodyEl.on('mousedown', this.onMouseDown, this);
30172             this.bodyEl.on('mousemove', this.onMouseMove, this);
30173             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30174             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30175             Roo.get(document).on('mouseup', this.onMouseUp, this);
30176         }
30177         
30178         this.selectorEl.on('change', this.onFileSelected, this);
30179     },
30180     
30181     reset : function()
30182     {    
30183         this.scale = 0;
30184         this.baseScale = 1;
30185         this.rotate = 0;
30186         this.baseRotate = 1;
30187         this.dragable = false;
30188         this.pinching = false;
30189         this.mouseX = 0;
30190         this.mouseY = 0;
30191         this.cropData = false;
30192         this.notifyEl.dom.innerHTML = this.emptyText;
30193         
30194         this.selectorEl.dom.value = '';
30195         
30196     },
30197     
30198     resize : function()
30199     {
30200         if(this.fireEvent('resize', this) != false){
30201             this.setThumbBoxPosition();
30202             this.setCanvasPosition();
30203         }
30204     },
30205     
30206     onFooterButtonClick : function(e, el, o, type)
30207     {
30208         switch (type) {
30209             case 'rotate-left' :
30210                 this.onRotateLeft(e);
30211                 break;
30212             case 'rotate-right' :
30213                 this.onRotateRight(e);
30214                 break;
30215             case 'picture' :
30216                 this.beforeSelectFile(e);
30217                 break;
30218             case 'trash' :
30219                 this.trash(e);
30220                 break;
30221             case 'crop' :
30222                 this.crop(e);
30223                 break;
30224             case 'download' :
30225                 this.download(e);
30226                 break;
30227             default :
30228                 break;
30229         }
30230         
30231         this.fireEvent('footerbuttonclick', this, type);
30232     },
30233     
30234     beforeSelectFile : function(e)
30235     {
30236         e.preventDefault();
30237         
30238         if(this.fireEvent('beforeselectfile', this) != false){
30239             this.selectorEl.dom.click();
30240         }
30241     },
30242     
30243     onFileSelected : function(e)
30244     {
30245         e.preventDefault();
30246         
30247         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30248             return;
30249         }
30250         
30251         var file = this.selectorEl.dom.files[0];
30252         
30253         if(this.fireEvent('inspect', this, file) != false){
30254             this.prepare(file);
30255         }
30256         
30257     },
30258     
30259     trash : function(e)
30260     {
30261         this.fireEvent('trash', this);
30262     },
30263     
30264     download : function(e)
30265     {
30266         this.fireEvent('download', this);
30267     },
30268     
30269     loadCanvas : function(src)
30270     {   
30271         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30272             
30273             this.reset();
30274             
30275             this.imageEl = document.createElement('img');
30276             
30277             var _this = this;
30278             
30279             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30280             
30281             this.imageEl.src = src;
30282         }
30283     },
30284     
30285     onLoadCanvas : function()
30286     {   
30287         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30288         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30289         
30290         this.bodyEl.un('click', this.beforeSelectFile, this);
30291         
30292         this.notifyEl.hide();
30293         this.thumbEl.show();
30294         this.footerEl.show();
30295         
30296         this.baseRotateLevel();
30297         
30298         if(this.isDocument){
30299             this.setThumbBoxSize();
30300         }
30301         
30302         this.setThumbBoxPosition();
30303         
30304         this.baseScaleLevel();
30305         
30306         this.draw();
30307         
30308         this.resize();
30309         
30310         this.canvasLoaded = true;
30311         
30312         if(this.loadMask){
30313             this.maskEl.unmask();
30314         }
30315         
30316     },
30317     
30318     setCanvasPosition : function()
30319     {   
30320         if(!this.canvasEl){
30321             return;
30322         }
30323         
30324         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30325         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30326         
30327         this.previewEl.setLeft(pw);
30328         this.previewEl.setTop(ph);
30329         
30330     },
30331     
30332     onMouseDown : function(e)
30333     {   
30334         e.stopEvent();
30335         
30336         this.dragable = true;
30337         this.pinching = false;
30338         
30339         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30340             this.dragable = false;
30341             return;
30342         }
30343         
30344         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30345         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30346         
30347     },
30348     
30349     onMouseMove : function(e)
30350     {   
30351         e.stopEvent();
30352         
30353         if(!this.canvasLoaded){
30354             return;
30355         }
30356         
30357         if (!this.dragable){
30358             return;
30359         }
30360         
30361         var minX = Math.ceil(this.thumbEl.getLeft(true));
30362         var minY = Math.ceil(this.thumbEl.getTop(true));
30363         
30364         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30365         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30366         
30367         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30368         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30369         
30370         x = x - this.mouseX;
30371         y = y - this.mouseY;
30372         
30373         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30374         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30375         
30376         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30377         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30378         
30379         this.previewEl.setLeft(bgX);
30380         this.previewEl.setTop(bgY);
30381         
30382         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30383         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30384     },
30385     
30386     onMouseUp : function(e)
30387     {   
30388         e.stopEvent();
30389         
30390         this.dragable = false;
30391     },
30392     
30393     onMouseWheel : function(e)
30394     {   
30395         e.stopEvent();
30396         
30397         this.startScale = this.scale;
30398         
30399         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30400         
30401         if(!this.zoomable()){
30402             this.scale = this.startScale;
30403             return;
30404         }
30405         
30406         this.draw();
30407         
30408         return;
30409     },
30410     
30411     zoomable : function()
30412     {
30413         var minScale = this.thumbEl.getWidth() / this.minWidth;
30414         
30415         if(this.minWidth < this.minHeight){
30416             minScale = this.thumbEl.getHeight() / this.minHeight;
30417         }
30418         
30419         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30420         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30421         
30422         if(
30423                 this.isDocument &&
30424                 (this.rotate == 0 || this.rotate == 180) && 
30425                 (
30426                     width > this.imageEl.OriginWidth || 
30427                     height > this.imageEl.OriginHeight ||
30428                     (width < this.minWidth && height < this.minHeight)
30429                 )
30430         ){
30431             return false;
30432         }
30433         
30434         if(
30435                 this.isDocument &&
30436                 (this.rotate == 90 || this.rotate == 270) && 
30437                 (
30438                     width > this.imageEl.OriginWidth || 
30439                     height > this.imageEl.OriginHeight ||
30440                     (width < this.minHeight && height < this.minWidth)
30441                 )
30442         ){
30443             return false;
30444         }
30445         
30446         if(
30447                 !this.isDocument &&
30448                 (this.rotate == 0 || this.rotate == 180) && 
30449                 (
30450                     width < this.minWidth || 
30451                     width > this.imageEl.OriginWidth || 
30452                     height < this.minHeight || 
30453                     height > this.imageEl.OriginHeight
30454                 )
30455         ){
30456             return false;
30457         }
30458         
30459         if(
30460                 !this.isDocument &&
30461                 (this.rotate == 90 || this.rotate == 270) && 
30462                 (
30463                     width < this.minHeight || 
30464                     width > this.imageEl.OriginWidth || 
30465                     height < this.minWidth || 
30466                     height > this.imageEl.OriginHeight
30467                 )
30468         ){
30469             return false;
30470         }
30471         
30472         return true;
30473         
30474     },
30475     
30476     onRotateLeft : function(e)
30477     {   
30478         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30479             
30480             var minScale = this.thumbEl.getWidth() / this.minWidth;
30481             
30482             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30483             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30484             
30485             this.startScale = this.scale;
30486             
30487             while (this.getScaleLevel() < minScale){
30488             
30489                 this.scale = this.scale + 1;
30490                 
30491                 if(!this.zoomable()){
30492                     break;
30493                 }
30494                 
30495                 if(
30496                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30497                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30498                 ){
30499                     continue;
30500                 }
30501                 
30502                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30503
30504                 this.draw();
30505                 
30506                 return;
30507             }
30508             
30509             this.scale = this.startScale;
30510             
30511             this.onRotateFail();
30512             
30513             return false;
30514         }
30515         
30516         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30517
30518         if(this.isDocument){
30519             this.setThumbBoxSize();
30520             this.setThumbBoxPosition();
30521             this.setCanvasPosition();
30522         }
30523         
30524         this.draw();
30525         
30526         this.fireEvent('rotate', this, 'left');
30527         
30528     },
30529     
30530     onRotateRight : function(e)
30531     {
30532         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30533             
30534             var minScale = this.thumbEl.getWidth() / this.minWidth;
30535         
30536             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30537             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30538             
30539             this.startScale = this.scale;
30540             
30541             while (this.getScaleLevel() < minScale){
30542             
30543                 this.scale = this.scale + 1;
30544                 
30545                 if(!this.zoomable()){
30546                     break;
30547                 }
30548                 
30549                 if(
30550                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30551                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30552                 ){
30553                     continue;
30554                 }
30555                 
30556                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30557
30558                 this.draw();
30559                 
30560                 return;
30561             }
30562             
30563             this.scale = this.startScale;
30564             
30565             this.onRotateFail();
30566             
30567             return false;
30568         }
30569         
30570         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30571
30572         if(this.isDocument){
30573             this.setThumbBoxSize();
30574             this.setThumbBoxPosition();
30575             this.setCanvasPosition();
30576         }
30577         
30578         this.draw();
30579         
30580         this.fireEvent('rotate', this, 'right');
30581     },
30582     
30583     onRotateFail : function()
30584     {
30585         this.errorEl.show(true);
30586         
30587         var _this = this;
30588         
30589         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30590     },
30591     
30592     draw : function()
30593     {
30594         this.previewEl.dom.innerHTML = '';
30595         
30596         var canvasEl = document.createElement("canvas");
30597         
30598         var contextEl = canvasEl.getContext("2d");
30599         
30600         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30601         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30602         var center = this.imageEl.OriginWidth / 2;
30603         
30604         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30605             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30606             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30607             center = this.imageEl.OriginHeight / 2;
30608         }
30609         
30610         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30611         
30612         contextEl.translate(center, center);
30613         contextEl.rotate(this.rotate * Math.PI / 180);
30614
30615         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30616         
30617         this.canvasEl = document.createElement("canvas");
30618         
30619         this.contextEl = this.canvasEl.getContext("2d");
30620         
30621         switch (this.rotate) {
30622             case 0 :
30623                 
30624                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30625                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30626                 
30627                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30628                 
30629                 break;
30630             case 90 : 
30631                 
30632                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30633                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30634                 
30635                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30636                     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);
30637                     break;
30638                 }
30639                 
30640                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30641                 
30642                 break;
30643             case 180 :
30644                 
30645                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30646                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30647                 
30648                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30649                     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);
30650                     break;
30651                 }
30652                 
30653                 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);
30654                 
30655                 break;
30656             case 270 :
30657                 
30658                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30659                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30660         
30661                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30662                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30663                     break;
30664                 }
30665                 
30666                 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);
30667                 
30668                 break;
30669             default : 
30670                 break;
30671         }
30672         
30673         this.previewEl.appendChild(this.canvasEl);
30674         
30675         this.setCanvasPosition();
30676     },
30677     
30678     crop : function()
30679     {
30680         if(!this.canvasLoaded){
30681             return;
30682         }
30683         
30684         var imageCanvas = document.createElement("canvas");
30685         
30686         var imageContext = imageCanvas.getContext("2d");
30687         
30688         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30689         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30690         
30691         var center = imageCanvas.width / 2;
30692         
30693         imageContext.translate(center, center);
30694         
30695         imageContext.rotate(this.rotate * Math.PI / 180);
30696         
30697         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30698         
30699         var canvas = document.createElement("canvas");
30700         
30701         var context = canvas.getContext("2d");
30702                 
30703         canvas.width = this.minWidth;
30704         canvas.height = this.minHeight;
30705
30706         switch (this.rotate) {
30707             case 0 :
30708                 
30709                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30710                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30711                 
30712                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30713                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30714                 
30715                 var targetWidth = this.minWidth - 2 * x;
30716                 var targetHeight = this.minHeight - 2 * y;
30717                 
30718                 var scale = 1;
30719                 
30720                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30721                     scale = targetWidth / width;
30722                 }
30723                 
30724                 if(x > 0 && y == 0){
30725                     scale = targetHeight / height;
30726                 }
30727                 
30728                 if(x > 0 && y > 0){
30729                     scale = targetWidth / width;
30730                     
30731                     if(width < height){
30732                         scale = targetHeight / height;
30733                     }
30734                 }
30735                 
30736                 context.scale(scale, scale);
30737                 
30738                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30739                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30740
30741                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30742                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30743
30744                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30745                 
30746                 break;
30747             case 90 : 
30748                 
30749                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30750                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30751                 
30752                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30753                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30754                 
30755                 var targetWidth = this.minWidth - 2 * x;
30756                 var targetHeight = this.minHeight - 2 * y;
30757                 
30758                 var scale = 1;
30759                 
30760                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30761                     scale = targetWidth / width;
30762                 }
30763                 
30764                 if(x > 0 && y == 0){
30765                     scale = targetHeight / height;
30766                 }
30767                 
30768                 if(x > 0 && y > 0){
30769                     scale = targetWidth / width;
30770                     
30771                     if(width < height){
30772                         scale = targetHeight / height;
30773                     }
30774                 }
30775                 
30776                 context.scale(scale, scale);
30777                 
30778                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30779                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30780
30781                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30782                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30783                 
30784                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30785                 
30786                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30787                 
30788                 break;
30789             case 180 :
30790                 
30791                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30792                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30793                 
30794                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30795                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30796                 
30797                 var targetWidth = this.minWidth - 2 * x;
30798                 var targetHeight = this.minHeight - 2 * y;
30799                 
30800                 var scale = 1;
30801                 
30802                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30803                     scale = targetWidth / width;
30804                 }
30805                 
30806                 if(x > 0 && y == 0){
30807                     scale = targetHeight / height;
30808                 }
30809                 
30810                 if(x > 0 && y > 0){
30811                     scale = targetWidth / width;
30812                     
30813                     if(width < height){
30814                         scale = targetHeight / height;
30815                     }
30816                 }
30817                 
30818                 context.scale(scale, scale);
30819                 
30820                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30821                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30822
30823                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30824                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30825
30826                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30827                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30828                 
30829                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30830                 
30831                 break;
30832             case 270 :
30833                 
30834                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30835                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30836                 
30837                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30838                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30839                 
30840                 var targetWidth = this.minWidth - 2 * x;
30841                 var targetHeight = this.minHeight - 2 * y;
30842                 
30843                 var scale = 1;
30844                 
30845                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30846                     scale = targetWidth / width;
30847                 }
30848                 
30849                 if(x > 0 && y == 0){
30850                     scale = targetHeight / height;
30851                 }
30852                 
30853                 if(x > 0 && y > 0){
30854                     scale = targetWidth / width;
30855                     
30856                     if(width < height){
30857                         scale = targetHeight / height;
30858                     }
30859                 }
30860                 
30861                 context.scale(scale, scale);
30862                 
30863                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30864                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30865
30866                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30867                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30868                 
30869                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30870                 
30871                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30872                 
30873                 break;
30874             default : 
30875                 break;
30876         }
30877         
30878         this.cropData = canvas.toDataURL(this.cropType);
30879         
30880         if(this.fireEvent('crop', this, this.cropData) !== false){
30881             this.process(this.file, this.cropData);
30882         }
30883         
30884         return;
30885         
30886     },
30887     
30888     setThumbBoxSize : function()
30889     {
30890         var width, height;
30891         
30892         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30893             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30894             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30895             
30896             this.minWidth = width;
30897             this.minHeight = height;
30898             
30899             if(this.rotate == 90 || this.rotate == 270){
30900                 this.minWidth = height;
30901                 this.minHeight = width;
30902             }
30903         }
30904         
30905         height = 300;
30906         width = Math.ceil(this.minWidth * height / this.minHeight);
30907         
30908         if(this.minWidth > this.minHeight){
30909             width = 300;
30910             height = Math.ceil(this.minHeight * width / this.minWidth);
30911         }
30912         
30913         this.thumbEl.setStyle({
30914             width : width + 'px',
30915             height : height + 'px'
30916         });
30917
30918         return;
30919             
30920     },
30921     
30922     setThumbBoxPosition : function()
30923     {
30924         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30925         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30926         
30927         this.thumbEl.setLeft(x);
30928         this.thumbEl.setTop(y);
30929         
30930     },
30931     
30932     baseRotateLevel : function()
30933     {
30934         this.baseRotate = 1;
30935         
30936         if(
30937                 typeof(this.exif) != 'undefined' &&
30938                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30939                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30940         ){
30941             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30942         }
30943         
30944         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30945         
30946     },
30947     
30948     baseScaleLevel : function()
30949     {
30950         var width, height;
30951         
30952         if(this.isDocument){
30953             
30954             if(this.baseRotate == 6 || this.baseRotate == 8){
30955             
30956                 height = this.thumbEl.getHeight();
30957                 this.baseScale = height / this.imageEl.OriginWidth;
30958
30959                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30960                     width = this.thumbEl.getWidth();
30961                     this.baseScale = width / this.imageEl.OriginHeight;
30962                 }
30963
30964                 return;
30965             }
30966
30967             height = this.thumbEl.getHeight();
30968             this.baseScale = height / this.imageEl.OriginHeight;
30969
30970             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30971                 width = this.thumbEl.getWidth();
30972                 this.baseScale = width / this.imageEl.OriginWidth;
30973             }
30974
30975             return;
30976         }
30977         
30978         if(this.baseRotate == 6 || this.baseRotate == 8){
30979             
30980             width = this.thumbEl.getHeight();
30981             this.baseScale = width / this.imageEl.OriginHeight;
30982             
30983             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30984                 height = this.thumbEl.getWidth();
30985                 this.baseScale = height / this.imageEl.OriginHeight;
30986             }
30987             
30988             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30989                 height = this.thumbEl.getWidth();
30990                 this.baseScale = height / this.imageEl.OriginHeight;
30991                 
30992                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30993                     width = this.thumbEl.getHeight();
30994                     this.baseScale = width / this.imageEl.OriginWidth;
30995                 }
30996             }
30997             
30998             return;
30999         }
31000         
31001         width = this.thumbEl.getWidth();
31002         this.baseScale = width / this.imageEl.OriginWidth;
31003         
31004         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31005             height = this.thumbEl.getHeight();
31006             this.baseScale = height / this.imageEl.OriginHeight;
31007         }
31008         
31009         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31010             
31011             height = this.thumbEl.getHeight();
31012             this.baseScale = height / this.imageEl.OriginHeight;
31013             
31014             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31015                 width = this.thumbEl.getWidth();
31016                 this.baseScale = width / this.imageEl.OriginWidth;
31017             }
31018             
31019         }
31020         
31021         return;
31022     },
31023     
31024     getScaleLevel : function()
31025     {
31026         return this.baseScale * Math.pow(1.1, this.scale);
31027     },
31028     
31029     onTouchStart : function(e)
31030     {
31031         if(!this.canvasLoaded){
31032             this.beforeSelectFile(e);
31033             return;
31034         }
31035         
31036         var touches = e.browserEvent.touches;
31037         
31038         if(!touches){
31039             return;
31040         }
31041         
31042         if(touches.length == 1){
31043             this.onMouseDown(e);
31044             return;
31045         }
31046         
31047         if(touches.length != 2){
31048             return;
31049         }
31050         
31051         var coords = [];
31052         
31053         for(var i = 0, finger; finger = touches[i]; i++){
31054             coords.push(finger.pageX, finger.pageY);
31055         }
31056         
31057         var x = Math.pow(coords[0] - coords[2], 2);
31058         var y = Math.pow(coords[1] - coords[3], 2);
31059         
31060         this.startDistance = Math.sqrt(x + y);
31061         
31062         this.startScale = this.scale;
31063         
31064         this.pinching = true;
31065         this.dragable = false;
31066         
31067     },
31068     
31069     onTouchMove : function(e)
31070     {
31071         if(!this.pinching && !this.dragable){
31072             return;
31073         }
31074         
31075         var touches = e.browserEvent.touches;
31076         
31077         if(!touches){
31078             return;
31079         }
31080         
31081         if(this.dragable){
31082             this.onMouseMove(e);
31083             return;
31084         }
31085         
31086         var coords = [];
31087         
31088         for(var i = 0, finger; finger = touches[i]; i++){
31089             coords.push(finger.pageX, finger.pageY);
31090         }
31091         
31092         var x = Math.pow(coords[0] - coords[2], 2);
31093         var y = Math.pow(coords[1] - coords[3], 2);
31094         
31095         this.endDistance = Math.sqrt(x + y);
31096         
31097         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31098         
31099         if(!this.zoomable()){
31100             this.scale = this.startScale;
31101             return;
31102         }
31103         
31104         this.draw();
31105         
31106     },
31107     
31108     onTouchEnd : function(e)
31109     {
31110         this.pinching = false;
31111         this.dragable = false;
31112         
31113     },
31114     
31115     process : function(file, crop)
31116     {
31117         if(this.loadMask){
31118             this.maskEl.mask(this.loadingText);
31119         }
31120         
31121         this.xhr = new XMLHttpRequest();
31122         
31123         file.xhr = this.xhr;
31124
31125         this.xhr.open(this.method, this.url, true);
31126         
31127         var headers = {
31128             "Accept": "application/json",
31129             "Cache-Control": "no-cache",
31130             "X-Requested-With": "XMLHttpRequest"
31131         };
31132         
31133         for (var headerName in headers) {
31134             var headerValue = headers[headerName];
31135             if (headerValue) {
31136                 this.xhr.setRequestHeader(headerName, headerValue);
31137             }
31138         }
31139         
31140         var _this = this;
31141         
31142         this.xhr.onload = function()
31143         {
31144             _this.xhrOnLoad(_this.xhr);
31145         }
31146         
31147         this.xhr.onerror = function()
31148         {
31149             _this.xhrOnError(_this.xhr);
31150         }
31151         
31152         var formData = new FormData();
31153
31154         formData.append('returnHTML', 'NO');
31155         
31156         if(crop){
31157             formData.append('crop', crop);
31158         }
31159         
31160         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31161             formData.append(this.paramName, file, file.name);
31162         }
31163         
31164         if(typeof(file.filename) != 'undefined'){
31165             formData.append('filename', file.filename);
31166         }
31167         
31168         if(typeof(file.mimetype) != 'undefined'){
31169             formData.append('mimetype', file.mimetype);
31170         }
31171         
31172         if(this.fireEvent('arrange', this, formData) != false){
31173             this.xhr.send(formData);
31174         };
31175     },
31176     
31177     xhrOnLoad : function(xhr)
31178     {
31179         if(this.loadMask){
31180             this.maskEl.unmask();
31181         }
31182         
31183         if (xhr.readyState !== 4) {
31184             this.fireEvent('exception', this, xhr);
31185             return;
31186         }
31187
31188         var response = Roo.decode(xhr.responseText);
31189         
31190         if(!response.success){
31191             this.fireEvent('exception', this, xhr);
31192             return;
31193         }
31194         
31195         var response = Roo.decode(xhr.responseText);
31196         
31197         this.fireEvent('upload', this, response);
31198         
31199     },
31200     
31201     xhrOnError : function()
31202     {
31203         if(this.loadMask){
31204             this.maskEl.unmask();
31205         }
31206         
31207         Roo.log('xhr on error');
31208         
31209         var response = Roo.decode(xhr.responseText);
31210           
31211         Roo.log(response);
31212         
31213     },
31214     
31215     prepare : function(file)
31216     {   
31217         if(this.loadMask){
31218             this.maskEl.mask(this.loadingText);
31219         }
31220         
31221         this.file = false;
31222         this.exif = {};
31223         
31224         if(typeof(file) === 'string'){
31225             this.loadCanvas(file);
31226             return;
31227         }
31228         
31229         if(!file || !this.urlAPI){
31230             return;
31231         }
31232         
31233         this.file = file;
31234         this.cropType = file.type;
31235         
31236         var _this = this;
31237         
31238         if(this.fireEvent('prepare', this, this.file) != false){
31239             
31240             var reader = new FileReader();
31241             
31242             reader.onload = function (e) {
31243                 if (e.target.error) {
31244                     Roo.log(e.target.error);
31245                     return;
31246                 }
31247                 
31248                 var buffer = e.target.result,
31249                     dataView = new DataView(buffer),
31250                     offset = 2,
31251                     maxOffset = dataView.byteLength - 4,
31252                     markerBytes,
31253                     markerLength;
31254                 
31255                 if (dataView.getUint16(0) === 0xffd8) {
31256                     while (offset < maxOffset) {
31257                         markerBytes = dataView.getUint16(offset);
31258                         
31259                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31260                             markerLength = dataView.getUint16(offset + 2) + 2;
31261                             if (offset + markerLength > dataView.byteLength) {
31262                                 Roo.log('Invalid meta data: Invalid segment size.');
31263                                 break;
31264                             }
31265                             
31266                             if(markerBytes == 0xffe1){
31267                                 _this.parseExifData(
31268                                     dataView,
31269                                     offset,
31270                                     markerLength
31271                                 );
31272                             }
31273                             
31274                             offset += markerLength;
31275                             
31276                             continue;
31277                         }
31278                         
31279                         break;
31280                     }
31281                     
31282                 }
31283                 
31284                 var url = _this.urlAPI.createObjectURL(_this.file);
31285                 
31286                 _this.loadCanvas(url);
31287                 
31288                 return;
31289             }
31290             
31291             reader.readAsArrayBuffer(this.file);
31292             
31293         }
31294         
31295     },
31296     
31297     parseExifData : function(dataView, offset, length)
31298     {
31299         var tiffOffset = offset + 10,
31300             littleEndian,
31301             dirOffset;
31302     
31303         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31304             // No Exif data, might be XMP data instead
31305             return;
31306         }
31307         
31308         // Check for the ASCII code for "Exif" (0x45786966):
31309         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31310             // No Exif data, might be XMP data instead
31311             return;
31312         }
31313         if (tiffOffset + 8 > dataView.byteLength) {
31314             Roo.log('Invalid Exif data: Invalid segment size.');
31315             return;
31316         }
31317         // Check for the two null bytes:
31318         if (dataView.getUint16(offset + 8) !== 0x0000) {
31319             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31320             return;
31321         }
31322         // Check the byte alignment:
31323         switch (dataView.getUint16(tiffOffset)) {
31324         case 0x4949:
31325             littleEndian = true;
31326             break;
31327         case 0x4D4D:
31328             littleEndian = false;
31329             break;
31330         default:
31331             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31332             return;
31333         }
31334         // Check for the TIFF tag marker (0x002A):
31335         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31336             Roo.log('Invalid Exif data: Missing TIFF marker.');
31337             return;
31338         }
31339         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31340         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31341         
31342         this.parseExifTags(
31343             dataView,
31344             tiffOffset,
31345             tiffOffset + dirOffset,
31346             littleEndian
31347         );
31348     },
31349     
31350     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31351     {
31352         var tagsNumber,
31353             dirEndOffset,
31354             i;
31355         if (dirOffset + 6 > dataView.byteLength) {
31356             Roo.log('Invalid Exif data: Invalid directory offset.');
31357             return;
31358         }
31359         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31360         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31361         if (dirEndOffset + 4 > dataView.byteLength) {
31362             Roo.log('Invalid Exif data: Invalid directory size.');
31363             return;
31364         }
31365         for (i = 0; i < tagsNumber; i += 1) {
31366             this.parseExifTag(
31367                 dataView,
31368                 tiffOffset,
31369                 dirOffset + 2 + 12 * i, // tag offset
31370                 littleEndian
31371             );
31372         }
31373         // Return the offset to the next directory:
31374         return dataView.getUint32(dirEndOffset, littleEndian);
31375     },
31376     
31377     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31378     {
31379         var tag = dataView.getUint16(offset, littleEndian);
31380         
31381         this.exif[tag] = this.getExifValue(
31382             dataView,
31383             tiffOffset,
31384             offset,
31385             dataView.getUint16(offset + 2, littleEndian), // tag type
31386             dataView.getUint32(offset + 4, littleEndian), // tag length
31387             littleEndian
31388         );
31389     },
31390     
31391     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31392     {
31393         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31394             tagSize,
31395             dataOffset,
31396             values,
31397             i,
31398             str,
31399             c;
31400     
31401         if (!tagType) {
31402             Roo.log('Invalid Exif data: Invalid tag type.');
31403             return;
31404         }
31405         
31406         tagSize = tagType.size * length;
31407         // Determine if the value is contained in the dataOffset bytes,
31408         // or if the value at the dataOffset is a pointer to the actual data:
31409         dataOffset = tagSize > 4 ?
31410                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31411         if (dataOffset + tagSize > dataView.byteLength) {
31412             Roo.log('Invalid Exif data: Invalid data offset.');
31413             return;
31414         }
31415         if (length === 1) {
31416             return tagType.getValue(dataView, dataOffset, littleEndian);
31417         }
31418         values = [];
31419         for (i = 0; i < length; i += 1) {
31420             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31421         }
31422         
31423         if (tagType.ascii) {
31424             str = '';
31425             // Concatenate the chars:
31426             for (i = 0; i < values.length; i += 1) {
31427                 c = values[i];
31428                 // Ignore the terminating NULL byte(s):
31429                 if (c === '\u0000') {
31430                     break;
31431                 }
31432                 str += c;
31433             }
31434             return str;
31435         }
31436         return values;
31437     }
31438     
31439 });
31440
31441 Roo.apply(Roo.bootstrap.UploadCropbox, {
31442     tags : {
31443         'Orientation': 0x0112
31444     },
31445     
31446     Orientation: {
31447             1: 0, //'top-left',
31448 //            2: 'top-right',
31449             3: 180, //'bottom-right',
31450 //            4: 'bottom-left',
31451 //            5: 'left-top',
31452             6: 90, //'right-top',
31453 //            7: 'right-bottom',
31454             8: 270 //'left-bottom'
31455     },
31456     
31457     exifTagTypes : {
31458         // byte, 8-bit unsigned int:
31459         1: {
31460             getValue: function (dataView, dataOffset) {
31461                 return dataView.getUint8(dataOffset);
31462             },
31463             size: 1
31464         },
31465         // ascii, 8-bit byte:
31466         2: {
31467             getValue: function (dataView, dataOffset) {
31468                 return String.fromCharCode(dataView.getUint8(dataOffset));
31469             },
31470             size: 1,
31471             ascii: true
31472         },
31473         // short, 16 bit int:
31474         3: {
31475             getValue: function (dataView, dataOffset, littleEndian) {
31476                 return dataView.getUint16(dataOffset, littleEndian);
31477             },
31478             size: 2
31479         },
31480         // long, 32 bit int:
31481         4: {
31482             getValue: function (dataView, dataOffset, littleEndian) {
31483                 return dataView.getUint32(dataOffset, littleEndian);
31484             },
31485             size: 4
31486         },
31487         // rational = two long values, first is numerator, second is denominator:
31488         5: {
31489             getValue: function (dataView, dataOffset, littleEndian) {
31490                 return dataView.getUint32(dataOffset, littleEndian) /
31491                     dataView.getUint32(dataOffset + 4, littleEndian);
31492             },
31493             size: 8
31494         },
31495         // slong, 32 bit signed int:
31496         9: {
31497             getValue: function (dataView, dataOffset, littleEndian) {
31498                 return dataView.getInt32(dataOffset, littleEndian);
31499             },
31500             size: 4
31501         },
31502         // srational, two slongs, first is numerator, second is denominator:
31503         10: {
31504             getValue: function (dataView, dataOffset, littleEndian) {
31505                 return dataView.getInt32(dataOffset, littleEndian) /
31506                     dataView.getInt32(dataOffset + 4, littleEndian);
31507             },
31508             size: 8
31509         }
31510     },
31511     
31512     footer : {
31513         STANDARD : [
31514             {
31515                 tag : 'div',
31516                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31517                 action : 'rotate-left',
31518                 cn : [
31519                     {
31520                         tag : 'button',
31521                         cls : 'btn btn-default',
31522                         html : '<i class="fa fa-undo"></i>'
31523                     }
31524                 ]
31525             },
31526             {
31527                 tag : 'div',
31528                 cls : 'btn-group roo-upload-cropbox-picture',
31529                 action : 'picture',
31530                 cn : [
31531                     {
31532                         tag : 'button',
31533                         cls : 'btn btn-default',
31534                         html : '<i class="fa fa-picture-o"></i>'
31535                     }
31536                 ]
31537             },
31538             {
31539                 tag : 'div',
31540                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31541                 action : 'rotate-right',
31542                 cn : [
31543                     {
31544                         tag : 'button',
31545                         cls : 'btn btn-default',
31546                         html : '<i class="fa fa-repeat"></i>'
31547                     }
31548                 ]
31549             }
31550         ],
31551         DOCUMENT : [
31552             {
31553                 tag : 'div',
31554                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31555                 action : 'rotate-left',
31556                 cn : [
31557                     {
31558                         tag : 'button',
31559                         cls : 'btn btn-default',
31560                         html : '<i class="fa fa-undo"></i>'
31561                     }
31562                 ]
31563             },
31564             {
31565                 tag : 'div',
31566                 cls : 'btn-group roo-upload-cropbox-download',
31567                 action : 'download',
31568                 cn : [
31569                     {
31570                         tag : 'button',
31571                         cls : 'btn btn-default',
31572                         html : '<i class="fa fa-download"></i>'
31573                     }
31574                 ]
31575             },
31576             {
31577                 tag : 'div',
31578                 cls : 'btn-group roo-upload-cropbox-crop',
31579                 action : 'crop',
31580                 cn : [
31581                     {
31582                         tag : 'button',
31583                         cls : 'btn btn-default',
31584                         html : '<i class="fa fa-crop"></i>'
31585                     }
31586                 ]
31587             },
31588             {
31589                 tag : 'div',
31590                 cls : 'btn-group roo-upload-cropbox-trash',
31591                 action : 'trash',
31592                 cn : [
31593                     {
31594                         tag : 'button',
31595                         cls : 'btn btn-default',
31596                         html : '<i class="fa fa-trash"></i>'
31597                     }
31598                 ]
31599             },
31600             {
31601                 tag : 'div',
31602                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31603                 action : 'rotate-right',
31604                 cn : [
31605                     {
31606                         tag : 'button',
31607                         cls : 'btn btn-default',
31608                         html : '<i class="fa fa-repeat"></i>'
31609                     }
31610                 ]
31611             }
31612         ],
31613         ROTATOR : [
31614             {
31615                 tag : 'div',
31616                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31617                 action : 'rotate-left',
31618                 cn : [
31619                     {
31620                         tag : 'button',
31621                         cls : 'btn btn-default',
31622                         html : '<i class="fa fa-undo"></i>'
31623                     }
31624                 ]
31625             },
31626             {
31627                 tag : 'div',
31628                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31629                 action : 'rotate-right',
31630                 cn : [
31631                     {
31632                         tag : 'button',
31633                         cls : 'btn btn-default',
31634                         html : '<i class="fa fa-repeat"></i>'
31635                     }
31636                 ]
31637             }
31638         ]
31639     }
31640 });
31641
31642 /*
31643 * Licence: LGPL
31644 */
31645
31646 /**
31647  * @class Roo.bootstrap.DocumentManager
31648  * @extends Roo.bootstrap.Component
31649  * Bootstrap DocumentManager class
31650  * @cfg {String} paramName default 'imageUpload'
31651  * @cfg {String} toolTipName default 'filename'
31652  * @cfg {String} method default POST
31653  * @cfg {String} url action url
31654  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31655  * @cfg {Boolean} multiple multiple upload default true
31656  * @cfg {Number} thumbSize default 300
31657  * @cfg {String} fieldLabel
31658  * @cfg {Number} labelWidth default 4
31659  * @cfg {String} labelAlign (left|top) default left
31660  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31661 * @cfg {Number} labellg set the width of label (1-12)
31662  * @cfg {Number} labelmd set the width of label (1-12)
31663  * @cfg {Number} labelsm set the width of label (1-12)
31664  * @cfg {Number} labelxs set the width of label (1-12)
31665  * 
31666  * @constructor
31667  * Create a new DocumentManager
31668  * @param {Object} config The config object
31669  */
31670
31671 Roo.bootstrap.DocumentManager = function(config){
31672     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31673     
31674     this.files = [];
31675     this.delegates = [];
31676     
31677     this.addEvents({
31678         /**
31679          * @event initial
31680          * Fire when initial the DocumentManager
31681          * @param {Roo.bootstrap.DocumentManager} this
31682          */
31683         "initial" : true,
31684         /**
31685          * @event inspect
31686          * inspect selected file
31687          * @param {Roo.bootstrap.DocumentManager} this
31688          * @param {File} file
31689          */
31690         "inspect" : true,
31691         /**
31692          * @event exception
31693          * Fire when xhr load exception
31694          * @param {Roo.bootstrap.DocumentManager} this
31695          * @param {XMLHttpRequest} xhr
31696          */
31697         "exception" : true,
31698         /**
31699          * @event afterupload
31700          * Fire when xhr load exception
31701          * @param {Roo.bootstrap.DocumentManager} this
31702          * @param {XMLHttpRequest} xhr
31703          */
31704         "afterupload" : true,
31705         /**
31706          * @event prepare
31707          * prepare the form data
31708          * @param {Roo.bootstrap.DocumentManager} this
31709          * @param {Object} formData
31710          */
31711         "prepare" : true,
31712         /**
31713          * @event remove
31714          * Fire when remove the file
31715          * @param {Roo.bootstrap.DocumentManager} this
31716          * @param {Object} file
31717          */
31718         "remove" : true,
31719         /**
31720          * @event refresh
31721          * Fire after refresh the file
31722          * @param {Roo.bootstrap.DocumentManager} this
31723          */
31724         "refresh" : true,
31725         /**
31726          * @event click
31727          * Fire after click the image
31728          * @param {Roo.bootstrap.DocumentManager} this
31729          * @param {Object} file
31730          */
31731         "click" : true,
31732         /**
31733          * @event edit
31734          * Fire when upload a image and editable set to true
31735          * @param {Roo.bootstrap.DocumentManager} this
31736          * @param {Object} file
31737          */
31738         "edit" : true,
31739         /**
31740          * @event beforeselectfile
31741          * Fire before select file
31742          * @param {Roo.bootstrap.DocumentManager} this
31743          */
31744         "beforeselectfile" : true,
31745         /**
31746          * @event process
31747          * Fire before process file
31748          * @param {Roo.bootstrap.DocumentManager} this
31749          * @param {Object} file
31750          */
31751         "process" : true,
31752         /**
31753          * @event previewrendered
31754          * Fire when preview rendered
31755          * @param {Roo.bootstrap.DocumentManager} this
31756          * @param {Object} file
31757          */
31758         "previewrendered" : true,
31759         /**
31760          */
31761         "previewResize" : true
31762         
31763     });
31764 };
31765
31766 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31767     
31768     boxes : 0,
31769     inputName : '',
31770     thumbSize : 300,
31771     multiple : true,
31772     files : false,
31773     method : 'POST',
31774     url : '',
31775     paramName : 'imageUpload',
31776     toolTipName : 'filename',
31777     fieldLabel : '',
31778     labelWidth : 4,
31779     labelAlign : 'left',
31780     editable : true,
31781     delegates : false,
31782     xhr : false, 
31783     
31784     labellg : 0,
31785     labelmd : 0,
31786     labelsm : 0,
31787     labelxs : 0,
31788     
31789     getAutoCreate : function()
31790     {   
31791         var managerWidget = {
31792             tag : 'div',
31793             cls : 'roo-document-manager',
31794             cn : [
31795                 {
31796                     tag : 'input',
31797                     cls : 'roo-document-manager-selector',
31798                     type : 'file'
31799                 },
31800                 {
31801                     tag : 'div',
31802                     cls : 'roo-document-manager-uploader',
31803                     cn : [
31804                         {
31805                             tag : 'div',
31806                             cls : 'roo-document-manager-upload-btn',
31807                             html : '<i class="fa fa-plus"></i>'
31808                         }
31809                     ]
31810                     
31811                 }
31812             ]
31813         };
31814         
31815         var content = [
31816             {
31817                 tag : 'div',
31818                 cls : 'column col-md-12',
31819                 cn : managerWidget
31820             }
31821         ];
31822         
31823         if(this.fieldLabel.length){
31824             
31825             content = [
31826                 {
31827                     tag : 'div',
31828                     cls : 'column col-md-12',
31829                     html : this.fieldLabel
31830                 },
31831                 {
31832                     tag : 'div',
31833                     cls : 'column col-md-12',
31834                     cn : managerWidget
31835                 }
31836             ];
31837
31838             if(this.labelAlign == 'left'){
31839                 content = [
31840                     {
31841                         tag : 'div',
31842                         cls : 'column',
31843                         html : this.fieldLabel
31844                     },
31845                     {
31846                         tag : 'div',
31847                         cls : 'column',
31848                         cn : managerWidget
31849                     }
31850                 ];
31851                 
31852                 if(this.labelWidth > 12){
31853                     content[0].style = "width: " + this.labelWidth + 'px';
31854                 }
31855
31856                 if(this.labelWidth < 13 && this.labelmd == 0){
31857                     this.labelmd = this.labelWidth;
31858                 }
31859
31860                 if(this.labellg > 0){
31861                     content[0].cls += ' col-lg-' + this.labellg;
31862                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31863                 }
31864
31865                 if(this.labelmd > 0){
31866                     content[0].cls += ' col-md-' + this.labelmd;
31867                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31868                 }
31869
31870                 if(this.labelsm > 0){
31871                     content[0].cls += ' col-sm-' + this.labelsm;
31872                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31873                 }
31874
31875                 if(this.labelxs > 0){
31876                     content[0].cls += ' col-xs-' + this.labelxs;
31877                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31878                 }
31879                 
31880             }
31881         }
31882         
31883         var cfg = {
31884             tag : 'div',
31885             cls : 'row clearfix',
31886             cn : content
31887         };
31888         
31889         return cfg;
31890         
31891     },
31892     
31893     initEvents : function()
31894     {
31895         this.managerEl = this.el.select('.roo-document-manager', true).first();
31896         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31897         
31898         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31899         this.selectorEl.hide();
31900         
31901         if(this.multiple){
31902             this.selectorEl.attr('multiple', 'multiple');
31903         }
31904         
31905         this.selectorEl.on('change', this.onFileSelected, this);
31906         
31907         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31908         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31909         
31910         this.uploader.on('click', this.onUploaderClick, this);
31911         
31912         this.renderProgressDialog();
31913         
31914         var _this = this;
31915         
31916         window.addEventListener("resize", function() { _this.refresh(); } );
31917         
31918         this.fireEvent('initial', this);
31919     },
31920     
31921     renderProgressDialog : function()
31922     {
31923         var _this = this;
31924         
31925         this.progressDialog = new Roo.bootstrap.Modal({
31926             cls : 'roo-document-manager-progress-dialog',
31927             allow_close : false,
31928             animate : false,
31929             title : '',
31930             buttons : [
31931                 {
31932                     name  :'cancel',
31933                     weight : 'danger',
31934                     html : 'Cancel'
31935                 }
31936             ], 
31937             listeners : { 
31938                 btnclick : function() {
31939                     _this.uploadCancel();
31940                     this.hide();
31941                 }
31942             }
31943         });
31944          
31945         this.progressDialog.render(Roo.get(document.body));
31946          
31947         this.progress = new Roo.bootstrap.Progress({
31948             cls : 'roo-document-manager-progress',
31949             active : true,
31950             striped : true
31951         });
31952         
31953         this.progress.render(this.progressDialog.getChildContainer());
31954         
31955         this.progressBar = new Roo.bootstrap.ProgressBar({
31956             cls : 'roo-document-manager-progress-bar',
31957             aria_valuenow : 0,
31958             aria_valuemin : 0,
31959             aria_valuemax : 12,
31960             panel : 'success'
31961         });
31962         
31963         this.progressBar.render(this.progress.getChildContainer());
31964     },
31965     
31966     onUploaderClick : function(e)
31967     {
31968         e.preventDefault();
31969      
31970         if(this.fireEvent('beforeselectfile', this) != false){
31971             this.selectorEl.dom.click();
31972         }
31973         
31974     },
31975     
31976     onFileSelected : function(e)
31977     {
31978         e.preventDefault();
31979         
31980         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31981             return;
31982         }
31983         
31984         Roo.each(this.selectorEl.dom.files, function(file){
31985             if(this.fireEvent('inspect', this, file) != false){
31986                 this.files.push(file);
31987             }
31988         }, this);
31989         
31990         this.queue();
31991         
31992     },
31993     
31994     queue : function()
31995     {
31996         this.selectorEl.dom.value = '';
31997         
31998         if(!this.files || !this.files.length){
31999             return;
32000         }
32001         
32002         if(this.boxes > 0 && this.files.length > this.boxes){
32003             this.files = this.files.slice(0, this.boxes);
32004         }
32005         
32006         this.uploader.show();
32007         
32008         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32009             this.uploader.hide();
32010         }
32011         
32012         var _this = this;
32013         
32014         var files = [];
32015         
32016         var docs = [];
32017         
32018         Roo.each(this.files, function(file){
32019             
32020             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32021                 var f = this.renderPreview(file);
32022                 files.push(f);
32023                 return;
32024             }
32025             
32026             if(file.type.indexOf('image') != -1){
32027                 this.delegates.push(
32028                     (function(){
32029                         _this.process(file);
32030                     }).createDelegate(this)
32031                 );
32032         
32033                 return;
32034             }
32035             
32036             docs.push(
32037                 (function(){
32038                     _this.process(file);
32039                 }).createDelegate(this)
32040             );
32041             
32042         }, this);
32043         
32044         this.files = files;
32045         
32046         this.delegates = this.delegates.concat(docs);
32047         
32048         if(!this.delegates.length){
32049             this.refresh();
32050             return;
32051         }
32052         
32053         this.progressBar.aria_valuemax = this.delegates.length;
32054         
32055         this.arrange();
32056         
32057         return;
32058     },
32059     
32060     arrange : function()
32061     {
32062         if(!this.delegates.length){
32063             this.progressDialog.hide();
32064             this.refresh();
32065             return;
32066         }
32067         
32068         var delegate = this.delegates.shift();
32069         
32070         this.progressDialog.show();
32071         
32072         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32073         
32074         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32075         
32076         delegate();
32077     },
32078     
32079     refresh : function()
32080     {
32081         this.uploader.show();
32082         
32083         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32084             this.uploader.hide();
32085         }
32086         
32087         Roo.isTouch ? this.closable(false) : this.closable(true);
32088         
32089         this.fireEvent('refresh', this);
32090     },
32091     
32092     onRemove : function(e, el, o)
32093     {
32094         e.preventDefault();
32095         
32096         this.fireEvent('remove', this, o);
32097         
32098     },
32099     
32100     remove : function(o)
32101     {
32102         var files = [];
32103         
32104         Roo.each(this.files, function(file){
32105             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32106                 files.push(file);
32107                 return;
32108             }
32109
32110             o.target.remove();
32111
32112         }, this);
32113         
32114         this.files = files;
32115         
32116         this.refresh();
32117     },
32118     
32119     clear : function()
32120     {
32121         Roo.each(this.files, function(file){
32122             if(!file.target){
32123                 return;
32124             }
32125             
32126             file.target.remove();
32127
32128         }, this);
32129         
32130         this.files = [];
32131         
32132         this.refresh();
32133     },
32134     
32135     onClick : function(e, el, o)
32136     {
32137         e.preventDefault();
32138         
32139         this.fireEvent('click', this, o);
32140         
32141     },
32142     
32143     closable : function(closable)
32144     {
32145         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32146             
32147             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32148             
32149             if(closable){
32150                 el.show();
32151                 return;
32152             }
32153             
32154             el.hide();
32155             
32156         }, this);
32157     },
32158     
32159     xhrOnLoad : function(xhr)
32160     {
32161         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32162             el.remove();
32163         }, this);
32164         
32165         if (xhr.readyState !== 4) {
32166             this.arrange();
32167             this.fireEvent('exception', this, xhr);
32168             return;
32169         }
32170
32171         var response = Roo.decode(xhr.responseText);
32172         
32173         if(!response.success){
32174             this.arrange();
32175             this.fireEvent('exception', this, xhr);
32176             return;
32177         }
32178         
32179         var file = this.renderPreview(response.data);
32180         
32181         this.files.push(file);
32182         
32183         this.arrange();
32184         
32185         this.fireEvent('afterupload', this, xhr);
32186         
32187     },
32188     
32189     xhrOnError : function(xhr)
32190     {
32191         Roo.log('xhr on error');
32192         
32193         var response = Roo.decode(xhr.responseText);
32194           
32195         Roo.log(response);
32196         
32197         this.arrange();
32198     },
32199     
32200     process : function(file)
32201     {
32202         if(this.fireEvent('process', this, file) !== false){
32203             if(this.editable && file.type.indexOf('image') != -1){
32204                 this.fireEvent('edit', this, file);
32205                 return;
32206             }
32207
32208             this.uploadStart(file, false);
32209
32210             return;
32211         }
32212         
32213     },
32214     
32215     uploadStart : function(file, crop)
32216     {
32217         this.xhr = new XMLHttpRequest();
32218         
32219         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32220             this.arrange();
32221             return;
32222         }
32223         
32224         file.xhr = this.xhr;
32225             
32226         this.managerEl.createChild({
32227             tag : 'div',
32228             cls : 'roo-document-manager-loading',
32229             cn : [
32230                 {
32231                     tag : 'div',
32232                     tooltip : file.name,
32233                     cls : 'roo-document-manager-thumb',
32234                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32235                 }
32236             ]
32237
32238         });
32239
32240         this.xhr.open(this.method, this.url, true);
32241         
32242         var headers = {
32243             "Accept": "application/json",
32244             "Cache-Control": "no-cache",
32245             "X-Requested-With": "XMLHttpRequest"
32246         };
32247         
32248         for (var headerName in headers) {
32249             var headerValue = headers[headerName];
32250             if (headerValue) {
32251                 this.xhr.setRequestHeader(headerName, headerValue);
32252             }
32253         }
32254         
32255         var _this = this;
32256         
32257         this.xhr.onload = function()
32258         {
32259             _this.xhrOnLoad(_this.xhr);
32260         }
32261         
32262         this.xhr.onerror = function()
32263         {
32264             _this.xhrOnError(_this.xhr);
32265         }
32266         
32267         var formData = new FormData();
32268
32269         formData.append('returnHTML', 'NO');
32270         
32271         if(crop){
32272             formData.append('crop', crop);
32273         }
32274         
32275         formData.append(this.paramName, file, file.name);
32276         
32277         var options = {
32278             file : file, 
32279             manually : false
32280         };
32281         
32282         if(this.fireEvent('prepare', this, formData, options) != false){
32283             
32284             if(options.manually){
32285                 return;
32286             }
32287             
32288             this.xhr.send(formData);
32289             return;
32290         };
32291         
32292         this.uploadCancel();
32293     },
32294     
32295     uploadCancel : function()
32296     {
32297         if (this.xhr) {
32298             this.xhr.abort();
32299         }
32300         
32301         this.delegates = [];
32302         
32303         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32304             el.remove();
32305         }, this);
32306         
32307         this.arrange();
32308     },
32309     
32310     renderPreview : function(file)
32311     {
32312         if(typeof(file.target) != 'undefined' && file.target){
32313             return file;
32314         }
32315         
32316         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32317         
32318         var previewEl = this.managerEl.createChild({
32319             tag : 'div',
32320             cls : 'roo-document-manager-preview',
32321             cn : [
32322                 {
32323                     tag : 'div',
32324                     tooltip : file[this.toolTipName],
32325                     cls : 'roo-document-manager-thumb',
32326                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32327                 },
32328                 {
32329                     tag : 'button',
32330                     cls : 'close',
32331                     html : '<i class="fa fa-times-circle"></i>'
32332                 }
32333             ]
32334         });
32335
32336         var close = previewEl.select('button.close', true).first();
32337
32338         close.on('click', this.onRemove, this, file);
32339
32340         file.target = previewEl;
32341
32342         var image = previewEl.select('img', true).first();
32343         
32344         var _this = this;
32345         
32346         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32347         
32348         image.on('click', this.onClick, this, file);
32349         
32350         this.fireEvent('previewrendered', this, file);
32351         
32352         return file;
32353         
32354     },
32355     
32356     onPreviewLoad : function(file, image)
32357     {
32358         if(typeof(file.target) == 'undefined' || !file.target){
32359             return;
32360         }
32361         
32362         var width = image.dom.naturalWidth || image.dom.width;
32363         var height = image.dom.naturalHeight || image.dom.height;
32364         
32365         if(!this.previewResize) {
32366             return;
32367         }
32368         
32369         if(width > height){
32370             file.target.addClass('wide');
32371             return;
32372         }
32373         
32374         file.target.addClass('tall');
32375         return;
32376         
32377     },
32378     
32379     uploadFromSource : function(file, crop)
32380     {
32381         this.xhr = new XMLHttpRequest();
32382         
32383         this.managerEl.createChild({
32384             tag : 'div',
32385             cls : 'roo-document-manager-loading',
32386             cn : [
32387                 {
32388                     tag : 'div',
32389                     tooltip : file.name,
32390                     cls : 'roo-document-manager-thumb',
32391                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32392                 }
32393             ]
32394
32395         });
32396
32397         this.xhr.open(this.method, this.url, true);
32398         
32399         var headers = {
32400             "Accept": "application/json",
32401             "Cache-Control": "no-cache",
32402             "X-Requested-With": "XMLHttpRequest"
32403         };
32404         
32405         for (var headerName in headers) {
32406             var headerValue = headers[headerName];
32407             if (headerValue) {
32408                 this.xhr.setRequestHeader(headerName, headerValue);
32409             }
32410         }
32411         
32412         var _this = this;
32413         
32414         this.xhr.onload = function()
32415         {
32416             _this.xhrOnLoad(_this.xhr);
32417         }
32418         
32419         this.xhr.onerror = function()
32420         {
32421             _this.xhrOnError(_this.xhr);
32422         }
32423         
32424         var formData = new FormData();
32425
32426         formData.append('returnHTML', 'NO');
32427         
32428         formData.append('crop', crop);
32429         
32430         if(typeof(file.filename) != 'undefined'){
32431             formData.append('filename', file.filename);
32432         }
32433         
32434         if(typeof(file.mimetype) != 'undefined'){
32435             formData.append('mimetype', file.mimetype);
32436         }
32437         
32438         Roo.log(formData);
32439         
32440         if(this.fireEvent('prepare', this, formData) != false){
32441             this.xhr.send(formData);
32442         };
32443     }
32444 });
32445
32446 /*
32447 * Licence: LGPL
32448 */
32449
32450 /**
32451  * @class Roo.bootstrap.DocumentViewer
32452  * @extends Roo.bootstrap.Component
32453  * Bootstrap DocumentViewer class
32454  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32455  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32456  * 
32457  * @constructor
32458  * Create a new DocumentViewer
32459  * @param {Object} config The config object
32460  */
32461
32462 Roo.bootstrap.DocumentViewer = function(config){
32463     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32464     
32465     this.addEvents({
32466         /**
32467          * @event initial
32468          * Fire after initEvent
32469          * @param {Roo.bootstrap.DocumentViewer} this
32470          */
32471         "initial" : true,
32472         /**
32473          * @event click
32474          * Fire after click
32475          * @param {Roo.bootstrap.DocumentViewer} this
32476          */
32477         "click" : true,
32478         /**
32479          * @event download
32480          * Fire after download button
32481          * @param {Roo.bootstrap.DocumentViewer} this
32482          */
32483         "download" : true,
32484         /**
32485          * @event trash
32486          * Fire after trash button
32487          * @param {Roo.bootstrap.DocumentViewer} this
32488          */
32489         "trash" : true
32490         
32491     });
32492 };
32493
32494 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32495     
32496     showDownload : true,
32497     
32498     showTrash : true,
32499     
32500     getAutoCreate : function()
32501     {
32502         var cfg = {
32503             tag : 'div',
32504             cls : 'roo-document-viewer',
32505             cn : [
32506                 {
32507                     tag : 'div',
32508                     cls : 'roo-document-viewer-body',
32509                     cn : [
32510                         {
32511                             tag : 'div',
32512                             cls : 'roo-document-viewer-thumb',
32513                             cn : [
32514                                 {
32515                                     tag : 'img',
32516                                     cls : 'roo-document-viewer-image'
32517                                 }
32518                             ]
32519                         }
32520                     ]
32521                 },
32522                 {
32523                     tag : 'div',
32524                     cls : 'roo-document-viewer-footer',
32525                     cn : {
32526                         tag : 'div',
32527                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32528                         cn : [
32529                             {
32530                                 tag : 'div',
32531                                 cls : 'btn-group roo-document-viewer-download',
32532                                 cn : [
32533                                     {
32534                                         tag : 'button',
32535                                         cls : 'btn btn-default',
32536                                         html : '<i class="fa fa-download"></i>'
32537                                     }
32538                                 ]
32539                             },
32540                             {
32541                                 tag : 'div',
32542                                 cls : 'btn-group roo-document-viewer-trash',
32543                                 cn : [
32544                                     {
32545                                         tag : 'button',
32546                                         cls : 'btn btn-default',
32547                                         html : '<i class="fa fa-trash"></i>'
32548                                     }
32549                                 ]
32550                             }
32551                         ]
32552                     }
32553                 }
32554             ]
32555         };
32556         
32557         return cfg;
32558     },
32559     
32560     initEvents : function()
32561     {
32562         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32563         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32564         
32565         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32566         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32567         
32568         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32569         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32570         
32571         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32572         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32573         
32574         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32575         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32576         
32577         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32578         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32579         
32580         this.bodyEl.on('click', this.onClick, this);
32581         this.downloadBtn.on('click', this.onDownload, this);
32582         this.trashBtn.on('click', this.onTrash, this);
32583         
32584         this.downloadBtn.hide();
32585         this.trashBtn.hide();
32586         
32587         if(this.showDownload){
32588             this.downloadBtn.show();
32589         }
32590         
32591         if(this.showTrash){
32592             this.trashBtn.show();
32593         }
32594         
32595         if(!this.showDownload && !this.showTrash) {
32596             this.footerEl.hide();
32597         }
32598         
32599     },
32600     
32601     initial : function()
32602     {
32603         this.fireEvent('initial', this);
32604         
32605     },
32606     
32607     onClick : function(e)
32608     {
32609         e.preventDefault();
32610         
32611         this.fireEvent('click', this);
32612     },
32613     
32614     onDownload : function(e)
32615     {
32616         e.preventDefault();
32617         
32618         this.fireEvent('download', this);
32619     },
32620     
32621     onTrash : function(e)
32622     {
32623         e.preventDefault();
32624         
32625         this.fireEvent('trash', this);
32626     }
32627     
32628 });
32629 /*
32630  * - LGPL
32631  *
32632  * nav progress bar
32633  * 
32634  */
32635
32636 /**
32637  * @class Roo.bootstrap.NavProgressBar
32638  * @extends Roo.bootstrap.Component
32639  * Bootstrap NavProgressBar class
32640  * 
32641  * @constructor
32642  * Create a new nav progress bar
32643  * @param {Object} config The config object
32644  */
32645
32646 Roo.bootstrap.NavProgressBar = function(config){
32647     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32648
32649     this.bullets = this.bullets || [];
32650    
32651 //    Roo.bootstrap.NavProgressBar.register(this);
32652      this.addEvents({
32653         /**
32654              * @event changed
32655              * Fires when the active item changes
32656              * @param {Roo.bootstrap.NavProgressBar} this
32657              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32658              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32659          */
32660         'changed': true
32661      });
32662     
32663 };
32664
32665 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32666     
32667     bullets : [],
32668     barItems : [],
32669     
32670     getAutoCreate : function()
32671     {
32672         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32673         
32674         cfg = {
32675             tag : 'div',
32676             cls : 'roo-navigation-bar-group',
32677             cn : [
32678                 {
32679                     tag : 'div',
32680                     cls : 'roo-navigation-top-bar'
32681                 },
32682                 {
32683                     tag : 'div',
32684                     cls : 'roo-navigation-bullets-bar',
32685                     cn : [
32686                         {
32687                             tag : 'ul',
32688                             cls : 'roo-navigation-bar'
32689                         }
32690                     ]
32691                 },
32692                 
32693                 {
32694                     tag : 'div',
32695                     cls : 'roo-navigation-bottom-bar'
32696                 }
32697             ]
32698             
32699         };
32700         
32701         return cfg;
32702         
32703     },
32704     
32705     initEvents: function() 
32706     {
32707         
32708     },
32709     
32710     onRender : function(ct, position) 
32711     {
32712         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32713         
32714         if(this.bullets.length){
32715             Roo.each(this.bullets, function(b){
32716                this.addItem(b);
32717             }, this);
32718         }
32719         
32720         this.format();
32721         
32722     },
32723     
32724     addItem : function(cfg)
32725     {
32726         var item = new Roo.bootstrap.NavProgressItem(cfg);
32727         
32728         item.parentId = this.id;
32729         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32730         
32731         if(cfg.html){
32732             var top = new Roo.bootstrap.Element({
32733                 tag : 'div',
32734                 cls : 'roo-navigation-bar-text'
32735             });
32736             
32737             var bottom = new Roo.bootstrap.Element({
32738                 tag : 'div',
32739                 cls : 'roo-navigation-bar-text'
32740             });
32741             
32742             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32743             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32744             
32745             var topText = new Roo.bootstrap.Element({
32746                 tag : 'span',
32747                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32748             });
32749             
32750             var bottomText = new Roo.bootstrap.Element({
32751                 tag : 'span',
32752                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32753             });
32754             
32755             topText.onRender(top.el, null);
32756             bottomText.onRender(bottom.el, null);
32757             
32758             item.topEl = top;
32759             item.bottomEl = bottom;
32760         }
32761         
32762         this.barItems.push(item);
32763         
32764         return item;
32765     },
32766     
32767     getActive : function()
32768     {
32769         var active = false;
32770         
32771         Roo.each(this.barItems, function(v){
32772             
32773             if (!v.isActive()) {
32774                 return;
32775             }
32776             
32777             active = v;
32778             return false;
32779             
32780         });
32781         
32782         return active;
32783     },
32784     
32785     setActiveItem : function(item)
32786     {
32787         var prev = false;
32788         
32789         Roo.each(this.barItems, function(v){
32790             if (v.rid == item.rid) {
32791                 return ;
32792             }
32793             
32794             if (v.isActive()) {
32795                 v.setActive(false);
32796                 prev = v;
32797             }
32798         });
32799
32800         item.setActive(true);
32801         
32802         this.fireEvent('changed', this, item, prev);
32803     },
32804     
32805     getBarItem: function(rid)
32806     {
32807         var ret = false;
32808         
32809         Roo.each(this.barItems, function(e) {
32810             if (e.rid != rid) {
32811                 return;
32812             }
32813             
32814             ret =  e;
32815             return false;
32816         });
32817         
32818         return ret;
32819     },
32820     
32821     indexOfItem : function(item)
32822     {
32823         var index = false;
32824         
32825         Roo.each(this.barItems, function(v, i){
32826             
32827             if (v.rid != item.rid) {
32828                 return;
32829             }
32830             
32831             index = i;
32832             return false
32833         });
32834         
32835         return index;
32836     },
32837     
32838     setActiveNext : function()
32839     {
32840         var i = this.indexOfItem(this.getActive());
32841         
32842         if (i > this.barItems.length) {
32843             return;
32844         }
32845         
32846         this.setActiveItem(this.barItems[i+1]);
32847     },
32848     
32849     setActivePrev : function()
32850     {
32851         var i = this.indexOfItem(this.getActive());
32852         
32853         if (i  < 1) {
32854             return;
32855         }
32856         
32857         this.setActiveItem(this.barItems[i-1]);
32858     },
32859     
32860     format : function()
32861     {
32862         if(!this.barItems.length){
32863             return;
32864         }
32865      
32866         var width = 100 / this.barItems.length;
32867         
32868         Roo.each(this.barItems, function(i){
32869             i.el.setStyle('width', width + '%');
32870             i.topEl.el.setStyle('width', width + '%');
32871             i.bottomEl.el.setStyle('width', width + '%');
32872         }, this);
32873         
32874     }
32875     
32876 });
32877 /*
32878  * - LGPL
32879  *
32880  * Nav Progress Item
32881  * 
32882  */
32883
32884 /**
32885  * @class Roo.bootstrap.NavProgressItem
32886  * @extends Roo.bootstrap.Component
32887  * Bootstrap NavProgressItem class
32888  * @cfg {String} rid the reference id
32889  * @cfg {Boolean} active (true|false) Is item active default false
32890  * @cfg {Boolean} disabled (true|false) Is item active default false
32891  * @cfg {String} html
32892  * @cfg {String} position (top|bottom) text position default bottom
32893  * @cfg {String} icon show icon instead of number
32894  * 
32895  * @constructor
32896  * Create a new NavProgressItem
32897  * @param {Object} config The config object
32898  */
32899 Roo.bootstrap.NavProgressItem = function(config){
32900     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32901     this.addEvents({
32902         // raw events
32903         /**
32904          * @event click
32905          * The raw click event for the entire grid.
32906          * @param {Roo.bootstrap.NavProgressItem} this
32907          * @param {Roo.EventObject} e
32908          */
32909         "click" : true
32910     });
32911    
32912 };
32913
32914 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32915     
32916     rid : '',
32917     active : false,
32918     disabled : false,
32919     html : '',
32920     position : 'bottom',
32921     icon : false,
32922     
32923     getAutoCreate : function()
32924     {
32925         var iconCls = 'roo-navigation-bar-item-icon';
32926         
32927         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32928         
32929         var cfg = {
32930             tag: 'li',
32931             cls: 'roo-navigation-bar-item',
32932             cn : [
32933                 {
32934                     tag : 'i',
32935                     cls : iconCls
32936                 }
32937             ]
32938         };
32939         
32940         if(this.active){
32941             cfg.cls += ' active';
32942         }
32943         if(this.disabled){
32944             cfg.cls += ' disabled';
32945         }
32946         
32947         return cfg;
32948     },
32949     
32950     disable : function()
32951     {
32952         this.setDisabled(true);
32953     },
32954     
32955     enable : function()
32956     {
32957         this.setDisabled(false);
32958     },
32959     
32960     initEvents: function() 
32961     {
32962         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32963         
32964         this.iconEl.on('click', this.onClick, this);
32965     },
32966     
32967     onClick : function(e)
32968     {
32969         e.preventDefault();
32970         
32971         if(this.disabled){
32972             return;
32973         }
32974         
32975         if(this.fireEvent('click', this, e) === false){
32976             return;
32977         };
32978         
32979         this.parent().setActiveItem(this);
32980     },
32981     
32982     isActive: function () 
32983     {
32984         return this.active;
32985     },
32986     
32987     setActive : function(state)
32988     {
32989         if(this.active == state){
32990             return;
32991         }
32992         
32993         this.active = state;
32994         
32995         if (state) {
32996             this.el.addClass('active');
32997             return;
32998         }
32999         
33000         this.el.removeClass('active');
33001         
33002         return;
33003     },
33004     
33005     setDisabled : function(state)
33006     {
33007         if(this.disabled == state){
33008             return;
33009         }
33010         
33011         this.disabled = state;
33012         
33013         if (state) {
33014             this.el.addClass('disabled');
33015             return;
33016         }
33017         
33018         this.el.removeClass('disabled');
33019     },
33020     
33021     tooltipEl : function()
33022     {
33023         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33024     }
33025 });
33026  
33027
33028  /*
33029  * - LGPL
33030  *
33031  * FieldLabel
33032  * 
33033  */
33034
33035 /**
33036  * @class Roo.bootstrap.FieldLabel
33037  * @extends Roo.bootstrap.Component
33038  * Bootstrap FieldLabel class
33039  * @cfg {String} html contents of the element
33040  * @cfg {String} tag tag of the element default label
33041  * @cfg {String} cls class of the element
33042  * @cfg {String} target label target 
33043  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33044  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33045  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33046  * @cfg {String} iconTooltip default "This field is required"
33047  * @cfg {String} indicatorpos (left|right) default left
33048  * 
33049  * @constructor
33050  * Create a new FieldLabel
33051  * @param {Object} config The config object
33052  */
33053
33054 Roo.bootstrap.FieldLabel = function(config){
33055     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33056     
33057     this.addEvents({
33058             /**
33059              * @event invalid
33060              * Fires after the field has been marked as invalid.
33061              * @param {Roo.form.FieldLabel} this
33062              * @param {String} msg The validation message
33063              */
33064             invalid : true,
33065             /**
33066              * @event valid
33067              * Fires after the field has been validated with no errors.
33068              * @param {Roo.form.FieldLabel} this
33069              */
33070             valid : true
33071         });
33072 };
33073
33074 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33075     
33076     tag: 'label',
33077     cls: '',
33078     html: '',
33079     target: '',
33080     allowBlank : true,
33081     invalidClass : 'has-warning',
33082     validClass : 'has-success',
33083     iconTooltip : 'This field is required',
33084     indicatorpos : 'left',
33085     
33086     getAutoCreate : function(){
33087         
33088         var cls = "";
33089         if (!this.allowBlank) {
33090             cls  = "visible";
33091         }
33092         
33093         var cfg = {
33094             tag : this.tag,
33095             cls : 'roo-bootstrap-field-label ' + this.cls,
33096             for : this.target,
33097             cn : [
33098                 {
33099                     tag : 'i',
33100                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33101                     tooltip : this.iconTooltip
33102                 },
33103                 {
33104                     tag : 'span',
33105                     html : this.html
33106                 }
33107             ] 
33108         };
33109         
33110         if(this.indicatorpos == 'right'){
33111             var cfg = {
33112                 tag : this.tag,
33113                 cls : 'roo-bootstrap-field-label ' + this.cls,
33114                 for : this.target,
33115                 cn : [
33116                     {
33117                         tag : 'span',
33118                         html : this.html
33119                     },
33120                     {
33121                         tag : 'i',
33122                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33123                         tooltip : this.iconTooltip
33124                     }
33125                 ] 
33126             };
33127         }
33128         
33129         return cfg;
33130     },
33131     
33132     initEvents: function() 
33133     {
33134         Roo.bootstrap.Element.superclass.initEvents.call(this);
33135         
33136         this.indicator = this.indicatorEl();
33137         
33138         if(this.indicator){
33139             this.indicator.removeClass('visible');
33140             this.indicator.addClass('invisible');
33141         }
33142         
33143         Roo.bootstrap.FieldLabel.register(this);
33144     },
33145     
33146     indicatorEl : function()
33147     {
33148         var indicator = this.el.select('i.roo-required-indicator',true).first();
33149         
33150         if(!indicator){
33151             return false;
33152         }
33153         
33154         return indicator;
33155         
33156     },
33157     
33158     /**
33159      * Mark this field as valid
33160      */
33161     markValid : function()
33162     {
33163         if(this.indicator){
33164             this.indicator.removeClass('visible');
33165             this.indicator.addClass('invisible');
33166         }
33167         if (Roo.bootstrap.version == 3) {
33168             this.el.removeClass(this.invalidClass);
33169             this.el.addClass(this.validClass);
33170         } else {
33171             this.el.removeClass('is-invalid');
33172             this.el.addClass('is-valid');
33173         }
33174         
33175         
33176         this.fireEvent('valid', this);
33177     },
33178     
33179     /**
33180      * Mark this field as invalid
33181      * @param {String} msg The validation message
33182      */
33183     markInvalid : function(msg)
33184     {
33185         if(this.indicator){
33186             this.indicator.removeClass('invisible');
33187             this.indicator.addClass('visible');
33188         }
33189           if (Roo.bootstrap.version == 3) {
33190             this.el.removeClass(this.validClass);
33191             this.el.addClass(this.invalidClass);
33192         } else {
33193             this.el.removeClass('is-valid');
33194             this.el.addClass('is-invalid');
33195         }
33196         
33197         
33198         this.fireEvent('invalid', this, msg);
33199     }
33200     
33201    
33202 });
33203
33204 Roo.apply(Roo.bootstrap.FieldLabel, {
33205     
33206     groups: {},
33207     
33208      /**
33209     * register a FieldLabel Group
33210     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33211     */
33212     register : function(label)
33213     {
33214         if(this.groups.hasOwnProperty(label.target)){
33215             return;
33216         }
33217      
33218         this.groups[label.target] = label;
33219         
33220     },
33221     /**
33222     * fetch a FieldLabel Group based on the target
33223     * @param {string} target
33224     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33225     */
33226     get: function(target) {
33227         if (typeof(this.groups[target]) == 'undefined') {
33228             return false;
33229         }
33230         
33231         return this.groups[target] ;
33232     }
33233 });
33234
33235  
33236
33237  /*
33238  * - LGPL
33239  *
33240  * page DateSplitField.
33241  * 
33242  */
33243
33244
33245 /**
33246  * @class Roo.bootstrap.DateSplitField
33247  * @extends Roo.bootstrap.Component
33248  * Bootstrap DateSplitField class
33249  * @cfg {string} fieldLabel - the label associated
33250  * @cfg {Number} labelWidth set the width of label (0-12)
33251  * @cfg {String} labelAlign (top|left)
33252  * @cfg {Boolean} dayAllowBlank (true|false) default false
33253  * @cfg {Boolean} monthAllowBlank (true|false) default false
33254  * @cfg {Boolean} yearAllowBlank (true|false) default false
33255  * @cfg {string} dayPlaceholder 
33256  * @cfg {string} monthPlaceholder
33257  * @cfg {string} yearPlaceholder
33258  * @cfg {string} dayFormat default 'd'
33259  * @cfg {string} monthFormat default 'm'
33260  * @cfg {string} yearFormat default 'Y'
33261  * @cfg {Number} labellg set the width of label (1-12)
33262  * @cfg {Number} labelmd set the width of label (1-12)
33263  * @cfg {Number} labelsm set the width of label (1-12)
33264  * @cfg {Number} labelxs set the width of label (1-12)
33265
33266  *     
33267  * @constructor
33268  * Create a new DateSplitField
33269  * @param {Object} config The config object
33270  */
33271
33272 Roo.bootstrap.DateSplitField = function(config){
33273     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33274     
33275     this.addEvents({
33276         // raw events
33277          /**
33278          * @event years
33279          * getting the data of years
33280          * @param {Roo.bootstrap.DateSplitField} this
33281          * @param {Object} years
33282          */
33283         "years" : true,
33284         /**
33285          * @event days
33286          * getting the data of days
33287          * @param {Roo.bootstrap.DateSplitField} this
33288          * @param {Object} days
33289          */
33290         "days" : true,
33291         /**
33292          * @event invalid
33293          * Fires after the field has been marked as invalid.
33294          * @param {Roo.form.Field} this
33295          * @param {String} msg The validation message
33296          */
33297         invalid : true,
33298        /**
33299          * @event valid
33300          * Fires after the field has been validated with no errors.
33301          * @param {Roo.form.Field} this
33302          */
33303         valid : true
33304     });
33305 };
33306
33307 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33308     
33309     fieldLabel : '',
33310     labelAlign : 'top',
33311     labelWidth : 3,
33312     dayAllowBlank : false,
33313     monthAllowBlank : false,
33314     yearAllowBlank : false,
33315     dayPlaceholder : '',
33316     monthPlaceholder : '',
33317     yearPlaceholder : '',
33318     dayFormat : 'd',
33319     monthFormat : 'm',
33320     yearFormat : 'Y',
33321     isFormField : true,
33322     labellg : 0,
33323     labelmd : 0,
33324     labelsm : 0,
33325     labelxs : 0,
33326     
33327     getAutoCreate : function()
33328     {
33329         var cfg = {
33330             tag : 'div',
33331             cls : 'row roo-date-split-field-group',
33332             cn : [
33333                 {
33334                     tag : 'input',
33335                     type : 'hidden',
33336                     cls : 'form-hidden-field roo-date-split-field-group-value',
33337                     name : this.name
33338                 }
33339             ]
33340         };
33341         
33342         var labelCls = 'col-md-12';
33343         var contentCls = 'col-md-4';
33344         
33345         if(this.fieldLabel){
33346             
33347             var label = {
33348                 tag : 'div',
33349                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33350                 cn : [
33351                     {
33352                         tag : 'label',
33353                         html : this.fieldLabel
33354                     }
33355                 ]
33356             };
33357             
33358             if(this.labelAlign == 'left'){
33359             
33360                 if(this.labelWidth > 12){
33361                     label.style = "width: " + this.labelWidth + 'px';
33362                 }
33363
33364                 if(this.labelWidth < 13 && this.labelmd == 0){
33365                     this.labelmd = this.labelWidth;
33366                 }
33367
33368                 if(this.labellg > 0){
33369                     labelCls = ' col-lg-' + this.labellg;
33370                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33371                 }
33372
33373                 if(this.labelmd > 0){
33374                     labelCls = ' col-md-' + this.labelmd;
33375                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33376                 }
33377
33378                 if(this.labelsm > 0){
33379                     labelCls = ' col-sm-' + this.labelsm;
33380                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33381                 }
33382
33383                 if(this.labelxs > 0){
33384                     labelCls = ' col-xs-' + this.labelxs;
33385                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33386                 }
33387             }
33388             
33389             label.cls += ' ' + labelCls;
33390             
33391             cfg.cn.push(label);
33392         }
33393         
33394         Roo.each(['day', 'month', 'year'], function(t){
33395             cfg.cn.push({
33396                 tag : 'div',
33397                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33398             });
33399         }, this);
33400         
33401         return cfg;
33402     },
33403     
33404     inputEl: function ()
33405     {
33406         return this.el.select('.roo-date-split-field-group-value', true).first();
33407     },
33408     
33409     onRender : function(ct, position) 
33410     {
33411         var _this = this;
33412         
33413         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33414         
33415         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33416         
33417         this.dayField = new Roo.bootstrap.ComboBox({
33418             allowBlank : this.dayAllowBlank,
33419             alwaysQuery : true,
33420             displayField : 'value',
33421             editable : false,
33422             fieldLabel : '',
33423             forceSelection : true,
33424             mode : 'local',
33425             placeholder : this.dayPlaceholder,
33426             selectOnFocus : true,
33427             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33428             triggerAction : 'all',
33429             typeAhead : true,
33430             valueField : 'value',
33431             store : new Roo.data.SimpleStore({
33432                 data : (function() {    
33433                     var days = [];
33434                     _this.fireEvent('days', _this, days);
33435                     return days;
33436                 })(),
33437                 fields : [ 'value' ]
33438             }),
33439             listeners : {
33440                 select : function (_self, record, index)
33441                 {
33442                     _this.setValue(_this.getValue());
33443                 }
33444             }
33445         });
33446
33447         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33448         
33449         this.monthField = new Roo.bootstrap.MonthField({
33450             after : '<i class=\"fa fa-calendar\"></i>',
33451             allowBlank : this.monthAllowBlank,
33452             placeholder : this.monthPlaceholder,
33453             readOnly : true,
33454             listeners : {
33455                 render : function (_self)
33456                 {
33457                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33458                         e.preventDefault();
33459                         _self.focus();
33460                     });
33461                 },
33462                 select : function (_self, oldvalue, newvalue)
33463                 {
33464                     _this.setValue(_this.getValue());
33465                 }
33466             }
33467         });
33468         
33469         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33470         
33471         this.yearField = new Roo.bootstrap.ComboBox({
33472             allowBlank : this.yearAllowBlank,
33473             alwaysQuery : true,
33474             displayField : 'value',
33475             editable : false,
33476             fieldLabel : '',
33477             forceSelection : true,
33478             mode : 'local',
33479             placeholder : this.yearPlaceholder,
33480             selectOnFocus : true,
33481             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33482             triggerAction : 'all',
33483             typeAhead : true,
33484             valueField : 'value',
33485             store : new Roo.data.SimpleStore({
33486                 data : (function() {
33487                     var years = [];
33488                     _this.fireEvent('years', _this, years);
33489                     return years;
33490                 })(),
33491                 fields : [ 'value' ]
33492             }),
33493             listeners : {
33494                 select : function (_self, record, index)
33495                 {
33496                     _this.setValue(_this.getValue());
33497                 }
33498             }
33499         });
33500
33501         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33502     },
33503     
33504     setValue : function(v, format)
33505     {
33506         this.inputEl.dom.value = v;
33507         
33508         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33509         
33510         var d = Date.parseDate(v, f);
33511         
33512         if(!d){
33513             this.validate();
33514             return;
33515         }
33516         
33517         this.setDay(d.format(this.dayFormat));
33518         this.setMonth(d.format(this.monthFormat));
33519         this.setYear(d.format(this.yearFormat));
33520         
33521         this.validate();
33522         
33523         return;
33524     },
33525     
33526     setDay : function(v)
33527     {
33528         this.dayField.setValue(v);
33529         this.inputEl.dom.value = this.getValue();
33530         this.validate();
33531         return;
33532     },
33533     
33534     setMonth : function(v)
33535     {
33536         this.monthField.setValue(v, true);
33537         this.inputEl.dom.value = this.getValue();
33538         this.validate();
33539         return;
33540     },
33541     
33542     setYear : function(v)
33543     {
33544         this.yearField.setValue(v);
33545         this.inputEl.dom.value = this.getValue();
33546         this.validate();
33547         return;
33548     },
33549     
33550     getDay : function()
33551     {
33552         return this.dayField.getValue();
33553     },
33554     
33555     getMonth : function()
33556     {
33557         return this.monthField.getValue();
33558     },
33559     
33560     getYear : function()
33561     {
33562         return this.yearField.getValue();
33563     },
33564     
33565     getValue : function()
33566     {
33567         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33568         
33569         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33570         
33571         return date;
33572     },
33573     
33574     reset : function()
33575     {
33576         this.setDay('');
33577         this.setMonth('');
33578         this.setYear('');
33579         this.inputEl.dom.value = '';
33580         this.validate();
33581         return;
33582     },
33583     
33584     validate : function()
33585     {
33586         var d = this.dayField.validate();
33587         var m = this.monthField.validate();
33588         var y = this.yearField.validate();
33589         
33590         var valid = true;
33591         
33592         if(
33593                 (!this.dayAllowBlank && !d) ||
33594                 (!this.monthAllowBlank && !m) ||
33595                 (!this.yearAllowBlank && !y)
33596         ){
33597             valid = false;
33598         }
33599         
33600         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33601             return valid;
33602         }
33603         
33604         if(valid){
33605             this.markValid();
33606             return valid;
33607         }
33608         
33609         this.markInvalid();
33610         
33611         return valid;
33612     },
33613     
33614     markValid : function()
33615     {
33616         
33617         var label = this.el.select('label', true).first();
33618         var icon = this.el.select('i.fa-star', true).first();
33619
33620         if(label && icon){
33621             icon.remove();
33622         }
33623         
33624         this.fireEvent('valid', this);
33625     },
33626     
33627      /**
33628      * Mark this field as invalid
33629      * @param {String} msg The validation message
33630      */
33631     markInvalid : function(msg)
33632     {
33633         
33634         var label = this.el.select('label', true).first();
33635         var icon = this.el.select('i.fa-star', true).first();
33636
33637         if(label && !icon){
33638             this.el.select('.roo-date-split-field-label', true).createChild({
33639                 tag : 'i',
33640                 cls : 'text-danger fa fa-lg fa-star',
33641                 tooltip : 'This field is required',
33642                 style : 'margin-right:5px;'
33643             }, label, true);
33644         }
33645         
33646         this.fireEvent('invalid', this, msg);
33647     },
33648     
33649     clearInvalid : function()
33650     {
33651         var label = this.el.select('label', true).first();
33652         var icon = this.el.select('i.fa-star', true).first();
33653
33654         if(label && icon){
33655             icon.remove();
33656         }
33657         
33658         this.fireEvent('valid', this);
33659     },
33660     
33661     getName: function()
33662     {
33663         return this.name;
33664     }
33665     
33666 });
33667
33668  /**
33669  *
33670  * This is based on 
33671  * http://masonry.desandro.com
33672  *
33673  * The idea is to render all the bricks based on vertical width...
33674  *
33675  * The original code extends 'outlayer' - we might need to use that....
33676  * 
33677  */
33678
33679
33680 /**
33681  * @class Roo.bootstrap.LayoutMasonry
33682  * @extends Roo.bootstrap.Component
33683  * Bootstrap Layout Masonry class
33684  * 
33685  * @constructor
33686  * Create a new Element
33687  * @param {Object} config The config object
33688  */
33689
33690 Roo.bootstrap.LayoutMasonry = function(config){
33691     
33692     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33693     
33694     this.bricks = [];
33695     
33696     Roo.bootstrap.LayoutMasonry.register(this);
33697     
33698     this.addEvents({
33699         // raw events
33700         /**
33701          * @event layout
33702          * Fire after layout the items
33703          * @param {Roo.bootstrap.LayoutMasonry} this
33704          * @param {Roo.EventObject} e
33705          */
33706         "layout" : true
33707     });
33708     
33709 };
33710
33711 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33712     
33713     /**
33714      * @cfg {Boolean} isLayoutInstant = no animation?
33715      */   
33716     isLayoutInstant : false, // needed?
33717    
33718     /**
33719      * @cfg {Number} boxWidth  width of the columns
33720      */   
33721     boxWidth : 450,
33722     
33723       /**
33724      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33725      */   
33726     boxHeight : 0,
33727     
33728     /**
33729      * @cfg {Number} padWidth padding below box..
33730      */   
33731     padWidth : 10, 
33732     
33733     /**
33734      * @cfg {Number} gutter gutter width..
33735      */   
33736     gutter : 10,
33737     
33738      /**
33739      * @cfg {Number} maxCols maximum number of columns
33740      */   
33741     
33742     maxCols: 0,
33743     
33744     /**
33745      * @cfg {Boolean} isAutoInitial defalut true
33746      */   
33747     isAutoInitial : true, 
33748     
33749     containerWidth: 0,
33750     
33751     /**
33752      * @cfg {Boolean} isHorizontal defalut false
33753      */   
33754     isHorizontal : false, 
33755
33756     currentSize : null,
33757     
33758     tag: 'div',
33759     
33760     cls: '',
33761     
33762     bricks: null, //CompositeElement
33763     
33764     cols : 1,
33765     
33766     _isLayoutInited : false,
33767     
33768 //    isAlternative : false, // only use for vertical layout...
33769     
33770     /**
33771      * @cfg {Number} alternativePadWidth padding below box..
33772      */   
33773     alternativePadWidth : 50,
33774     
33775     selectedBrick : [],
33776     
33777     getAutoCreate : function(){
33778         
33779         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33780         
33781         var cfg = {
33782             tag: this.tag,
33783             cls: 'blog-masonary-wrapper ' + this.cls,
33784             cn : {
33785                 cls : 'mas-boxes masonary'
33786             }
33787         };
33788         
33789         return cfg;
33790     },
33791     
33792     getChildContainer: function( )
33793     {
33794         if (this.boxesEl) {
33795             return this.boxesEl;
33796         }
33797         
33798         this.boxesEl = this.el.select('.mas-boxes').first();
33799         
33800         return this.boxesEl;
33801     },
33802     
33803     
33804     initEvents : function()
33805     {
33806         var _this = this;
33807         
33808         if(this.isAutoInitial){
33809             Roo.log('hook children rendered');
33810             this.on('childrenrendered', function() {
33811                 Roo.log('children rendered');
33812                 _this.initial();
33813             } ,this);
33814         }
33815     },
33816     
33817     initial : function()
33818     {
33819         this.selectedBrick = [];
33820         
33821         this.currentSize = this.el.getBox(true);
33822         
33823         Roo.EventManager.onWindowResize(this.resize, this); 
33824
33825         if(!this.isAutoInitial){
33826             this.layout();
33827             return;
33828         }
33829         
33830         this.layout();
33831         
33832         return;
33833         //this.layout.defer(500,this);
33834         
33835     },
33836     
33837     resize : function()
33838     {
33839         var cs = this.el.getBox(true);
33840         
33841         if (
33842                 this.currentSize.width == cs.width && 
33843                 this.currentSize.x == cs.x && 
33844                 this.currentSize.height == cs.height && 
33845                 this.currentSize.y == cs.y 
33846         ) {
33847             Roo.log("no change in with or X or Y");
33848             return;
33849         }
33850         
33851         this.currentSize = cs;
33852         
33853         this.layout();
33854         
33855     },
33856     
33857     layout : function()
33858     {   
33859         this._resetLayout();
33860         
33861         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33862         
33863         this.layoutItems( isInstant );
33864       
33865         this._isLayoutInited = true;
33866         
33867         this.fireEvent('layout', this);
33868         
33869     },
33870     
33871     _resetLayout : function()
33872     {
33873         if(this.isHorizontal){
33874             this.horizontalMeasureColumns();
33875             return;
33876         }
33877         
33878         this.verticalMeasureColumns();
33879         
33880     },
33881     
33882     verticalMeasureColumns : function()
33883     {
33884         this.getContainerWidth();
33885         
33886 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33887 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33888 //            return;
33889 //        }
33890         
33891         var boxWidth = this.boxWidth + this.padWidth;
33892         
33893         if(this.containerWidth < this.boxWidth){
33894             boxWidth = this.containerWidth
33895         }
33896         
33897         var containerWidth = this.containerWidth;
33898         
33899         var cols = Math.floor(containerWidth / boxWidth);
33900         
33901         this.cols = Math.max( cols, 1 );
33902         
33903         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33904         
33905         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33906         
33907         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33908         
33909         this.colWidth = boxWidth + avail - this.padWidth;
33910         
33911         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33912         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33913     },
33914     
33915     horizontalMeasureColumns : function()
33916     {
33917         this.getContainerWidth();
33918         
33919         var boxWidth = this.boxWidth;
33920         
33921         if(this.containerWidth < boxWidth){
33922             boxWidth = this.containerWidth;
33923         }
33924         
33925         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33926         
33927         this.el.setHeight(boxWidth);
33928         
33929     },
33930     
33931     getContainerWidth : function()
33932     {
33933         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33934     },
33935     
33936     layoutItems : function( isInstant )
33937     {
33938         Roo.log(this.bricks);
33939         
33940         var items = Roo.apply([], this.bricks);
33941         
33942         if(this.isHorizontal){
33943             this._horizontalLayoutItems( items , isInstant );
33944             return;
33945         }
33946         
33947 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33948 //            this._verticalAlternativeLayoutItems( items , isInstant );
33949 //            return;
33950 //        }
33951         
33952         this._verticalLayoutItems( items , isInstant );
33953         
33954     },
33955     
33956     _verticalLayoutItems : function ( items , isInstant)
33957     {
33958         if ( !items || !items.length ) {
33959             return;
33960         }
33961         
33962         var standard = [
33963             ['xs', 'xs', 'xs', 'tall'],
33964             ['xs', 'xs', 'tall'],
33965             ['xs', 'xs', 'sm'],
33966             ['xs', 'xs', 'xs'],
33967             ['xs', 'tall'],
33968             ['xs', 'sm'],
33969             ['xs', 'xs'],
33970             ['xs'],
33971             
33972             ['sm', 'xs', 'xs'],
33973             ['sm', 'xs'],
33974             ['sm'],
33975             
33976             ['tall', 'xs', 'xs', 'xs'],
33977             ['tall', 'xs', 'xs'],
33978             ['tall', 'xs'],
33979             ['tall']
33980             
33981         ];
33982         
33983         var queue = [];
33984         
33985         var boxes = [];
33986         
33987         var box = [];
33988         
33989         Roo.each(items, function(item, k){
33990             
33991             switch (item.size) {
33992                 // these layouts take up a full box,
33993                 case 'md' :
33994                 case 'md-left' :
33995                 case 'md-right' :
33996                 case 'wide' :
33997                     
33998                     if(box.length){
33999                         boxes.push(box);
34000                         box = [];
34001                     }
34002                     
34003                     boxes.push([item]);
34004                     
34005                     break;
34006                     
34007                 case 'xs' :
34008                 case 'sm' :
34009                 case 'tall' :
34010                     
34011                     box.push(item);
34012                     
34013                     break;
34014                 default :
34015                     break;
34016                     
34017             }
34018             
34019         }, this);
34020         
34021         if(box.length){
34022             boxes.push(box);
34023             box = [];
34024         }
34025         
34026         var filterPattern = function(box, length)
34027         {
34028             if(!box.length){
34029                 return;
34030             }
34031             
34032             var match = false;
34033             
34034             var pattern = box.slice(0, length);
34035             
34036             var format = [];
34037             
34038             Roo.each(pattern, function(i){
34039                 format.push(i.size);
34040             }, this);
34041             
34042             Roo.each(standard, function(s){
34043                 
34044                 if(String(s) != String(format)){
34045                     return;
34046                 }
34047                 
34048                 match = true;
34049                 return false;
34050                 
34051             }, this);
34052             
34053             if(!match && length == 1){
34054                 return;
34055             }
34056             
34057             if(!match){
34058                 filterPattern(box, length - 1);
34059                 return;
34060             }
34061                 
34062             queue.push(pattern);
34063
34064             box = box.slice(length, box.length);
34065
34066             filterPattern(box, 4);
34067
34068             return;
34069             
34070         }
34071         
34072         Roo.each(boxes, function(box, k){
34073             
34074             if(!box.length){
34075                 return;
34076             }
34077             
34078             if(box.length == 1){
34079                 queue.push(box);
34080                 return;
34081             }
34082             
34083             filterPattern(box, 4);
34084             
34085         }, this);
34086         
34087         this._processVerticalLayoutQueue( queue, isInstant );
34088         
34089     },
34090     
34091 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34092 //    {
34093 //        if ( !items || !items.length ) {
34094 //            return;
34095 //        }
34096 //
34097 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34098 //        
34099 //    },
34100     
34101     _horizontalLayoutItems : function ( items , isInstant)
34102     {
34103         if ( !items || !items.length || items.length < 3) {
34104             return;
34105         }
34106         
34107         items.reverse();
34108         
34109         var eItems = items.slice(0, 3);
34110         
34111         items = items.slice(3, items.length);
34112         
34113         var standard = [
34114             ['xs', 'xs', 'xs', 'wide'],
34115             ['xs', 'xs', 'wide'],
34116             ['xs', 'xs', 'sm'],
34117             ['xs', 'xs', 'xs'],
34118             ['xs', 'wide'],
34119             ['xs', 'sm'],
34120             ['xs', 'xs'],
34121             ['xs'],
34122             
34123             ['sm', 'xs', 'xs'],
34124             ['sm', 'xs'],
34125             ['sm'],
34126             
34127             ['wide', 'xs', 'xs', 'xs'],
34128             ['wide', 'xs', 'xs'],
34129             ['wide', 'xs'],
34130             ['wide'],
34131             
34132             ['wide-thin']
34133         ];
34134         
34135         var queue = [];
34136         
34137         var boxes = [];
34138         
34139         var box = [];
34140         
34141         Roo.each(items, function(item, k){
34142             
34143             switch (item.size) {
34144                 case 'md' :
34145                 case 'md-left' :
34146                 case 'md-right' :
34147                 case 'tall' :
34148                     
34149                     if(box.length){
34150                         boxes.push(box);
34151                         box = [];
34152                     }
34153                     
34154                     boxes.push([item]);
34155                     
34156                     break;
34157                     
34158                 case 'xs' :
34159                 case 'sm' :
34160                 case 'wide' :
34161                 case 'wide-thin' :
34162                     
34163                     box.push(item);
34164                     
34165                     break;
34166                 default :
34167                     break;
34168                     
34169             }
34170             
34171         }, this);
34172         
34173         if(box.length){
34174             boxes.push(box);
34175             box = [];
34176         }
34177         
34178         var filterPattern = function(box, length)
34179         {
34180             if(!box.length){
34181                 return;
34182             }
34183             
34184             var match = false;
34185             
34186             var pattern = box.slice(0, length);
34187             
34188             var format = [];
34189             
34190             Roo.each(pattern, function(i){
34191                 format.push(i.size);
34192             }, this);
34193             
34194             Roo.each(standard, function(s){
34195                 
34196                 if(String(s) != String(format)){
34197                     return;
34198                 }
34199                 
34200                 match = true;
34201                 return false;
34202                 
34203             }, this);
34204             
34205             if(!match && length == 1){
34206                 return;
34207             }
34208             
34209             if(!match){
34210                 filterPattern(box, length - 1);
34211                 return;
34212             }
34213                 
34214             queue.push(pattern);
34215
34216             box = box.slice(length, box.length);
34217
34218             filterPattern(box, 4);
34219
34220             return;
34221             
34222         }
34223         
34224         Roo.each(boxes, function(box, k){
34225             
34226             if(!box.length){
34227                 return;
34228             }
34229             
34230             if(box.length == 1){
34231                 queue.push(box);
34232                 return;
34233             }
34234             
34235             filterPattern(box, 4);
34236             
34237         }, this);
34238         
34239         
34240         var prune = [];
34241         
34242         var pos = this.el.getBox(true);
34243         
34244         var minX = pos.x;
34245         
34246         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34247         
34248         var hit_end = false;
34249         
34250         Roo.each(queue, function(box){
34251             
34252             if(hit_end){
34253                 
34254                 Roo.each(box, function(b){
34255                 
34256                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34257                     b.el.hide();
34258
34259                 }, this);
34260
34261                 return;
34262             }
34263             
34264             var mx = 0;
34265             
34266             Roo.each(box, function(b){
34267                 
34268                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34269                 b.el.show();
34270
34271                 mx = Math.max(mx, b.x);
34272                 
34273             }, this);
34274             
34275             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34276             
34277             if(maxX < minX){
34278                 
34279                 Roo.each(box, function(b){
34280                 
34281                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34282                     b.el.hide();
34283                     
34284                 }, this);
34285                 
34286                 hit_end = true;
34287                 
34288                 return;
34289             }
34290             
34291             prune.push(box);
34292             
34293         }, this);
34294         
34295         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34296     },
34297     
34298     /** Sets position of item in DOM
34299     * @param {Element} item
34300     * @param {Number} x - horizontal position
34301     * @param {Number} y - vertical position
34302     * @param {Boolean} isInstant - disables transitions
34303     */
34304     _processVerticalLayoutQueue : function( queue, isInstant )
34305     {
34306         var pos = this.el.getBox(true);
34307         var x = pos.x;
34308         var y = pos.y;
34309         var maxY = [];
34310         
34311         for (var i = 0; i < this.cols; i++){
34312             maxY[i] = pos.y;
34313         }
34314         
34315         Roo.each(queue, function(box, k){
34316             
34317             var col = k % this.cols;
34318             
34319             Roo.each(box, function(b,kk){
34320                 
34321                 b.el.position('absolute');
34322                 
34323                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34324                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34325                 
34326                 if(b.size == 'md-left' || b.size == 'md-right'){
34327                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34328                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34329                 }
34330                 
34331                 b.el.setWidth(width);
34332                 b.el.setHeight(height);
34333                 // iframe?
34334                 b.el.select('iframe',true).setSize(width,height);
34335                 
34336             }, this);
34337             
34338             for (var i = 0; i < this.cols; i++){
34339                 
34340                 if(maxY[i] < maxY[col]){
34341                     col = i;
34342                     continue;
34343                 }
34344                 
34345                 col = Math.min(col, i);
34346                 
34347             }
34348             
34349             x = pos.x + col * (this.colWidth + this.padWidth);
34350             
34351             y = maxY[col];
34352             
34353             var positions = [];
34354             
34355             switch (box.length){
34356                 case 1 :
34357                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34358                     break;
34359                 case 2 :
34360                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34361                     break;
34362                 case 3 :
34363                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34364                     break;
34365                 case 4 :
34366                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34367                     break;
34368                 default :
34369                     break;
34370             }
34371             
34372             Roo.each(box, function(b,kk){
34373                 
34374                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34375                 
34376                 var sz = b.el.getSize();
34377                 
34378                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34379                 
34380             }, this);
34381             
34382         }, this);
34383         
34384         var mY = 0;
34385         
34386         for (var i = 0; i < this.cols; i++){
34387             mY = Math.max(mY, maxY[i]);
34388         }
34389         
34390         this.el.setHeight(mY - pos.y);
34391         
34392     },
34393     
34394 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34395 //    {
34396 //        var pos = this.el.getBox(true);
34397 //        var x = pos.x;
34398 //        var y = pos.y;
34399 //        var maxX = pos.right;
34400 //        
34401 //        var maxHeight = 0;
34402 //        
34403 //        Roo.each(items, function(item, k){
34404 //            
34405 //            var c = k % 2;
34406 //            
34407 //            item.el.position('absolute');
34408 //                
34409 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34410 //
34411 //            item.el.setWidth(width);
34412 //
34413 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34414 //
34415 //            item.el.setHeight(height);
34416 //            
34417 //            if(c == 0){
34418 //                item.el.setXY([x, y], isInstant ? false : true);
34419 //            } else {
34420 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34421 //            }
34422 //            
34423 //            y = y + height + this.alternativePadWidth;
34424 //            
34425 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34426 //            
34427 //        }, this);
34428 //        
34429 //        this.el.setHeight(maxHeight);
34430 //        
34431 //    },
34432     
34433     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34434     {
34435         var pos = this.el.getBox(true);
34436         
34437         var minX = pos.x;
34438         var minY = pos.y;
34439         
34440         var maxX = pos.right;
34441         
34442         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34443         
34444         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34445         
34446         Roo.each(queue, function(box, k){
34447             
34448             Roo.each(box, function(b, kk){
34449                 
34450                 b.el.position('absolute');
34451                 
34452                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34453                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34454                 
34455                 if(b.size == 'md-left' || b.size == 'md-right'){
34456                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34457                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34458                 }
34459                 
34460                 b.el.setWidth(width);
34461                 b.el.setHeight(height);
34462                 
34463             }, this);
34464             
34465             if(!box.length){
34466                 return;
34467             }
34468             
34469             var positions = [];
34470             
34471             switch (box.length){
34472                 case 1 :
34473                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34474                     break;
34475                 case 2 :
34476                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34477                     break;
34478                 case 3 :
34479                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34480                     break;
34481                 case 4 :
34482                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34483                     break;
34484                 default :
34485                     break;
34486             }
34487             
34488             Roo.each(box, function(b,kk){
34489                 
34490                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34491                 
34492                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34493                 
34494             }, this);
34495             
34496         }, this);
34497         
34498     },
34499     
34500     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34501     {
34502         Roo.each(eItems, function(b,k){
34503             
34504             b.size = (k == 0) ? 'sm' : 'xs';
34505             b.x = (k == 0) ? 2 : 1;
34506             b.y = (k == 0) ? 2 : 1;
34507             
34508             b.el.position('absolute');
34509             
34510             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34511                 
34512             b.el.setWidth(width);
34513             
34514             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34515             
34516             b.el.setHeight(height);
34517             
34518         }, this);
34519
34520         var positions = [];
34521         
34522         positions.push({
34523             x : maxX - this.unitWidth * 2 - this.gutter,
34524             y : minY
34525         });
34526         
34527         positions.push({
34528             x : maxX - this.unitWidth,
34529             y : minY + (this.unitWidth + this.gutter) * 2
34530         });
34531         
34532         positions.push({
34533             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34534             y : minY
34535         });
34536         
34537         Roo.each(eItems, function(b,k){
34538             
34539             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34540
34541         }, this);
34542         
34543     },
34544     
34545     getVerticalOneBoxColPositions : function(x, y, box)
34546     {
34547         var pos = [];
34548         
34549         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34550         
34551         if(box[0].size == 'md-left'){
34552             rand = 0;
34553         }
34554         
34555         if(box[0].size == 'md-right'){
34556             rand = 1;
34557         }
34558         
34559         pos.push({
34560             x : x + (this.unitWidth + this.gutter) * rand,
34561             y : y
34562         });
34563         
34564         return pos;
34565     },
34566     
34567     getVerticalTwoBoxColPositions : function(x, y, box)
34568     {
34569         var pos = [];
34570         
34571         if(box[0].size == 'xs'){
34572             
34573             pos.push({
34574                 x : x,
34575                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34576             });
34577
34578             pos.push({
34579                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34580                 y : y
34581             });
34582             
34583             return pos;
34584             
34585         }
34586         
34587         pos.push({
34588             x : x,
34589             y : y
34590         });
34591
34592         pos.push({
34593             x : x + (this.unitWidth + this.gutter) * 2,
34594             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34595         });
34596         
34597         return pos;
34598         
34599     },
34600     
34601     getVerticalThreeBoxColPositions : function(x, y, box)
34602     {
34603         var pos = [];
34604         
34605         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34606             
34607             pos.push({
34608                 x : x,
34609                 y : y
34610             });
34611
34612             pos.push({
34613                 x : x + (this.unitWidth + this.gutter) * 1,
34614                 y : y
34615             });
34616             
34617             pos.push({
34618                 x : x + (this.unitWidth + this.gutter) * 2,
34619                 y : y
34620             });
34621             
34622             return pos;
34623             
34624         }
34625         
34626         if(box[0].size == 'xs' && box[1].size == 'xs'){
34627             
34628             pos.push({
34629                 x : x,
34630                 y : y
34631             });
34632
34633             pos.push({
34634                 x : x,
34635                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34636             });
34637             
34638             pos.push({
34639                 x : x + (this.unitWidth + this.gutter) * 1,
34640                 y : y
34641             });
34642             
34643             return pos;
34644             
34645         }
34646         
34647         pos.push({
34648             x : x,
34649             y : y
34650         });
34651
34652         pos.push({
34653             x : x + (this.unitWidth + this.gutter) * 2,
34654             y : y
34655         });
34656
34657         pos.push({
34658             x : x + (this.unitWidth + this.gutter) * 2,
34659             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34660         });
34661             
34662         return pos;
34663         
34664     },
34665     
34666     getVerticalFourBoxColPositions : function(x, y, box)
34667     {
34668         var pos = [];
34669         
34670         if(box[0].size == 'xs'){
34671             
34672             pos.push({
34673                 x : x,
34674                 y : y
34675             });
34676
34677             pos.push({
34678                 x : x,
34679                 y : y + (this.unitHeight + this.gutter) * 1
34680             });
34681             
34682             pos.push({
34683                 x : x,
34684                 y : y + (this.unitHeight + this.gutter) * 2
34685             });
34686             
34687             pos.push({
34688                 x : x + (this.unitWidth + this.gutter) * 1,
34689                 y : y
34690             });
34691             
34692             return pos;
34693             
34694         }
34695         
34696         pos.push({
34697             x : x,
34698             y : y
34699         });
34700
34701         pos.push({
34702             x : x + (this.unitWidth + this.gutter) * 2,
34703             y : y
34704         });
34705
34706         pos.push({
34707             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34708             y : y + (this.unitHeight + this.gutter) * 1
34709         });
34710
34711         pos.push({
34712             x : x + (this.unitWidth + this.gutter) * 2,
34713             y : y + (this.unitWidth + this.gutter) * 2
34714         });
34715
34716         return pos;
34717         
34718     },
34719     
34720     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34721     {
34722         var pos = [];
34723         
34724         if(box[0].size == 'md-left'){
34725             pos.push({
34726                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34727                 y : minY
34728             });
34729             
34730             return pos;
34731         }
34732         
34733         if(box[0].size == 'md-right'){
34734             pos.push({
34735                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34736                 y : minY + (this.unitWidth + this.gutter) * 1
34737             });
34738             
34739             return pos;
34740         }
34741         
34742         var rand = Math.floor(Math.random() * (4 - box[0].y));
34743         
34744         pos.push({
34745             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34746             y : minY + (this.unitWidth + this.gutter) * rand
34747         });
34748         
34749         return pos;
34750         
34751     },
34752     
34753     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34754     {
34755         var pos = [];
34756         
34757         if(box[0].size == 'xs'){
34758             
34759             pos.push({
34760                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34761                 y : minY
34762             });
34763
34764             pos.push({
34765                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34766                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34767             });
34768             
34769             return pos;
34770             
34771         }
34772         
34773         pos.push({
34774             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34775             y : minY
34776         });
34777
34778         pos.push({
34779             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34780             y : minY + (this.unitWidth + this.gutter) * 2
34781         });
34782         
34783         return pos;
34784         
34785     },
34786     
34787     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34788     {
34789         var pos = [];
34790         
34791         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34792             
34793             pos.push({
34794                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34795                 y : minY
34796             });
34797
34798             pos.push({
34799                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34800                 y : minY + (this.unitWidth + this.gutter) * 1
34801             });
34802             
34803             pos.push({
34804                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34805                 y : minY + (this.unitWidth + this.gutter) * 2
34806             });
34807             
34808             return pos;
34809             
34810         }
34811         
34812         if(box[0].size == 'xs' && box[1].size == 'xs'){
34813             
34814             pos.push({
34815                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34816                 y : minY
34817             });
34818
34819             pos.push({
34820                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34821                 y : minY
34822             });
34823             
34824             pos.push({
34825                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34826                 y : minY + (this.unitWidth + this.gutter) * 1
34827             });
34828             
34829             return pos;
34830             
34831         }
34832         
34833         pos.push({
34834             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34835             y : minY
34836         });
34837
34838         pos.push({
34839             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34840             y : minY + (this.unitWidth + this.gutter) * 2
34841         });
34842
34843         pos.push({
34844             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34845             y : minY + (this.unitWidth + this.gutter) * 2
34846         });
34847             
34848         return pos;
34849         
34850     },
34851     
34852     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34853     {
34854         var pos = [];
34855         
34856         if(box[0].size == 'xs'){
34857             
34858             pos.push({
34859                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34860                 y : minY
34861             });
34862
34863             pos.push({
34864                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34865                 y : minY
34866             });
34867             
34868             pos.push({
34869                 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),
34870                 y : minY
34871             });
34872             
34873             pos.push({
34874                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34875                 y : minY + (this.unitWidth + this.gutter) * 1
34876             });
34877             
34878             return pos;
34879             
34880         }
34881         
34882         pos.push({
34883             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34884             y : minY
34885         });
34886         
34887         pos.push({
34888             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34889             y : minY + (this.unitWidth + this.gutter) * 2
34890         });
34891         
34892         pos.push({
34893             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34894             y : minY + (this.unitWidth + this.gutter) * 2
34895         });
34896         
34897         pos.push({
34898             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),
34899             y : minY + (this.unitWidth + this.gutter) * 2
34900         });
34901
34902         return pos;
34903         
34904     },
34905     
34906     /**
34907     * remove a Masonry Brick
34908     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34909     */
34910     removeBrick : function(brick_id)
34911     {
34912         if (!brick_id) {
34913             return;
34914         }
34915         
34916         for (var i = 0; i<this.bricks.length; i++) {
34917             if (this.bricks[i].id == brick_id) {
34918                 this.bricks.splice(i,1);
34919                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34920                 this.initial();
34921             }
34922         }
34923     },
34924     
34925     /**
34926     * adds a Masonry Brick
34927     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34928     */
34929     addBrick : function(cfg)
34930     {
34931         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34932         //this.register(cn);
34933         cn.parentId = this.id;
34934         cn.render(this.el);
34935         return cn;
34936     },
34937     
34938     /**
34939     * register a Masonry Brick
34940     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34941     */
34942     
34943     register : function(brick)
34944     {
34945         this.bricks.push(brick);
34946         brick.masonryId = this.id;
34947     },
34948     
34949     /**
34950     * clear all the Masonry Brick
34951     */
34952     clearAll : function()
34953     {
34954         this.bricks = [];
34955         //this.getChildContainer().dom.innerHTML = "";
34956         this.el.dom.innerHTML = '';
34957     },
34958     
34959     getSelected : function()
34960     {
34961         if (!this.selectedBrick) {
34962             return false;
34963         }
34964         
34965         return this.selectedBrick;
34966     }
34967 });
34968
34969 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34970     
34971     groups: {},
34972      /**
34973     * register a Masonry Layout
34974     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34975     */
34976     
34977     register : function(layout)
34978     {
34979         this.groups[layout.id] = layout;
34980     },
34981     /**
34982     * fetch a  Masonry Layout based on the masonry layout ID
34983     * @param {string} the masonry layout to add
34984     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34985     */
34986     
34987     get: function(layout_id) {
34988         if (typeof(this.groups[layout_id]) == 'undefined') {
34989             return false;
34990         }
34991         return this.groups[layout_id] ;
34992     }
34993     
34994     
34995     
34996 });
34997
34998  
34999
35000  /**
35001  *
35002  * This is based on 
35003  * http://masonry.desandro.com
35004  *
35005  * The idea is to render all the bricks based on vertical width...
35006  *
35007  * The original code extends 'outlayer' - we might need to use that....
35008  * 
35009  */
35010
35011
35012 /**
35013  * @class Roo.bootstrap.LayoutMasonryAuto
35014  * @extends Roo.bootstrap.Component
35015  * Bootstrap Layout Masonry class
35016  * 
35017  * @constructor
35018  * Create a new Element
35019  * @param {Object} config The config object
35020  */
35021
35022 Roo.bootstrap.LayoutMasonryAuto = function(config){
35023     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35024 };
35025
35026 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35027     
35028       /**
35029      * @cfg {Boolean} isFitWidth  - resize the width..
35030      */   
35031     isFitWidth : false,  // options..
35032     /**
35033      * @cfg {Boolean} isOriginLeft = left align?
35034      */   
35035     isOriginLeft : true,
35036     /**
35037      * @cfg {Boolean} isOriginTop = top align?
35038      */   
35039     isOriginTop : false,
35040     /**
35041      * @cfg {Boolean} isLayoutInstant = no animation?
35042      */   
35043     isLayoutInstant : false, // needed?
35044     /**
35045      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35046      */   
35047     isResizingContainer : true,
35048     /**
35049      * @cfg {Number} columnWidth  width of the columns 
35050      */   
35051     
35052     columnWidth : 0,
35053     
35054     /**
35055      * @cfg {Number} maxCols maximum number of columns
35056      */   
35057     
35058     maxCols: 0,
35059     /**
35060      * @cfg {Number} padHeight padding below box..
35061      */   
35062     
35063     padHeight : 10, 
35064     
35065     /**
35066      * @cfg {Boolean} isAutoInitial defalut true
35067      */   
35068     
35069     isAutoInitial : true, 
35070     
35071     // private?
35072     gutter : 0,
35073     
35074     containerWidth: 0,
35075     initialColumnWidth : 0,
35076     currentSize : null,
35077     
35078     colYs : null, // array.
35079     maxY : 0,
35080     padWidth: 10,
35081     
35082     
35083     tag: 'div',
35084     cls: '',
35085     bricks: null, //CompositeElement
35086     cols : 0, // array?
35087     // element : null, // wrapped now this.el
35088     _isLayoutInited : null, 
35089     
35090     
35091     getAutoCreate : function(){
35092         
35093         var cfg = {
35094             tag: this.tag,
35095             cls: 'blog-masonary-wrapper ' + this.cls,
35096             cn : {
35097                 cls : 'mas-boxes masonary'
35098             }
35099         };
35100         
35101         return cfg;
35102     },
35103     
35104     getChildContainer: function( )
35105     {
35106         if (this.boxesEl) {
35107             return this.boxesEl;
35108         }
35109         
35110         this.boxesEl = this.el.select('.mas-boxes').first();
35111         
35112         return this.boxesEl;
35113     },
35114     
35115     
35116     initEvents : function()
35117     {
35118         var _this = this;
35119         
35120         if(this.isAutoInitial){
35121             Roo.log('hook children rendered');
35122             this.on('childrenrendered', function() {
35123                 Roo.log('children rendered');
35124                 _this.initial();
35125             } ,this);
35126         }
35127         
35128     },
35129     
35130     initial : function()
35131     {
35132         this.reloadItems();
35133
35134         this.currentSize = this.el.getBox(true);
35135
35136         /// was window resize... - let's see if this works..
35137         Roo.EventManager.onWindowResize(this.resize, this); 
35138
35139         if(!this.isAutoInitial){
35140             this.layout();
35141             return;
35142         }
35143         
35144         this.layout.defer(500,this);
35145     },
35146     
35147     reloadItems: function()
35148     {
35149         this.bricks = this.el.select('.masonry-brick', true);
35150         
35151         this.bricks.each(function(b) {
35152             //Roo.log(b.getSize());
35153             if (!b.attr('originalwidth')) {
35154                 b.attr('originalwidth',  b.getSize().width);
35155             }
35156             
35157         });
35158         
35159         Roo.log(this.bricks.elements.length);
35160     },
35161     
35162     resize : function()
35163     {
35164         Roo.log('resize');
35165         var cs = this.el.getBox(true);
35166         
35167         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35168             Roo.log("no change in with or X");
35169             return;
35170         }
35171         this.currentSize = cs;
35172         this.layout();
35173     },
35174     
35175     layout : function()
35176     {
35177          Roo.log('layout');
35178         this._resetLayout();
35179         //this._manageStamps();
35180       
35181         // don't animate first layout
35182         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35183         this.layoutItems( isInstant );
35184       
35185         // flag for initalized
35186         this._isLayoutInited = true;
35187     },
35188     
35189     layoutItems : function( isInstant )
35190     {
35191         //var items = this._getItemsForLayout( this.items );
35192         // original code supports filtering layout items.. we just ignore it..
35193         
35194         this._layoutItems( this.bricks , isInstant );
35195       
35196         this._postLayout();
35197     },
35198     _layoutItems : function ( items , isInstant)
35199     {
35200        //this.fireEvent( 'layout', this, items );
35201     
35202
35203         if ( !items || !items.elements.length ) {
35204           // no items, emit event with empty array
35205             return;
35206         }
35207
35208         var queue = [];
35209         items.each(function(item) {
35210             Roo.log("layout item");
35211             Roo.log(item);
35212             // get x/y object from method
35213             var position = this._getItemLayoutPosition( item );
35214             // enqueue
35215             position.item = item;
35216             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35217             queue.push( position );
35218         }, this);
35219       
35220         this._processLayoutQueue( queue );
35221     },
35222     /** Sets position of item in DOM
35223     * @param {Element} item
35224     * @param {Number} x - horizontal position
35225     * @param {Number} y - vertical position
35226     * @param {Boolean} isInstant - disables transitions
35227     */
35228     _processLayoutQueue : function( queue )
35229     {
35230         for ( var i=0, len = queue.length; i < len; i++ ) {
35231             var obj = queue[i];
35232             obj.item.position('absolute');
35233             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35234         }
35235     },
35236       
35237     
35238     /**
35239     * Any logic you want to do after each layout,
35240     * i.e. size the container
35241     */
35242     _postLayout : function()
35243     {
35244         this.resizeContainer();
35245     },
35246     
35247     resizeContainer : function()
35248     {
35249         if ( !this.isResizingContainer ) {
35250             return;
35251         }
35252         var size = this._getContainerSize();
35253         if ( size ) {
35254             this.el.setSize(size.width,size.height);
35255             this.boxesEl.setSize(size.width,size.height);
35256         }
35257     },
35258     
35259     
35260     
35261     _resetLayout : function()
35262     {
35263         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35264         this.colWidth = this.el.getWidth();
35265         //this.gutter = this.el.getWidth(); 
35266         
35267         this.measureColumns();
35268
35269         // reset column Y
35270         var i = this.cols;
35271         this.colYs = [];
35272         while (i--) {
35273             this.colYs.push( 0 );
35274         }
35275     
35276         this.maxY = 0;
35277     },
35278
35279     measureColumns : function()
35280     {
35281         this.getContainerWidth();
35282       // if columnWidth is 0, default to outerWidth of first item
35283         if ( !this.columnWidth ) {
35284             var firstItem = this.bricks.first();
35285             Roo.log(firstItem);
35286             this.columnWidth  = this.containerWidth;
35287             if (firstItem && firstItem.attr('originalwidth') ) {
35288                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35289             }
35290             // columnWidth fall back to item of first element
35291             Roo.log("set column width?");
35292                         this.initialColumnWidth = this.columnWidth  ;
35293
35294             // if first elem has no width, default to size of container
35295             
35296         }
35297         
35298         
35299         if (this.initialColumnWidth) {
35300             this.columnWidth = this.initialColumnWidth;
35301         }
35302         
35303         
35304             
35305         // column width is fixed at the top - however if container width get's smaller we should
35306         // reduce it...
35307         
35308         // this bit calcs how man columns..
35309             
35310         var columnWidth = this.columnWidth += this.gutter;
35311       
35312         // calculate columns
35313         var containerWidth = this.containerWidth + this.gutter;
35314         
35315         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35316         // fix rounding errors, typically with gutters
35317         var excess = columnWidth - containerWidth % columnWidth;
35318         
35319         
35320         // if overshoot is less than a pixel, round up, otherwise floor it
35321         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35322         cols = Math[ mathMethod ]( cols );
35323         this.cols = Math.max( cols, 1 );
35324         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35325         
35326          // padding positioning..
35327         var totalColWidth = this.cols * this.columnWidth;
35328         var padavail = this.containerWidth - totalColWidth;
35329         // so for 2 columns - we need 3 'pads'
35330         
35331         var padNeeded = (1+this.cols) * this.padWidth;
35332         
35333         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35334         
35335         this.columnWidth += padExtra
35336         //this.padWidth = Math.floor(padavail /  ( this.cols));
35337         
35338         // adjust colum width so that padding is fixed??
35339         
35340         // we have 3 columns ... total = width * 3
35341         // we have X left over... that should be used by 
35342         
35343         //if (this.expandC) {
35344             
35345         //}
35346         
35347         
35348         
35349     },
35350     
35351     getContainerWidth : function()
35352     {
35353        /* // container is parent if fit width
35354         var container = this.isFitWidth ? this.element.parentNode : this.element;
35355         // check that this.size and size are there
35356         // IE8 triggers resize on body size change, so they might not be
35357         
35358         var size = getSize( container );  //FIXME
35359         this.containerWidth = size && size.innerWidth; //FIXME
35360         */
35361          
35362         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35363         
35364     },
35365     
35366     _getItemLayoutPosition : function( item )  // what is item?
35367     {
35368         // we resize the item to our columnWidth..
35369       
35370         item.setWidth(this.columnWidth);
35371         item.autoBoxAdjust  = false;
35372         
35373         var sz = item.getSize();
35374  
35375         // how many columns does this brick span
35376         var remainder = this.containerWidth % this.columnWidth;
35377         
35378         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35379         // round if off by 1 pixel, otherwise use ceil
35380         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35381         colSpan = Math.min( colSpan, this.cols );
35382         
35383         // normally this should be '1' as we dont' currently allow multi width columns..
35384         
35385         var colGroup = this._getColGroup( colSpan );
35386         // get the minimum Y value from the columns
35387         var minimumY = Math.min.apply( Math, colGroup );
35388         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35389         
35390         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35391          
35392         // position the brick
35393         var position = {
35394             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35395             y: this.currentSize.y + minimumY + this.padHeight
35396         };
35397         
35398         Roo.log(position);
35399         // apply setHeight to necessary columns
35400         var setHeight = minimumY + sz.height + this.padHeight;
35401         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35402         
35403         var setSpan = this.cols + 1 - colGroup.length;
35404         for ( var i = 0; i < setSpan; i++ ) {
35405           this.colYs[ shortColIndex + i ] = setHeight ;
35406         }
35407       
35408         return position;
35409     },
35410     
35411     /**
35412      * @param {Number} colSpan - number of columns the element spans
35413      * @returns {Array} colGroup
35414      */
35415     _getColGroup : function( colSpan )
35416     {
35417         if ( colSpan < 2 ) {
35418           // if brick spans only one column, use all the column Ys
35419           return this.colYs;
35420         }
35421       
35422         var colGroup = [];
35423         // how many different places could this brick fit horizontally
35424         var groupCount = this.cols + 1 - colSpan;
35425         // for each group potential horizontal position
35426         for ( var i = 0; i < groupCount; i++ ) {
35427           // make an array of colY values for that one group
35428           var groupColYs = this.colYs.slice( i, i + colSpan );
35429           // and get the max value of the array
35430           colGroup[i] = Math.max.apply( Math, groupColYs );
35431         }
35432         return colGroup;
35433     },
35434     /*
35435     _manageStamp : function( stamp )
35436     {
35437         var stampSize =  stamp.getSize();
35438         var offset = stamp.getBox();
35439         // get the columns that this stamp affects
35440         var firstX = this.isOriginLeft ? offset.x : offset.right;
35441         var lastX = firstX + stampSize.width;
35442         var firstCol = Math.floor( firstX / this.columnWidth );
35443         firstCol = Math.max( 0, firstCol );
35444         
35445         var lastCol = Math.floor( lastX / this.columnWidth );
35446         // lastCol should not go over if multiple of columnWidth #425
35447         lastCol -= lastX % this.columnWidth ? 0 : 1;
35448         lastCol = Math.min( this.cols - 1, lastCol );
35449         
35450         // set colYs to bottom of the stamp
35451         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35452             stampSize.height;
35453             
35454         for ( var i = firstCol; i <= lastCol; i++ ) {
35455           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35456         }
35457     },
35458     */
35459     
35460     _getContainerSize : function()
35461     {
35462         this.maxY = Math.max.apply( Math, this.colYs );
35463         var size = {
35464             height: this.maxY
35465         };
35466       
35467         if ( this.isFitWidth ) {
35468             size.width = this._getContainerFitWidth();
35469         }
35470       
35471         return size;
35472     },
35473     
35474     _getContainerFitWidth : function()
35475     {
35476         var unusedCols = 0;
35477         // count unused columns
35478         var i = this.cols;
35479         while ( --i ) {
35480           if ( this.colYs[i] !== 0 ) {
35481             break;
35482           }
35483           unusedCols++;
35484         }
35485         // fit container to columns that have been used
35486         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35487     },
35488     
35489     needsResizeLayout : function()
35490     {
35491         var previousWidth = this.containerWidth;
35492         this.getContainerWidth();
35493         return previousWidth !== this.containerWidth;
35494     }
35495  
35496 });
35497
35498  
35499
35500  /*
35501  * - LGPL
35502  *
35503  * element
35504  * 
35505  */
35506
35507 /**
35508  * @class Roo.bootstrap.MasonryBrick
35509  * @extends Roo.bootstrap.Component
35510  * Bootstrap MasonryBrick class
35511  * 
35512  * @constructor
35513  * Create a new MasonryBrick
35514  * @param {Object} config The config object
35515  */
35516
35517 Roo.bootstrap.MasonryBrick = function(config){
35518     
35519     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35520     
35521     Roo.bootstrap.MasonryBrick.register(this);
35522     
35523     this.addEvents({
35524         // raw events
35525         /**
35526          * @event click
35527          * When a MasonryBrick is clcik
35528          * @param {Roo.bootstrap.MasonryBrick} this
35529          * @param {Roo.EventObject} e
35530          */
35531         "click" : true
35532     });
35533 };
35534
35535 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35536     
35537     /**
35538      * @cfg {String} title
35539      */   
35540     title : '',
35541     /**
35542      * @cfg {String} html
35543      */   
35544     html : '',
35545     /**
35546      * @cfg {String} bgimage
35547      */   
35548     bgimage : '',
35549     /**
35550      * @cfg {String} videourl
35551      */   
35552     videourl : '',
35553     /**
35554      * @cfg {String} cls
35555      */   
35556     cls : '',
35557     /**
35558      * @cfg {String} href
35559      */   
35560     href : '',
35561     /**
35562      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35563      */   
35564     size : 'xs',
35565     
35566     /**
35567      * @cfg {String} placetitle (center|bottom)
35568      */   
35569     placetitle : '',
35570     
35571     /**
35572      * @cfg {Boolean} isFitContainer defalut true
35573      */   
35574     isFitContainer : true, 
35575     
35576     /**
35577      * @cfg {Boolean} preventDefault defalut false
35578      */   
35579     preventDefault : false, 
35580     
35581     /**
35582      * @cfg {Boolean} inverse defalut false
35583      */   
35584     maskInverse : false, 
35585     
35586     getAutoCreate : function()
35587     {
35588         if(!this.isFitContainer){
35589             return this.getSplitAutoCreate();
35590         }
35591         
35592         var cls = 'masonry-brick masonry-brick-full';
35593         
35594         if(this.href.length){
35595             cls += ' masonry-brick-link';
35596         }
35597         
35598         if(this.bgimage.length){
35599             cls += ' masonry-brick-image';
35600         }
35601         
35602         if(this.maskInverse){
35603             cls += ' mask-inverse';
35604         }
35605         
35606         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35607             cls += ' enable-mask';
35608         }
35609         
35610         if(this.size){
35611             cls += ' masonry-' + this.size + '-brick';
35612         }
35613         
35614         if(this.placetitle.length){
35615             
35616             switch (this.placetitle) {
35617                 case 'center' :
35618                     cls += ' masonry-center-title';
35619                     break;
35620                 case 'bottom' :
35621                     cls += ' masonry-bottom-title';
35622                     break;
35623                 default:
35624                     break;
35625             }
35626             
35627         } else {
35628             if(!this.html.length && !this.bgimage.length){
35629                 cls += ' masonry-center-title';
35630             }
35631
35632             if(!this.html.length && this.bgimage.length){
35633                 cls += ' masonry-bottom-title';
35634             }
35635         }
35636         
35637         if(this.cls){
35638             cls += ' ' + this.cls;
35639         }
35640         
35641         var cfg = {
35642             tag: (this.href.length) ? 'a' : 'div',
35643             cls: cls,
35644             cn: [
35645                 {
35646                     tag: 'div',
35647                     cls: 'masonry-brick-mask'
35648                 },
35649                 {
35650                     tag: 'div',
35651                     cls: 'masonry-brick-paragraph',
35652                     cn: []
35653                 }
35654             ]
35655         };
35656         
35657         if(this.href.length){
35658             cfg.href = this.href;
35659         }
35660         
35661         var cn = cfg.cn[1].cn;
35662         
35663         if(this.title.length){
35664             cn.push({
35665                 tag: 'h4',
35666                 cls: 'masonry-brick-title',
35667                 html: this.title
35668             });
35669         }
35670         
35671         if(this.html.length){
35672             cn.push({
35673                 tag: 'p',
35674                 cls: 'masonry-brick-text',
35675                 html: this.html
35676             });
35677         }
35678         
35679         if (!this.title.length && !this.html.length) {
35680             cfg.cn[1].cls += ' hide';
35681         }
35682         
35683         if(this.bgimage.length){
35684             cfg.cn.push({
35685                 tag: 'img',
35686                 cls: 'masonry-brick-image-view',
35687                 src: this.bgimage
35688             });
35689         }
35690         
35691         if(this.videourl.length){
35692             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35693             // youtube support only?
35694             cfg.cn.push({
35695                 tag: 'iframe',
35696                 cls: 'masonry-brick-image-view',
35697                 src: vurl,
35698                 frameborder : 0,
35699                 allowfullscreen : true
35700             });
35701         }
35702         
35703         return cfg;
35704         
35705     },
35706     
35707     getSplitAutoCreate : function()
35708     {
35709         var cls = 'masonry-brick masonry-brick-split';
35710         
35711         if(this.href.length){
35712             cls += ' masonry-brick-link';
35713         }
35714         
35715         if(this.bgimage.length){
35716             cls += ' masonry-brick-image';
35717         }
35718         
35719         if(this.size){
35720             cls += ' masonry-' + this.size + '-brick';
35721         }
35722         
35723         switch (this.placetitle) {
35724             case 'center' :
35725                 cls += ' masonry-center-title';
35726                 break;
35727             case 'bottom' :
35728                 cls += ' masonry-bottom-title';
35729                 break;
35730             default:
35731                 if(!this.bgimage.length){
35732                     cls += ' masonry-center-title';
35733                 }
35734
35735                 if(this.bgimage.length){
35736                     cls += ' masonry-bottom-title';
35737                 }
35738                 break;
35739         }
35740         
35741         if(this.cls){
35742             cls += ' ' + this.cls;
35743         }
35744         
35745         var cfg = {
35746             tag: (this.href.length) ? 'a' : 'div',
35747             cls: cls,
35748             cn: [
35749                 {
35750                     tag: 'div',
35751                     cls: 'masonry-brick-split-head',
35752                     cn: [
35753                         {
35754                             tag: 'div',
35755                             cls: 'masonry-brick-paragraph',
35756                             cn: []
35757                         }
35758                     ]
35759                 },
35760                 {
35761                     tag: 'div',
35762                     cls: 'masonry-brick-split-body',
35763                     cn: []
35764                 }
35765             ]
35766         };
35767         
35768         if(this.href.length){
35769             cfg.href = this.href;
35770         }
35771         
35772         if(this.title.length){
35773             cfg.cn[0].cn[0].cn.push({
35774                 tag: 'h4',
35775                 cls: 'masonry-brick-title',
35776                 html: this.title
35777             });
35778         }
35779         
35780         if(this.html.length){
35781             cfg.cn[1].cn.push({
35782                 tag: 'p',
35783                 cls: 'masonry-brick-text',
35784                 html: this.html
35785             });
35786         }
35787
35788         if(this.bgimage.length){
35789             cfg.cn[0].cn.push({
35790                 tag: 'img',
35791                 cls: 'masonry-brick-image-view',
35792                 src: this.bgimage
35793             });
35794         }
35795         
35796         if(this.videourl.length){
35797             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35798             // youtube support only?
35799             cfg.cn[0].cn.cn.push({
35800                 tag: 'iframe',
35801                 cls: 'masonry-brick-image-view',
35802                 src: vurl,
35803                 frameborder : 0,
35804                 allowfullscreen : true
35805             });
35806         }
35807         
35808         return cfg;
35809     },
35810     
35811     initEvents: function() 
35812     {
35813         switch (this.size) {
35814             case 'xs' :
35815                 this.x = 1;
35816                 this.y = 1;
35817                 break;
35818             case 'sm' :
35819                 this.x = 2;
35820                 this.y = 2;
35821                 break;
35822             case 'md' :
35823             case 'md-left' :
35824             case 'md-right' :
35825                 this.x = 3;
35826                 this.y = 3;
35827                 break;
35828             case 'tall' :
35829                 this.x = 2;
35830                 this.y = 3;
35831                 break;
35832             case 'wide' :
35833                 this.x = 3;
35834                 this.y = 2;
35835                 break;
35836             case 'wide-thin' :
35837                 this.x = 3;
35838                 this.y = 1;
35839                 break;
35840                         
35841             default :
35842                 break;
35843         }
35844         
35845         if(Roo.isTouch){
35846             this.el.on('touchstart', this.onTouchStart, this);
35847             this.el.on('touchmove', this.onTouchMove, this);
35848             this.el.on('touchend', this.onTouchEnd, this);
35849             this.el.on('contextmenu', this.onContextMenu, this);
35850         } else {
35851             this.el.on('mouseenter'  ,this.enter, this);
35852             this.el.on('mouseleave', this.leave, this);
35853             this.el.on('click', this.onClick, this);
35854         }
35855         
35856         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35857             this.parent().bricks.push(this);   
35858         }
35859         
35860     },
35861     
35862     onClick: function(e, el)
35863     {
35864         var time = this.endTimer - this.startTimer;
35865         // Roo.log(e.preventDefault());
35866         if(Roo.isTouch){
35867             if(time > 1000){
35868                 e.preventDefault();
35869                 return;
35870             }
35871         }
35872         
35873         if(!this.preventDefault){
35874             return;
35875         }
35876         
35877         e.preventDefault();
35878         
35879         if (this.activeClass != '') {
35880             this.selectBrick();
35881         }
35882         
35883         this.fireEvent('click', this, e);
35884     },
35885     
35886     enter: function(e, el)
35887     {
35888         e.preventDefault();
35889         
35890         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35891             return;
35892         }
35893         
35894         if(this.bgimage.length && this.html.length){
35895             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35896         }
35897     },
35898     
35899     leave: function(e, el)
35900     {
35901         e.preventDefault();
35902         
35903         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35904             return;
35905         }
35906         
35907         if(this.bgimage.length && this.html.length){
35908             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35909         }
35910     },
35911     
35912     onTouchStart: function(e, el)
35913     {
35914 //        e.preventDefault();
35915         
35916         this.touchmoved = false;
35917         
35918         if(!this.isFitContainer){
35919             return;
35920         }
35921         
35922         if(!this.bgimage.length || !this.html.length){
35923             return;
35924         }
35925         
35926         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35927         
35928         this.timer = new Date().getTime();
35929         
35930     },
35931     
35932     onTouchMove: function(e, el)
35933     {
35934         this.touchmoved = true;
35935     },
35936     
35937     onContextMenu : function(e,el)
35938     {
35939         e.preventDefault();
35940         e.stopPropagation();
35941         return false;
35942     },
35943     
35944     onTouchEnd: function(e, el)
35945     {
35946 //        e.preventDefault();
35947         
35948         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35949         
35950             this.leave(e,el);
35951             
35952             return;
35953         }
35954         
35955         if(!this.bgimage.length || !this.html.length){
35956             
35957             if(this.href.length){
35958                 window.location.href = this.href;
35959             }
35960             
35961             return;
35962         }
35963         
35964         if(!this.isFitContainer){
35965             return;
35966         }
35967         
35968         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35969         
35970         window.location.href = this.href;
35971     },
35972     
35973     //selection on single brick only
35974     selectBrick : function() {
35975         
35976         if (!this.parentId) {
35977             return;
35978         }
35979         
35980         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35981         var index = m.selectedBrick.indexOf(this.id);
35982         
35983         if ( index > -1) {
35984             m.selectedBrick.splice(index,1);
35985             this.el.removeClass(this.activeClass);
35986             return;
35987         }
35988         
35989         for(var i = 0; i < m.selectedBrick.length; i++) {
35990             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35991             b.el.removeClass(b.activeClass);
35992         }
35993         
35994         m.selectedBrick = [];
35995         
35996         m.selectedBrick.push(this.id);
35997         this.el.addClass(this.activeClass);
35998         return;
35999     },
36000     
36001     isSelected : function(){
36002         return this.el.hasClass(this.activeClass);
36003         
36004     }
36005 });
36006
36007 Roo.apply(Roo.bootstrap.MasonryBrick, {
36008     
36009     //groups: {},
36010     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36011      /**
36012     * register a Masonry Brick
36013     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36014     */
36015     
36016     register : function(brick)
36017     {
36018         //this.groups[brick.id] = brick;
36019         this.groups.add(brick.id, brick);
36020     },
36021     /**
36022     * fetch a  masonry brick based on the masonry brick ID
36023     * @param {string} the masonry brick to add
36024     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36025     */
36026     
36027     get: function(brick_id) 
36028     {
36029         // if (typeof(this.groups[brick_id]) == 'undefined') {
36030         //     return false;
36031         // }
36032         // return this.groups[brick_id] ;
36033         
36034         if(this.groups.key(brick_id)) {
36035             return this.groups.key(brick_id);
36036         }
36037         
36038         return false;
36039     }
36040     
36041     
36042     
36043 });
36044
36045  /*
36046  * - LGPL
36047  *
36048  * element
36049  * 
36050  */
36051
36052 /**
36053  * @class Roo.bootstrap.Brick
36054  * @extends Roo.bootstrap.Component
36055  * Bootstrap Brick class
36056  * 
36057  * @constructor
36058  * Create a new Brick
36059  * @param {Object} config The config object
36060  */
36061
36062 Roo.bootstrap.Brick = function(config){
36063     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36064     
36065     this.addEvents({
36066         // raw events
36067         /**
36068          * @event click
36069          * When a Brick is click
36070          * @param {Roo.bootstrap.Brick} this
36071          * @param {Roo.EventObject} e
36072          */
36073         "click" : true
36074     });
36075 };
36076
36077 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36078     
36079     /**
36080      * @cfg {String} title
36081      */   
36082     title : '',
36083     /**
36084      * @cfg {String} html
36085      */   
36086     html : '',
36087     /**
36088      * @cfg {String} bgimage
36089      */   
36090     bgimage : '',
36091     /**
36092      * @cfg {String} cls
36093      */   
36094     cls : '',
36095     /**
36096      * @cfg {String} href
36097      */   
36098     href : '',
36099     /**
36100      * @cfg {String} video
36101      */   
36102     video : '',
36103     /**
36104      * @cfg {Boolean} square
36105      */   
36106     square : true,
36107     
36108     getAutoCreate : function()
36109     {
36110         var cls = 'roo-brick';
36111         
36112         if(this.href.length){
36113             cls += ' roo-brick-link';
36114         }
36115         
36116         if(this.bgimage.length){
36117             cls += ' roo-brick-image';
36118         }
36119         
36120         if(!this.html.length && !this.bgimage.length){
36121             cls += ' roo-brick-center-title';
36122         }
36123         
36124         if(!this.html.length && this.bgimage.length){
36125             cls += ' roo-brick-bottom-title';
36126         }
36127         
36128         if(this.cls){
36129             cls += ' ' + this.cls;
36130         }
36131         
36132         var cfg = {
36133             tag: (this.href.length) ? 'a' : 'div',
36134             cls: cls,
36135             cn: [
36136                 {
36137                     tag: 'div',
36138                     cls: 'roo-brick-paragraph',
36139                     cn: []
36140                 }
36141             ]
36142         };
36143         
36144         if(this.href.length){
36145             cfg.href = this.href;
36146         }
36147         
36148         var cn = cfg.cn[0].cn;
36149         
36150         if(this.title.length){
36151             cn.push({
36152                 tag: 'h4',
36153                 cls: 'roo-brick-title',
36154                 html: this.title
36155             });
36156         }
36157         
36158         if(this.html.length){
36159             cn.push({
36160                 tag: 'p',
36161                 cls: 'roo-brick-text',
36162                 html: this.html
36163             });
36164         } else {
36165             cn.cls += ' hide';
36166         }
36167         
36168         if(this.bgimage.length){
36169             cfg.cn.push({
36170                 tag: 'img',
36171                 cls: 'roo-brick-image-view',
36172                 src: this.bgimage
36173             });
36174         }
36175         
36176         return cfg;
36177     },
36178     
36179     initEvents: function() 
36180     {
36181         if(this.title.length || this.html.length){
36182             this.el.on('mouseenter'  ,this.enter, this);
36183             this.el.on('mouseleave', this.leave, this);
36184         }
36185         
36186         Roo.EventManager.onWindowResize(this.resize, this); 
36187         
36188         if(this.bgimage.length){
36189             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36190             this.imageEl.on('load', this.onImageLoad, this);
36191             return;
36192         }
36193         
36194         this.resize();
36195     },
36196     
36197     onImageLoad : function()
36198     {
36199         this.resize();
36200     },
36201     
36202     resize : function()
36203     {
36204         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36205         
36206         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36207         
36208         if(this.bgimage.length){
36209             var image = this.el.select('.roo-brick-image-view', true).first();
36210             
36211             image.setWidth(paragraph.getWidth());
36212             
36213             if(this.square){
36214                 image.setHeight(paragraph.getWidth());
36215             }
36216             
36217             this.el.setHeight(image.getHeight());
36218             paragraph.setHeight(image.getHeight());
36219             
36220         }
36221         
36222     },
36223     
36224     enter: function(e, el)
36225     {
36226         e.preventDefault();
36227         
36228         if(this.bgimage.length){
36229             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36230             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36231         }
36232     },
36233     
36234     leave: function(e, el)
36235     {
36236         e.preventDefault();
36237         
36238         if(this.bgimage.length){
36239             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36240             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36241         }
36242     }
36243     
36244 });
36245
36246  
36247
36248  /*
36249  * - LGPL
36250  *
36251  * Number field 
36252  */
36253
36254 /**
36255  * @class Roo.bootstrap.NumberField
36256  * @extends Roo.bootstrap.Input
36257  * Bootstrap NumberField class
36258  * 
36259  * 
36260  * 
36261  * 
36262  * @constructor
36263  * Create a new NumberField
36264  * @param {Object} config The config object
36265  */
36266
36267 Roo.bootstrap.NumberField = function(config){
36268     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36269 };
36270
36271 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36272     
36273     /**
36274      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36275      */
36276     allowDecimals : true,
36277     /**
36278      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36279      */
36280     decimalSeparator : ".",
36281     /**
36282      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36283      */
36284     decimalPrecision : 2,
36285     /**
36286      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36287      */
36288     allowNegative : true,
36289     
36290     /**
36291      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36292      */
36293     allowZero: true,
36294     /**
36295      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36296      */
36297     minValue : Number.NEGATIVE_INFINITY,
36298     /**
36299      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36300      */
36301     maxValue : Number.MAX_VALUE,
36302     /**
36303      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36304      */
36305     minText : "The minimum value for this field is {0}",
36306     /**
36307      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36308      */
36309     maxText : "The maximum value for this field is {0}",
36310     /**
36311      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36312      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36313      */
36314     nanText : "{0} is not a valid number",
36315     /**
36316      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36317      */
36318     thousandsDelimiter : false,
36319     /**
36320      * @cfg {String} valueAlign alignment of value
36321      */
36322     valueAlign : "left",
36323
36324     getAutoCreate : function()
36325     {
36326         var hiddenInput = {
36327             tag: 'input',
36328             type: 'hidden',
36329             id: Roo.id(),
36330             cls: 'hidden-number-input'
36331         };
36332         
36333         if (this.name) {
36334             hiddenInput.name = this.name;
36335         }
36336         
36337         this.name = '';
36338         
36339         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36340         
36341         this.name = hiddenInput.name;
36342         
36343         if(cfg.cn.length > 0) {
36344             cfg.cn.push(hiddenInput);
36345         }
36346         
36347         return cfg;
36348     },
36349
36350     // private
36351     initEvents : function()
36352     {   
36353         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36354         
36355         var allowed = "0123456789";
36356         
36357         if(this.allowDecimals){
36358             allowed += this.decimalSeparator;
36359         }
36360         
36361         if(this.allowNegative){
36362             allowed += "-";
36363         }
36364         
36365         if(this.thousandsDelimiter) {
36366             allowed += ",";
36367         }
36368         
36369         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36370         
36371         var keyPress = function(e){
36372             
36373             var k = e.getKey();
36374             
36375             var c = e.getCharCode();
36376             
36377             if(
36378                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36379                     allowed.indexOf(String.fromCharCode(c)) === -1
36380             ){
36381                 e.stopEvent();
36382                 return;
36383             }
36384             
36385             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36386                 return;
36387             }
36388             
36389             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36390                 e.stopEvent();
36391             }
36392         };
36393         
36394         this.el.on("keypress", keyPress, this);
36395     },
36396     
36397     validateValue : function(value)
36398     {
36399         
36400         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36401             return false;
36402         }
36403         
36404         var num = this.parseValue(value);
36405         
36406         if(isNaN(num)){
36407             this.markInvalid(String.format(this.nanText, value));
36408             return false;
36409         }
36410         
36411         if(num < this.minValue){
36412             this.markInvalid(String.format(this.minText, this.minValue));
36413             return false;
36414         }
36415         
36416         if(num > this.maxValue){
36417             this.markInvalid(String.format(this.maxText, this.maxValue));
36418             return false;
36419         }
36420         
36421         return true;
36422     },
36423
36424     getValue : function()
36425     {
36426         var v = this.hiddenEl().getValue();
36427         
36428         return this.fixPrecision(this.parseValue(v));
36429     },
36430
36431     parseValue : function(value)
36432     {
36433         if(this.thousandsDelimiter) {
36434             value += "";
36435             r = new RegExp(",", "g");
36436             value = value.replace(r, "");
36437         }
36438         
36439         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36440         return isNaN(value) ? '' : value;
36441     },
36442
36443     fixPrecision : function(value)
36444     {
36445         if(this.thousandsDelimiter) {
36446             value += "";
36447             r = new RegExp(",", "g");
36448             value = value.replace(r, "");
36449         }
36450         
36451         var nan = isNaN(value);
36452         
36453         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36454             return nan ? '' : value;
36455         }
36456         return parseFloat(value).toFixed(this.decimalPrecision);
36457     },
36458
36459     setValue : function(v)
36460     {
36461         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36462         
36463         this.value = v;
36464         
36465         if(this.rendered){
36466             
36467             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36468             
36469             this.inputEl().dom.value = (v == '') ? '' :
36470                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36471             
36472             if(!this.allowZero && v === '0') {
36473                 this.hiddenEl().dom.value = '';
36474                 this.inputEl().dom.value = '';
36475             }
36476             
36477             this.validate();
36478         }
36479     },
36480
36481     decimalPrecisionFcn : function(v)
36482     {
36483         return Math.floor(v);
36484     },
36485
36486     beforeBlur : function()
36487     {
36488         var v = this.parseValue(this.getRawValue());
36489         
36490         if(v || v === 0 || v === ''){
36491             this.setValue(v);
36492         }
36493     },
36494     
36495     hiddenEl : function()
36496     {
36497         return this.el.select('input.hidden-number-input',true).first();
36498     }
36499     
36500 });
36501
36502  
36503
36504 /*
36505 * Licence: LGPL
36506 */
36507
36508 /**
36509  * @class Roo.bootstrap.DocumentSlider
36510  * @extends Roo.bootstrap.Component
36511  * Bootstrap DocumentSlider class
36512  * 
36513  * @constructor
36514  * Create a new DocumentViewer
36515  * @param {Object} config The config object
36516  */
36517
36518 Roo.bootstrap.DocumentSlider = function(config){
36519     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36520     
36521     this.files = [];
36522     
36523     this.addEvents({
36524         /**
36525          * @event initial
36526          * Fire after initEvent
36527          * @param {Roo.bootstrap.DocumentSlider} this
36528          */
36529         "initial" : true,
36530         /**
36531          * @event update
36532          * Fire after update
36533          * @param {Roo.bootstrap.DocumentSlider} this
36534          */
36535         "update" : true,
36536         /**
36537          * @event click
36538          * Fire after click
36539          * @param {Roo.bootstrap.DocumentSlider} this
36540          */
36541         "click" : true
36542     });
36543 };
36544
36545 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36546     
36547     files : false,
36548     
36549     indicator : 0,
36550     
36551     getAutoCreate : function()
36552     {
36553         var cfg = {
36554             tag : 'div',
36555             cls : 'roo-document-slider',
36556             cn : [
36557                 {
36558                     tag : 'div',
36559                     cls : 'roo-document-slider-header',
36560                     cn : [
36561                         {
36562                             tag : 'div',
36563                             cls : 'roo-document-slider-header-title'
36564                         }
36565                     ]
36566                 },
36567                 {
36568                     tag : 'div',
36569                     cls : 'roo-document-slider-body',
36570                     cn : [
36571                         {
36572                             tag : 'div',
36573                             cls : 'roo-document-slider-prev',
36574                             cn : [
36575                                 {
36576                                     tag : 'i',
36577                                     cls : 'fa fa-chevron-left'
36578                                 }
36579                             ]
36580                         },
36581                         {
36582                             tag : 'div',
36583                             cls : 'roo-document-slider-thumb',
36584                             cn : [
36585                                 {
36586                                     tag : 'img',
36587                                     cls : 'roo-document-slider-image'
36588                                 }
36589                             ]
36590                         },
36591                         {
36592                             tag : 'div',
36593                             cls : 'roo-document-slider-next',
36594                             cn : [
36595                                 {
36596                                     tag : 'i',
36597                                     cls : 'fa fa-chevron-right'
36598                                 }
36599                             ]
36600                         }
36601                     ]
36602                 }
36603             ]
36604         };
36605         
36606         return cfg;
36607     },
36608     
36609     initEvents : function()
36610     {
36611         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36612         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36613         
36614         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36615         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36616         
36617         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36618         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36619         
36620         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36621         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36622         
36623         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36624         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36625         
36626         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36627         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36628         
36629         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36630         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36631         
36632         this.thumbEl.on('click', this.onClick, this);
36633         
36634         this.prevIndicator.on('click', this.prev, this);
36635         
36636         this.nextIndicator.on('click', this.next, this);
36637         
36638     },
36639     
36640     initial : function()
36641     {
36642         if(this.files.length){
36643             this.indicator = 1;
36644             this.update()
36645         }
36646         
36647         this.fireEvent('initial', this);
36648     },
36649     
36650     update : function()
36651     {
36652         this.imageEl.attr('src', this.files[this.indicator - 1]);
36653         
36654         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36655         
36656         this.prevIndicator.show();
36657         
36658         if(this.indicator == 1){
36659             this.prevIndicator.hide();
36660         }
36661         
36662         this.nextIndicator.show();
36663         
36664         if(this.indicator == this.files.length){
36665             this.nextIndicator.hide();
36666         }
36667         
36668         this.thumbEl.scrollTo('top');
36669         
36670         this.fireEvent('update', this);
36671     },
36672     
36673     onClick : function(e)
36674     {
36675         e.preventDefault();
36676         
36677         this.fireEvent('click', this);
36678     },
36679     
36680     prev : function(e)
36681     {
36682         e.preventDefault();
36683         
36684         this.indicator = Math.max(1, this.indicator - 1);
36685         
36686         this.update();
36687     },
36688     
36689     next : function(e)
36690     {
36691         e.preventDefault();
36692         
36693         this.indicator = Math.min(this.files.length, this.indicator + 1);
36694         
36695         this.update();
36696     }
36697 });
36698 /*
36699  * - LGPL
36700  *
36701  * RadioSet
36702  *
36703  *
36704  */
36705
36706 /**
36707  * @class Roo.bootstrap.RadioSet
36708  * @extends Roo.bootstrap.Input
36709  * Bootstrap RadioSet class
36710  * @cfg {String} indicatorpos (left|right) default left
36711  * @cfg {Boolean} inline (true|false) inline the element (default true)
36712  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36713  * @constructor
36714  * Create a new RadioSet
36715  * @param {Object} config The config object
36716  */
36717
36718 Roo.bootstrap.RadioSet = function(config){
36719     
36720     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36721     
36722     this.radioes = [];
36723     
36724     Roo.bootstrap.RadioSet.register(this);
36725     
36726     this.addEvents({
36727         /**
36728         * @event check
36729         * Fires when the element is checked or unchecked.
36730         * @param {Roo.bootstrap.RadioSet} this This radio
36731         * @param {Roo.bootstrap.Radio} item The checked item
36732         */
36733        check : true,
36734        /**
36735         * @event click
36736         * Fires when the element is click.
36737         * @param {Roo.bootstrap.RadioSet} this This radio set
36738         * @param {Roo.bootstrap.Radio} item The checked item
36739         * @param {Roo.EventObject} e The event object
36740         */
36741        click : true
36742     });
36743     
36744 };
36745
36746 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36747
36748     radioes : false,
36749     
36750     inline : true,
36751     
36752     weight : '',
36753     
36754     indicatorpos : 'left',
36755     
36756     getAutoCreate : function()
36757     {
36758         var label = {
36759             tag : 'label',
36760             cls : 'roo-radio-set-label',
36761             cn : [
36762                 {
36763                     tag : 'span',
36764                     html : this.fieldLabel
36765                 }
36766             ]
36767         };
36768         if (Roo.bootstrap.version == 3) {
36769             
36770             
36771             if(this.indicatorpos == 'left'){
36772                 label.cn.unshift({
36773                     tag : 'i',
36774                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36775                     tooltip : 'This field is required'
36776                 });
36777             } else {
36778                 label.cn.push({
36779                     tag : 'i',
36780                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36781                     tooltip : 'This field is required'
36782                 });
36783             }
36784         }
36785         var items = {
36786             tag : 'div',
36787             cls : 'roo-radio-set-items'
36788         };
36789         
36790         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36791         
36792         if (align === 'left' && this.fieldLabel.length) {
36793             
36794             items = {
36795                 cls : "roo-radio-set-right", 
36796                 cn: [
36797                     items
36798                 ]
36799             };
36800             
36801             if(this.labelWidth > 12){
36802                 label.style = "width: " + this.labelWidth + 'px';
36803             }
36804             
36805             if(this.labelWidth < 13 && this.labelmd == 0){
36806                 this.labelmd = this.labelWidth;
36807             }
36808             
36809             if(this.labellg > 0){
36810                 label.cls += ' col-lg-' + this.labellg;
36811                 items.cls += ' col-lg-' + (12 - this.labellg);
36812             }
36813             
36814             if(this.labelmd > 0){
36815                 label.cls += ' col-md-' + this.labelmd;
36816                 items.cls += ' col-md-' + (12 - this.labelmd);
36817             }
36818             
36819             if(this.labelsm > 0){
36820                 label.cls += ' col-sm-' + this.labelsm;
36821                 items.cls += ' col-sm-' + (12 - this.labelsm);
36822             }
36823             
36824             if(this.labelxs > 0){
36825                 label.cls += ' col-xs-' + this.labelxs;
36826                 items.cls += ' col-xs-' + (12 - this.labelxs);
36827             }
36828         }
36829         
36830         var cfg = {
36831             tag : 'div',
36832             cls : 'roo-radio-set',
36833             cn : [
36834                 {
36835                     tag : 'input',
36836                     cls : 'roo-radio-set-input',
36837                     type : 'hidden',
36838                     name : this.name,
36839                     value : this.value ? this.value :  ''
36840                 },
36841                 label,
36842                 items
36843             ]
36844         };
36845         
36846         if(this.weight.length){
36847             cfg.cls += ' roo-radio-' + this.weight;
36848         }
36849         
36850         if(this.inline) {
36851             cfg.cls += ' roo-radio-set-inline';
36852         }
36853         
36854         var settings=this;
36855         ['xs','sm','md','lg'].map(function(size){
36856             if (settings[size]) {
36857                 cfg.cls += ' col-' + size + '-' + settings[size];
36858             }
36859         });
36860         
36861         return cfg;
36862         
36863     },
36864
36865     initEvents : function()
36866     {
36867         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36868         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36869         
36870         if(!this.fieldLabel.length){
36871             this.labelEl.hide();
36872         }
36873         
36874         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36875         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36876         
36877         this.indicator = this.indicatorEl();
36878         
36879         if(this.indicator){
36880             this.indicator.addClass('invisible');
36881         }
36882         
36883         this.originalValue = this.getValue();
36884         
36885     },
36886     
36887     inputEl: function ()
36888     {
36889         return this.el.select('.roo-radio-set-input', true).first();
36890     },
36891     
36892     getChildContainer : function()
36893     {
36894         return this.itemsEl;
36895     },
36896     
36897     register : function(item)
36898     {
36899         this.radioes.push(item);
36900         
36901     },
36902     
36903     validate : function()
36904     {   
36905         if(this.getVisibilityEl().hasClass('hidden')){
36906             return true;
36907         }
36908         
36909         var valid = false;
36910         
36911         Roo.each(this.radioes, function(i){
36912             if(!i.checked){
36913                 return;
36914             }
36915             
36916             valid = true;
36917             return false;
36918         });
36919         
36920         if(this.allowBlank) {
36921             return true;
36922         }
36923         
36924         if(this.disabled || valid){
36925             this.markValid();
36926             return true;
36927         }
36928         
36929         this.markInvalid();
36930         return false;
36931         
36932     },
36933     
36934     markValid : function()
36935     {
36936         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36937             this.indicatorEl().removeClass('visible');
36938             this.indicatorEl().addClass('invisible');
36939         }
36940         
36941         
36942         if (Roo.bootstrap.version == 3) {
36943             this.el.removeClass([this.invalidClass, this.validClass]);
36944             this.el.addClass(this.validClass);
36945         } else {
36946             this.el.removeClass(['is-invalid','is-valid']);
36947             this.el.addClass(['is-valid']);
36948         }
36949         this.fireEvent('valid', this);
36950     },
36951     
36952     markInvalid : function(msg)
36953     {
36954         if(this.allowBlank || this.disabled){
36955             return;
36956         }
36957         
36958         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36959             this.indicatorEl().removeClass('invisible');
36960             this.indicatorEl().addClass('visible');
36961         }
36962         if (Roo.bootstrap.version == 3) {
36963             this.el.removeClass([this.invalidClass, this.validClass]);
36964             this.el.addClass(this.invalidClass);
36965         } else {
36966             this.el.removeClass(['is-invalid','is-valid']);
36967             this.el.addClass(['is-invalid']);
36968         }
36969         
36970         this.fireEvent('invalid', this, msg);
36971         
36972     },
36973     
36974     setValue : function(v, suppressEvent)
36975     {   
36976         if(this.value === v){
36977             return;
36978         }
36979         
36980         this.value = v;
36981         
36982         if(this.rendered){
36983             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36984         }
36985         
36986         Roo.each(this.radioes, function(i){
36987             i.checked = false;
36988             i.el.removeClass('checked');
36989         });
36990         
36991         Roo.each(this.radioes, function(i){
36992             
36993             if(i.value === v || i.value.toString() === v.toString()){
36994                 i.checked = true;
36995                 i.el.addClass('checked');
36996                 
36997                 if(suppressEvent !== true){
36998                     this.fireEvent('check', this, i);
36999                 }
37000                 
37001                 return false;
37002             }
37003             
37004         }, this);
37005         
37006         this.validate();
37007     },
37008     
37009     clearInvalid : function(){
37010         
37011         if(!this.el || this.preventMark){
37012             return;
37013         }
37014         
37015         this.el.removeClass([this.invalidClass]);
37016         
37017         this.fireEvent('valid', this);
37018     }
37019     
37020 });
37021
37022 Roo.apply(Roo.bootstrap.RadioSet, {
37023     
37024     groups: {},
37025     
37026     register : function(set)
37027     {
37028         this.groups[set.name] = set;
37029     },
37030     
37031     get: function(name) 
37032     {
37033         if (typeof(this.groups[name]) == 'undefined') {
37034             return false;
37035         }
37036         
37037         return this.groups[name] ;
37038     }
37039     
37040 });
37041 /*
37042  * Based on:
37043  * Ext JS Library 1.1.1
37044  * Copyright(c) 2006-2007, Ext JS, LLC.
37045  *
37046  * Originally Released Under LGPL - original licence link has changed is not relivant.
37047  *
37048  * Fork - LGPL
37049  * <script type="text/javascript">
37050  */
37051
37052
37053 /**
37054  * @class Roo.bootstrap.SplitBar
37055  * @extends Roo.util.Observable
37056  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37057  * <br><br>
37058  * Usage:
37059  * <pre><code>
37060 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37061                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37062 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37063 split.minSize = 100;
37064 split.maxSize = 600;
37065 split.animate = true;
37066 split.on('moved', splitterMoved);
37067 </code></pre>
37068  * @constructor
37069  * Create a new SplitBar
37070  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37071  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37072  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37073  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37074                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37075                         position of the SplitBar).
37076  */
37077 Roo.bootstrap.SplitBar = function(cfg){
37078     
37079     /** @private */
37080     
37081     //{
37082     //  dragElement : elm
37083     //  resizingElement: el,
37084         // optional..
37085     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37086     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37087         // existingProxy ???
37088     //}
37089     
37090     this.el = Roo.get(cfg.dragElement, true);
37091     this.el.dom.unselectable = "on";
37092     /** @private */
37093     this.resizingEl = Roo.get(cfg.resizingElement, true);
37094
37095     /**
37096      * @private
37097      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37098      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37099      * @type Number
37100      */
37101     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37102     
37103     /**
37104      * The minimum size of the resizing element. (Defaults to 0)
37105      * @type Number
37106      */
37107     this.minSize = 0;
37108     
37109     /**
37110      * The maximum size of the resizing element. (Defaults to 2000)
37111      * @type Number
37112      */
37113     this.maxSize = 2000;
37114     
37115     /**
37116      * Whether to animate the transition to the new size
37117      * @type Boolean
37118      */
37119     this.animate = false;
37120     
37121     /**
37122      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37123      * @type Boolean
37124      */
37125     this.useShim = false;
37126     
37127     /** @private */
37128     this.shim = null;
37129     
37130     if(!cfg.existingProxy){
37131         /** @private */
37132         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37133     }else{
37134         this.proxy = Roo.get(cfg.existingProxy).dom;
37135     }
37136     /** @private */
37137     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37138     
37139     /** @private */
37140     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37141     
37142     /** @private */
37143     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37144     
37145     /** @private */
37146     this.dragSpecs = {};
37147     
37148     /**
37149      * @private The adapter to use to positon and resize elements
37150      */
37151     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37152     this.adapter.init(this);
37153     
37154     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37155         /** @private */
37156         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37157         this.el.addClass("roo-splitbar-h");
37158     }else{
37159         /** @private */
37160         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37161         this.el.addClass("roo-splitbar-v");
37162     }
37163     
37164     this.addEvents({
37165         /**
37166          * @event resize
37167          * Fires when the splitter is moved (alias for {@link #event-moved})
37168          * @param {Roo.bootstrap.SplitBar} this
37169          * @param {Number} newSize the new width or height
37170          */
37171         "resize" : true,
37172         /**
37173          * @event moved
37174          * Fires when the splitter is moved
37175          * @param {Roo.bootstrap.SplitBar} this
37176          * @param {Number} newSize the new width or height
37177          */
37178         "moved" : true,
37179         /**
37180          * @event beforeresize
37181          * Fires before the splitter is dragged
37182          * @param {Roo.bootstrap.SplitBar} this
37183          */
37184         "beforeresize" : true,
37185
37186         "beforeapply" : true
37187     });
37188
37189     Roo.util.Observable.call(this);
37190 };
37191
37192 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37193     onStartProxyDrag : function(x, y){
37194         this.fireEvent("beforeresize", this);
37195         if(!this.overlay){
37196             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37197             o.unselectable();
37198             o.enableDisplayMode("block");
37199             // all splitbars share the same overlay
37200             Roo.bootstrap.SplitBar.prototype.overlay = o;
37201         }
37202         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37203         this.overlay.show();
37204         Roo.get(this.proxy).setDisplayed("block");
37205         var size = this.adapter.getElementSize(this);
37206         this.activeMinSize = this.getMinimumSize();;
37207         this.activeMaxSize = this.getMaximumSize();;
37208         var c1 = size - this.activeMinSize;
37209         var c2 = Math.max(this.activeMaxSize - size, 0);
37210         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37211             this.dd.resetConstraints();
37212             this.dd.setXConstraint(
37213                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37214                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37215             );
37216             this.dd.setYConstraint(0, 0);
37217         }else{
37218             this.dd.resetConstraints();
37219             this.dd.setXConstraint(0, 0);
37220             this.dd.setYConstraint(
37221                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37222                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37223             );
37224          }
37225         this.dragSpecs.startSize = size;
37226         this.dragSpecs.startPoint = [x, y];
37227         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37228     },
37229     
37230     /** 
37231      * @private Called after the drag operation by the DDProxy
37232      */
37233     onEndProxyDrag : function(e){
37234         Roo.get(this.proxy).setDisplayed(false);
37235         var endPoint = Roo.lib.Event.getXY(e);
37236         if(this.overlay){
37237             this.overlay.hide();
37238         }
37239         var newSize;
37240         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37241             newSize = this.dragSpecs.startSize + 
37242                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37243                     endPoint[0] - this.dragSpecs.startPoint[0] :
37244                     this.dragSpecs.startPoint[0] - endPoint[0]
37245                 );
37246         }else{
37247             newSize = this.dragSpecs.startSize + 
37248                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37249                     endPoint[1] - this.dragSpecs.startPoint[1] :
37250                     this.dragSpecs.startPoint[1] - endPoint[1]
37251                 );
37252         }
37253         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37254         if(newSize != this.dragSpecs.startSize){
37255             if(this.fireEvent('beforeapply', this, newSize) !== false){
37256                 this.adapter.setElementSize(this, newSize);
37257                 this.fireEvent("moved", this, newSize);
37258                 this.fireEvent("resize", this, newSize);
37259             }
37260         }
37261     },
37262     
37263     /**
37264      * Get the adapter this SplitBar uses
37265      * @return The adapter object
37266      */
37267     getAdapter : function(){
37268         return this.adapter;
37269     },
37270     
37271     /**
37272      * Set the adapter this SplitBar uses
37273      * @param {Object} adapter A SplitBar adapter object
37274      */
37275     setAdapter : function(adapter){
37276         this.adapter = adapter;
37277         this.adapter.init(this);
37278     },
37279     
37280     /**
37281      * Gets the minimum size for the resizing element
37282      * @return {Number} The minimum size
37283      */
37284     getMinimumSize : function(){
37285         return this.minSize;
37286     },
37287     
37288     /**
37289      * Sets the minimum size for the resizing element
37290      * @param {Number} minSize The minimum size
37291      */
37292     setMinimumSize : function(minSize){
37293         this.minSize = minSize;
37294     },
37295     
37296     /**
37297      * Gets the maximum size for the resizing element
37298      * @return {Number} The maximum size
37299      */
37300     getMaximumSize : function(){
37301         return this.maxSize;
37302     },
37303     
37304     /**
37305      * Sets the maximum size for the resizing element
37306      * @param {Number} maxSize The maximum size
37307      */
37308     setMaximumSize : function(maxSize){
37309         this.maxSize = maxSize;
37310     },
37311     
37312     /**
37313      * Sets the initialize size for the resizing element
37314      * @param {Number} size The initial size
37315      */
37316     setCurrentSize : function(size){
37317         var oldAnimate = this.animate;
37318         this.animate = false;
37319         this.adapter.setElementSize(this, size);
37320         this.animate = oldAnimate;
37321     },
37322     
37323     /**
37324      * Destroy this splitbar. 
37325      * @param {Boolean} removeEl True to remove the element
37326      */
37327     destroy : function(removeEl){
37328         if(this.shim){
37329             this.shim.remove();
37330         }
37331         this.dd.unreg();
37332         this.proxy.parentNode.removeChild(this.proxy);
37333         if(removeEl){
37334             this.el.remove();
37335         }
37336     }
37337 });
37338
37339 /**
37340  * @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.
37341  */
37342 Roo.bootstrap.SplitBar.createProxy = function(dir){
37343     var proxy = new Roo.Element(document.createElement("div"));
37344     proxy.unselectable();
37345     var cls = 'roo-splitbar-proxy';
37346     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37347     document.body.appendChild(proxy.dom);
37348     return proxy.dom;
37349 };
37350
37351 /** 
37352  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37353  * Default Adapter. It assumes the splitter and resizing element are not positioned
37354  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37355  */
37356 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37357 };
37358
37359 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37360     // do nothing for now
37361     init : function(s){
37362     
37363     },
37364     /**
37365      * Called before drag operations to get the current size of the resizing element. 
37366      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37367      */
37368      getElementSize : function(s){
37369         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37370             return s.resizingEl.getWidth();
37371         }else{
37372             return s.resizingEl.getHeight();
37373         }
37374     },
37375     
37376     /**
37377      * Called after drag operations to set the size of the resizing element.
37378      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37379      * @param {Number} newSize The new size to set
37380      * @param {Function} onComplete A function to be invoked when resizing is complete
37381      */
37382     setElementSize : function(s, newSize, onComplete){
37383         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37384             if(!s.animate){
37385                 s.resizingEl.setWidth(newSize);
37386                 if(onComplete){
37387                     onComplete(s, newSize);
37388                 }
37389             }else{
37390                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37391             }
37392         }else{
37393             
37394             if(!s.animate){
37395                 s.resizingEl.setHeight(newSize);
37396                 if(onComplete){
37397                     onComplete(s, newSize);
37398                 }
37399             }else{
37400                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37401             }
37402         }
37403     }
37404 };
37405
37406 /** 
37407  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37408  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37409  * Adapter that  moves the splitter element to align with the resized sizing element. 
37410  * Used with an absolute positioned SplitBar.
37411  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37412  * document.body, make sure you assign an id to the body element.
37413  */
37414 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37415     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37416     this.container = Roo.get(container);
37417 };
37418
37419 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37420     init : function(s){
37421         this.basic.init(s);
37422     },
37423     
37424     getElementSize : function(s){
37425         return this.basic.getElementSize(s);
37426     },
37427     
37428     setElementSize : function(s, newSize, onComplete){
37429         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37430     },
37431     
37432     moveSplitter : function(s){
37433         var yes = Roo.bootstrap.SplitBar;
37434         switch(s.placement){
37435             case yes.LEFT:
37436                 s.el.setX(s.resizingEl.getRight());
37437                 break;
37438             case yes.RIGHT:
37439                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37440                 break;
37441             case yes.TOP:
37442                 s.el.setY(s.resizingEl.getBottom());
37443                 break;
37444             case yes.BOTTOM:
37445                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37446                 break;
37447         }
37448     }
37449 };
37450
37451 /**
37452  * Orientation constant - Create a vertical SplitBar
37453  * @static
37454  * @type Number
37455  */
37456 Roo.bootstrap.SplitBar.VERTICAL = 1;
37457
37458 /**
37459  * Orientation constant - Create a horizontal SplitBar
37460  * @static
37461  * @type Number
37462  */
37463 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37464
37465 /**
37466  * Placement constant - The resizing element is to the left of the splitter element
37467  * @static
37468  * @type Number
37469  */
37470 Roo.bootstrap.SplitBar.LEFT = 1;
37471
37472 /**
37473  * Placement constant - The resizing element is to the right of the splitter element
37474  * @static
37475  * @type Number
37476  */
37477 Roo.bootstrap.SplitBar.RIGHT = 2;
37478
37479 /**
37480  * Placement constant - The resizing element is positioned above the splitter element
37481  * @static
37482  * @type Number
37483  */
37484 Roo.bootstrap.SplitBar.TOP = 3;
37485
37486 /**
37487  * Placement constant - The resizing element is positioned under splitter element
37488  * @static
37489  * @type Number
37490  */
37491 Roo.bootstrap.SplitBar.BOTTOM = 4;
37492 Roo.namespace("Roo.bootstrap.layout");/*
37493  * Based on:
37494  * Ext JS Library 1.1.1
37495  * Copyright(c) 2006-2007, Ext JS, LLC.
37496  *
37497  * Originally Released Under LGPL - original licence link has changed is not relivant.
37498  *
37499  * Fork - LGPL
37500  * <script type="text/javascript">
37501  */
37502
37503 /**
37504  * @class Roo.bootstrap.layout.Manager
37505  * @extends Roo.bootstrap.Component
37506  * Base class for layout managers.
37507  */
37508 Roo.bootstrap.layout.Manager = function(config)
37509 {
37510     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37511
37512
37513
37514
37515
37516     /** false to disable window resize monitoring @type Boolean */
37517     this.monitorWindowResize = true;
37518     this.regions = {};
37519     this.addEvents({
37520         /**
37521          * @event layout
37522          * Fires when a layout is performed.
37523          * @param {Roo.LayoutManager} this
37524          */
37525         "layout" : true,
37526         /**
37527          * @event regionresized
37528          * Fires when the user resizes a region.
37529          * @param {Roo.LayoutRegion} region The resized region
37530          * @param {Number} newSize The new size (width for east/west, height for north/south)
37531          */
37532         "regionresized" : true,
37533         /**
37534          * @event regioncollapsed
37535          * Fires when a region is collapsed.
37536          * @param {Roo.LayoutRegion} region The collapsed region
37537          */
37538         "regioncollapsed" : true,
37539         /**
37540          * @event regionexpanded
37541          * Fires when a region is expanded.
37542          * @param {Roo.LayoutRegion} region The expanded region
37543          */
37544         "regionexpanded" : true
37545     });
37546     this.updating = false;
37547
37548     if (config.el) {
37549         this.el = Roo.get(config.el);
37550         this.initEvents();
37551     }
37552
37553 };
37554
37555 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37556
37557
37558     regions : null,
37559
37560     monitorWindowResize : true,
37561
37562
37563     updating : false,
37564
37565
37566     onRender : function(ct, position)
37567     {
37568         if(!this.el){
37569             this.el = Roo.get(ct);
37570             this.initEvents();
37571         }
37572         //this.fireEvent('render',this);
37573     },
37574
37575
37576     initEvents: function()
37577     {
37578
37579
37580         // ie scrollbar fix
37581         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37582             document.body.scroll = "no";
37583         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37584             this.el.position('relative');
37585         }
37586         this.id = this.el.id;
37587         this.el.addClass("roo-layout-container");
37588         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37589         if(this.el.dom != document.body ) {
37590             this.el.on('resize', this.layout,this);
37591             this.el.on('show', this.layout,this);
37592         }
37593
37594     },
37595
37596     /**
37597      * Returns true if this layout is currently being updated
37598      * @return {Boolean}
37599      */
37600     isUpdating : function(){
37601         return this.updating;
37602     },
37603
37604     /**
37605      * Suspend the LayoutManager from doing auto-layouts while
37606      * making multiple add or remove calls
37607      */
37608     beginUpdate : function(){
37609         this.updating = true;
37610     },
37611
37612     /**
37613      * Restore auto-layouts and optionally disable the manager from performing a layout
37614      * @param {Boolean} noLayout true to disable a layout update
37615      */
37616     endUpdate : function(noLayout){
37617         this.updating = false;
37618         if(!noLayout){
37619             this.layout();
37620         }
37621     },
37622
37623     layout: function(){
37624         // abstract...
37625     },
37626
37627     onRegionResized : function(region, newSize){
37628         this.fireEvent("regionresized", region, newSize);
37629         this.layout();
37630     },
37631
37632     onRegionCollapsed : function(region){
37633         this.fireEvent("regioncollapsed", region);
37634     },
37635
37636     onRegionExpanded : function(region){
37637         this.fireEvent("regionexpanded", region);
37638     },
37639
37640     /**
37641      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37642      * performs box-model adjustments.
37643      * @return {Object} The size as an object {width: (the width), height: (the height)}
37644      */
37645     getViewSize : function()
37646     {
37647         var size;
37648         if(this.el.dom != document.body){
37649             size = this.el.getSize();
37650         }else{
37651             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37652         }
37653         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37654         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37655         return size;
37656     },
37657
37658     /**
37659      * Returns the Element this layout is bound to.
37660      * @return {Roo.Element}
37661      */
37662     getEl : function(){
37663         return this.el;
37664     },
37665
37666     /**
37667      * Returns the specified region.
37668      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37669      * @return {Roo.LayoutRegion}
37670      */
37671     getRegion : function(target){
37672         return this.regions[target.toLowerCase()];
37673     },
37674
37675     onWindowResize : function(){
37676         if(this.monitorWindowResize){
37677             this.layout();
37678         }
37679     }
37680 });
37681 /*
37682  * Based on:
37683  * Ext JS Library 1.1.1
37684  * Copyright(c) 2006-2007, Ext JS, LLC.
37685  *
37686  * Originally Released Under LGPL - original licence link has changed is not relivant.
37687  *
37688  * Fork - LGPL
37689  * <script type="text/javascript">
37690  */
37691 /**
37692  * @class Roo.bootstrap.layout.Border
37693  * @extends Roo.bootstrap.layout.Manager
37694  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37695  * please see: examples/bootstrap/nested.html<br><br>
37696  
37697 <b>The container the layout is rendered into can be either the body element or any other element.
37698 If it is not the body element, the container needs to either be an absolute positioned element,
37699 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37700 the container size if it is not the body element.</b>
37701
37702 * @constructor
37703 * Create a new Border
37704 * @param {Object} config Configuration options
37705  */
37706 Roo.bootstrap.layout.Border = function(config){
37707     config = config || {};
37708     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37709     
37710     
37711     
37712     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37713         if(config[region]){
37714             config[region].region = region;
37715             this.addRegion(config[region]);
37716         }
37717     },this);
37718     
37719 };
37720
37721 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37722
37723 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37724     
37725     parent : false, // this might point to a 'nest' or a ???
37726     
37727     /**
37728      * Creates and adds a new region if it doesn't already exist.
37729      * @param {String} target The target region key (north, south, east, west or center).
37730      * @param {Object} config The regions config object
37731      * @return {BorderLayoutRegion} The new region
37732      */
37733     addRegion : function(config)
37734     {
37735         if(!this.regions[config.region]){
37736             var r = this.factory(config);
37737             this.bindRegion(r);
37738         }
37739         return this.regions[config.region];
37740     },
37741
37742     // private (kinda)
37743     bindRegion : function(r){
37744         this.regions[r.config.region] = r;
37745         
37746         r.on("visibilitychange",    this.layout, this);
37747         r.on("paneladded",          this.layout, this);
37748         r.on("panelremoved",        this.layout, this);
37749         r.on("invalidated",         this.layout, this);
37750         r.on("resized",             this.onRegionResized, this);
37751         r.on("collapsed",           this.onRegionCollapsed, this);
37752         r.on("expanded",            this.onRegionExpanded, this);
37753     },
37754
37755     /**
37756      * Performs a layout update.
37757      */
37758     layout : function()
37759     {
37760         if(this.updating) {
37761             return;
37762         }
37763         
37764         // render all the rebions if they have not been done alreayd?
37765         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37766             if(this.regions[region] && !this.regions[region].bodyEl){
37767                 this.regions[region].onRender(this.el)
37768             }
37769         },this);
37770         
37771         var size = this.getViewSize();
37772         var w = size.width;
37773         var h = size.height;
37774         var centerW = w;
37775         var centerH = h;
37776         var centerY = 0;
37777         var centerX = 0;
37778         //var x = 0, y = 0;
37779
37780         var rs = this.regions;
37781         var north = rs["north"];
37782         var south = rs["south"]; 
37783         var west = rs["west"];
37784         var east = rs["east"];
37785         var center = rs["center"];
37786         //if(this.hideOnLayout){ // not supported anymore
37787             //c.el.setStyle("display", "none");
37788         //}
37789         if(north && north.isVisible()){
37790             var b = north.getBox();
37791             var m = north.getMargins();
37792             b.width = w - (m.left+m.right);
37793             b.x = m.left;
37794             b.y = m.top;
37795             centerY = b.height + b.y + m.bottom;
37796             centerH -= centerY;
37797             north.updateBox(this.safeBox(b));
37798         }
37799         if(south && south.isVisible()){
37800             var b = south.getBox();
37801             var m = south.getMargins();
37802             b.width = w - (m.left+m.right);
37803             b.x = m.left;
37804             var totalHeight = (b.height + m.top + m.bottom);
37805             b.y = h - totalHeight + m.top;
37806             centerH -= totalHeight;
37807             south.updateBox(this.safeBox(b));
37808         }
37809         if(west && west.isVisible()){
37810             var b = west.getBox();
37811             var m = west.getMargins();
37812             b.height = centerH - (m.top+m.bottom);
37813             b.x = m.left;
37814             b.y = centerY + m.top;
37815             var totalWidth = (b.width + m.left + m.right);
37816             centerX += totalWidth;
37817             centerW -= totalWidth;
37818             west.updateBox(this.safeBox(b));
37819         }
37820         if(east && east.isVisible()){
37821             var b = east.getBox();
37822             var m = east.getMargins();
37823             b.height = centerH - (m.top+m.bottom);
37824             var totalWidth = (b.width + m.left + m.right);
37825             b.x = w - totalWidth + m.left;
37826             b.y = centerY + m.top;
37827             centerW -= totalWidth;
37828             east.updateBox(this.safeBox(b));
37829         }
37830         if(center){
37831             var m = center.getMargins();
37832             var centerBox = {
37833                 x: centerX + m.left,
37834                 y: centerY + m.top,
37835                 width: centerW - (m.left+m.right),
37836                 height: centerH - (m.top+m.bottom)
37837             };
37838             //if(this.hideOnLayout){
37839                 //center.el.setStyle("display", "block");
37840             //}
37841             center.updateBox(this.safeBox(centerBox));
37842         }
37843         this.el.repaint();
37844         this.fireEvent("layout", this);
37845     },
37846
37847     // private
37848     safeBox : function(box){
37849         box.width = Math.max(0, box.width);
37850         box.height = Math.max(0, box.height);
37851         return box;
37852     },
37853
37854     /**
37855      * Adds a ContentPanel (or subclass) to this layout.
37856      * @param {String} target The target region key (north, south, east, west or center).
37857      * @param {Roo.ContentPanel} panel The panel to add
37858      * @return {Roo.ContentPanel} The added panel
37859      */
37860     add : function(target, panel){
37861          
37862         target = target.toLowerCase();
37863         return this.regions[target].add(panel);
37864     },
37865
37866     /**
37867      * Remove a ContentPanel (or subclass) to this layout.
37868      * @param {String} target The target region key (north, south, east, west or center).
37869      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37870      * @return {Roo.ContentPanel} The removed panel
37871      */
37872     remove : function(target, panel){
37873         target = target.toLowerCase();
37874         return this.regions[target].remove(panel);
37875     },
37876
37877     /**
37878      * Searches all regions for a panel with the specified id
37879      * @param {String} panelId
37880      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37881      */
37882     findPanel : function(panelId){
37883         var rs = this.regions;
37884         for(var target in rs){
37885             if(typeof rs[target] != "function"){
37886                 var p = rs[target].getPanel(panelId);
37887                 if(p){
37888                     return p;
37889                 }
37890             }
37891         }
37892         return null;
37893     },
37894
37895     /**
37896      * Searches all regions for a panel with the specified id and activates (shows) it.
37897      * @param {String/ContentPanel} panelId The panels id or the panel itself
37898      * @return {Roo.ContentPanel} The shown panel or null
37899      */
37900     showPanel : function(panelId) {
37901       var rs = this.regions;
37902       for(var target in rs){
37903          var r = rs[target];
37904          if(typeof r != "function"){
37905             if(r.hasPanel(panelId)){
37906                return r.showPanel(panelId);
37907             }
37908          }
37909       }
37910       return null;
37911    },
37912
37913    /**
37914      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37915      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37916      */
37917    /*
37918     restoreState : function(provider){
37919         if(!provider){
37920             provider = Roo.state.Manager;
37921         }
37922         var sm = new Roo.LayoutStateManager();
37923         sm.init(this, provider);
37924     },
37925 */
37926  
37927  
37928     /**
37929      * Adds a xtype elements to the layout.
37930      * <pre><code>
37931
37932 layout.addxtype({
37933        xtype : 'ContentPanel',
37934        region: 'west',
37935        items: [ .... ]
37936    }
37937 );
37938
37939 layout.addxtype({
37940         xtype : 'NestedLayoutPanel',
37941         region: 'west',
37942         layout: {
37943            center: { },
37944            west: { }   
37945         },
37946         items : [ ... list of content panels or nested layout panels.. ]
37947    }
37948 );
37949 </code></pre>
37950      * @param {Object} cfg Xtype definition of item to add.
37951      */
37952     addxtype : function(cfg)
37953     {
37954         // basically accepts a pannel...
37955         // can accept a layout region..!?!?
37956         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37957         
37958         
37959         // theory?  children can only be panels??
37960         
37961         //if (!cfg.xtype.match(/Panel$/)) {
37962         //    return false;
37963         //}
37964         var ret = false;
37965         
37966         if (typeof(cfg.region) == 'undefined') {
37967             Roo.log("Failed to add Panel, region was not set");
37968             Roo.log(cfg);
37969             return false;
37970         }
37971         var region = cfg.region;
37972         delete cfg.region;
37973         
37974           
37975         var xitems = [];
37976         if (cfg.items) {
37977             xitems = cfg.items;
37978             delete cfg.items;
37979         }
37980         var nb = false;
37981         
37982         if ( region == 'center') {
37983             Roo.log("Center: " + cfg.title);
37984         }
37985         
37986         
37987         switch(cfg.xtype) 
37988         {
37989             case 'Content':  // ContentPanel (el, cfg)
37990             case 'Scroll':  // ContentPanel (el, cfg)
37991             case 'View': 
37992                 cfg.autoCreate = cfg.autoCreate || true;
37993                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37994                 //} else {
37995                 //    var el = this.el.createChild();
37996                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37997                 //}
37998                 
37999                 this.add(region, ret);
38000                 break;
38001             
38002             /*
38003             case 'TreePanel': // our new panel!
38004                 cfg.el = this.el.createChild();
38005                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38006                 this.add(region, ret);
38007                 break;
38008             */
38009             
38010             case 'Nest': 
38011                 // create a new Layout (which is  a Border Layout...
38012                 
38013                 var clayout = cfg.layout;
38014                 clayout.el  = this.el.createChild();
38015                 clayout.items   = clayout.items  || [];
38016                 
38017                 delete cfg.layout;
38018                 
38019                 // replace this exitems with the clayout ones..
38020                 xitems = clayout.items;
38021                  
38022                 // force background off if it's in center...
38023                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38024                     cfg.background = false;
38025                 }
38026                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38027                 
38028                 
38029                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38030                 //console.log('adding nested layout panel '  + cfg.toSource());
38031                 this.add(region, ret);
38032                 nb = {}; /// find first...
38033                 break;
38034             
38035             case 'Grid':
38036                 
38037                 // needs grid and region
38038                 
38039                 //var el = this.getRegion(region).el.createChild();
38040                 /*
38041                  *var el = this.el.createChild();
38042                 // create the grid first...
38043                 cfg.grid.container = el;
38044                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38045                 */
38046                 
38047                 if (region == 'center' && this.active ) {
38048                     cfg.background = false;
38049                 }
38050                 
38051                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38052                 
38053                 this.add(region, ret);
38054                 /*
38055                 if (cfg.background) {
38056                     // render grid on panel activation (if panel background)
38057                     ret.on('activate', function(gp) {
38058                         if (!gp.grid.rendered) {
38059                     //        gp.grid.render(el);
38060                         }
38061                     });
38062                 } else {
38063                   //  cfg.grid.render(el);
38064                 }
38065                 */
38066                 break;
38067            
38068            
38069             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38070                 // it was the old xcomponent building that caused this before.
38071                 // espeically if border is the top element in the tree.
38072                 ret = this;
38073                 break; 
38074                 
38075                     
38076                 
38077                 
38078                 
38079             default:
38080                 /*
38081                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38082                     
38083                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38084                     this.add(region, ret);
38085                 } else {
38086                 */
38087                     Roo.log(cfg);
38088                     throw "Can not add '" + cfg.xtype + "' to Border";
38089                     return null;
38090              
38091                                 
38092              
38093         }
38094         this.beginUpdate();
38095         // add children..
38096         var region = '';
38097         var abn = {};
38098         Roo.each(xitems, function(i)  {
38099             region = nb && i.region ? i.region : false;
38100             
38101             var add = ret.addxtype(i);
38102            
38103             if (region) {
38104                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38105                 if (!i.background) {
38106                     abn[region] = nb[region] ;
38107                 }
38108             }
38109             
38110         });
38111         this.endUpdate();
38112
38113         // make the last non-background panel active..
38114         //if (nb) { Roo.log(abn); }
38115         if (nb) {
38116             
38117             for(var r in abn) {
38118                 region = this.getRegion(r);
38119                 if (region) {
38120                     // tried using nb[r], but it does not work..
38121                      
38122                     region.showPanel(abn[r]);
38123                    
38124                 }
38125             }
38126         }
38127         return ret;
38128         
38129     },
38130     
38131     
38132 // private
38133     factory : function(cfg)
38134     {
38135         
38136         var validRegions = Roo.bootstrap.layout.Border.regions;
38137
38138         var target = cfg.region;
38139         cfg.mgr = this;
38140         
38141         var r = Roo.bootstrap.layout;
38142         Roo.log(target);
38143         switch(target){
38144             case "north":
38145                 return new r.North(cfg);
38146             case "south":
38147                 return new r.South(cfg);
38148             case "east":
38149                 return new r.East(cfg);
38150             case "west":
38151                 return new r.West(cfg);
38152             case "center":
38153                 return new r.Center(cfg);
38154         }
38155         throw 'Layout region "'+target+'" not supported.';
38156     }
38157     
38158     
38159 });
38160  /*
38161  * Based on:
38162  * Ext JS Library 1.1.1
38163  * Copyright(c) 2006-2007, Ext JS, LLC.
38164  *
38165  * Originally Released Under LGPL - original licence link has changed is not relivant.
38166  *
38167  * Fork - LGPL
38168  * <script type="text/javascript">
38169  */
38170  
38171 /**
38172  * @class Roo.bootstrap.layout.Basic
38173  * @extends Roo.util.Observable
38174  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38175  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38176  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38177  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38178  * @cfg {string}   region  the region that it inhabits..
38179  * @cfg {bool}   skipConfig skip config?
38180  * 
38181
38182  */
38183 Roo.bootstrap.layout.Basic = function(config){
38184     
38185     this.mgr = config.mgr;
38186     
38187     this.position = config.region;
38188     
38189     var skipConfig = config.skipConfig;
38190     
38191     this.events = {
38192         /**
38193          * @scope Roo.BasicLayoutRegion
38194          */
38195         
38196         /**
38197          * @event beforeremove
38198          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38199          * @param {Roo.LayoutRegion} this
38200          * @param {Roo.ContentPanel} panel The panel
38201          * @param {Object} e The cancel event object
38202          */
38203         "beforeremove" : true,
38204         /**
38205          * @event invalidated
38206          * Fires when the layout for this region is changed.
38207          * @param {Roo.LayoutRegion} this
38208          */
38209         "invalidated" : true,
38210         /**
38211          * @event visibilitychange
38212          * Fires when this region is shown or hidden 
38213          * @param {Roo.LayoutRegion} this
38214          * @param {Boolean} visibility true or false
38215          */
38216         "visibilitychange" : true,
38217         /**
38218          * @event paneladded
38219          * Fires when a panel is added. 
38220          * @param {Roo.LayoutRegion} this
38221          * @param {Roo.ContentPanel} panel The panel
38222          */
38223         "paneladded" : true,
38224         /**
38225          * @event panelremoved
38226          * Fires when a panel is removed. 
38227          * @param {Roo.LayoutRegion} this
38228          * @param {Roo.ContentPanel} panel The panel
38229          */
38230         "panelremoved" : true,
38231         /**
38232          * @event beforecollapse
38233          * Fires when this region before collapse.
38234          * @param {Roo.LayoutRegion} this
38235          */
38236         "beforecollapse" : true,
38237         /**
38238          * @event collapsed
38239          * Fires when this region is collapsed.
38240          * @param {Roo.LayoutRegion} this
38241          */
38242         "collapsed" : true,
38243         /**
38244          * @event expanded
38245          * Fires when this region is expanded.
38246          * @param {Roo.LayoutRegion} this
38247          */
38248         "expanded" : true,
38249         /**
38250          * @event slideshow
38251          * Fires when this region is slid into view.
38252          * @param {Roo.LayoutRegion} this
38253          */
38254         "slideshow" : true,
38255         /**
38256          * @event slidehide
38257          * Fires when this region slides out of view. 
38258          * @param {Roo.LayoutRegion} this
38259          */
38260         "slidehide" : true,
38261         /**
38262          * @event panelactivated
38263          * Fires when a panel is activated. 
38264          * @param {Roo.LayoutRegion} this
38265          * @param {Roo.ContentPanel} panel The activated panel
38266          */
38267         "panelactivated" : true,
38268         /**
38269          * @event resized
38270          * Fires when the user resizes this region. 
38271          * @param {Roo.LayoutRegion} this
38272          * @param {Number} newSize The new size (width for east/west, height for north/south)
38273          */
38274         "resized" : true
38275     };
38276     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38277     this.panels = new Roo.util.MixedCollection();
38278     this.panels.getKey = this.getPanelId.createDelegate(this);
38279     this.box = null;
38280     this.activePanel = null;
38281     // ensure listeners are added...
38282     
38283     if (config.listeners || config.events) {
38284         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38285             listeners : config.listeners || {},
38286             events : config.events || {}
38287         });
38288     }
38289     
38290     if(skipConfig !== true){
38291         this.applyConfig(config);
38292     }
38293 };
38294
38295 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38296 {
38297     getPanelId : function(p){
38298         return p.getId();
38299     },
38300     
38301     applyConfig : function(config){
38302         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38303         this.config = config;
38304         
38305     },
38306     
38307     /**
38308      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38309      * the width, for horizontal (north, south) the height.
38310      * @param {Number} newSize The new width or height
38311      */
38312     resizeTo : function(newSize){
38313         var el = this.el ? this.el :
38314                  (this.activePanel ? this.activePanel.getEl() : null);
38315         if(el){
38316             switch(this.position){
38317                 case "east":
38318                 case "west":
38319                     el.setWidth(newSize);
38320                     this.fireEvent("resized", this, newSize);
38321                 break;
38322                 case "north":
38323                 case "south":
38324                     el.setHeight(newSize);
38325                     this.fireEvent("resized", this, newSize);
38326                 break;                
38327             }
38328         }
38329     },
38330     
38331     getBox : function(){
38332         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38333     },
38334     
38335     getMargins : function(){
38336         return this.margins;
38337     },
38338     
38339     updateBox : function(box){
38340         this.box = box;
38341         var el = this.activePanel.getEl();
38342         el.dom.style.left = box.x + "px";
38343         el.dom.style.top = box.y + "px";
38344         this.activePanel.setSize(box.width, box.height);
38345     },
38346     
38347     /**
38348      * Returns the container element for this region.
38349      * @return {Roo.Element}
38350      */
38351     getEl : function(){
38352         return this.activePanel;
38353     },
38354     
38355     /**
38356      * Returns true if this region is currently visible.
38357      * @return {Boolean}
38358      */
38359     isVisible : function(){
38360         return this.activePanel ? true : false;
38361     },
38362     
38363     setActivePanel : function(panel){
38364         panel = this.getPanel(panel);
38365         if(this.activePanel && this.activePanel != panel){
38366             this.activePanel.setActiveState(false);
38367             this.activePanel.getEl().setLeftTop(-10000,-10000);
38368         }
38369         this.activePanel = panel;
38370         panel.setActiveState(true);
38371         if(this.box){
38372             panel.setSize(this.box.width, this.box.height);
38373         }
38374         this.fireEvent("panelactivated", this, panel);
38375         this.fireEvent("invalidated");
38376     },
38377     
38378     /**
38379      * Show the specified panel.
38380      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38381      * @return {Roo.ContentPanel} The shown panel or null
38382      */
38383     showPanel : function(panel){
38384         panel = this.getPanel(panel);
38385         if(panel){
38386             this.setActivePanel(panel);
38387         }
38388         return panel;
38389     },
38390     
38391     /**
38392      * Get the active panel for this region.
38393      * @return {Roo.ContentPanel} The active panel or null
38394      */
38395     getActivePanel : function(){
38396         return this.activePanel;
38397     },
38398     
38399     /**
38400      * Add the passed ContentPanel(s)
38401      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38402      * @return {Roo.ContentPanel} The panel added (if only one was added)
38403      */
38404     add : function(panel){
38405         if(arguments.length > 1){
38406             for(var i = 0, len = arguments.length; i < len; i++) {
38407                 this.add(arguments[i]);
38408             }
38409             return null;
38410         }
38411         if(this.hasPanel(panel)){
38412             this.showPanel(panel);
38413             return panel;
38414         }
38415         var el = panel.getEl();
38416         if(el.dom.parentNode != this.mgr.el.dom){
38417             this.mgr.el.dom.appendChild(el.dom);
38418         }
38419         if(panel.setRegion){
38420             panel.setRegion(this);
38421         }
38422         this.panels.add(panel);
38423         el.setStyle("position", "absolute");
38424         if(!panel.background){
38425             this.setActivePanel(panel);
38426             if(this.config.initialSize && this.panels.getCount()==1){
38427                 this.resizeTo(this.config.initialSize);
38428             }
38429         }
38430         this.fireEvent("paneladded", this, panel);
38431         return panel;
38432     },
38433     
38434     /**
38435      * Returns true if the panel is in this region.
38436      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38437      * @return {Boolean}
38438      */
38439     hasPanel : function(panel){
38440         if(typeof panel == "object"){ // must be panel obj
38441             panel = panel.getId();
38442         }
38443         return this.getPanel(panel) ? true : false;
38444     },
38445     
38446     /**
38447      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38448      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38449      * @param {Boolean} preservePanel Overrides the config preservePanel option
38450      * @return {Roo.ContentPanel} The panel that was removed
38451      */
38452     remove : function(panel, preservePanel){
38453         panel = this.getPanel(panel);
38454         if(!panel){
38455             return null;
38456         }
38457         var e = {};
38458         this.fireEvent("beforeremove", this, panel, e);
38459         if(e.cancel === true){
38460             return null;
38461         }
38462         var panelId = panel.getId();
38463         this.panels.removeKey(panelId);
38464         return panel;
38465     },
38466     
38467     /**
38468      * Returns the panel specified or null if it's not in this region.
38469      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38470      * @return {Roo.ContentPanel}
38471      */
38472     getPanel : function(id){
38473         if(typeof id == "object"){ // must be panel obj
38474             return id;
38475         }
38476         return this.panels.get(id);
38477     },
38478     
38479     /**
38480      * Returns this regions position (north/south/east/west/center).
38481      * @return {String} 
38482      */
38483     getPosition: function(){
38484         return this.position;    
38485     }
38486 });/*
38487  * Based on:
38488  * Ext JS Library 1.1.1
38489  * Copyright(c) 2006-2007, Ext JS, LLC.
38490  *
38491  * Originally Released Under LGPL - original licence link has changed is not relivant.
38492  *
38493  * Fork - LGPL
38494  * <script type="text/javascript">
38495  */
38496  
38497 /**
38498  * @class Roo.bootstrap.layout.Region
38499  * @extends Roo.bootstrap.layout.Basic
38500  * This class represents a region in a layout manager.
38501  
38502  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38503  * @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})
38504  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38505  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38506  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38507  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38508  * @cfg {String}    title           The title for the region (overrides panel titles)
38509  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38510  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38511  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38512  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38513  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38514  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38515  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38516  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38517  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38518  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38519
38520  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38521  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38522  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38523  * @cfg {Number}    width           For East/West panels
38524  * @cfg {Number}    height          For North/South panels
38525  * @cfg {Boolean}   split           To show the splitter
38526  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38527  * 
38528  * @cfg {string}   cls             Extra CSS classes to add to region
38529  * 
38530  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38531  * @cfg {string}   region  the region that it inhabits..
38532  *
38533
38534  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38535  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38536
38537  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38538  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38539  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38540  */
38541 Roo.bootstrap.layout.Region = function(config)
38542 {
38543     this.applyConfig(config);
38544
38545     var mgr = config.mgr;
38546     var pos = config.region;
38547     config.skipConfig = true;
38548     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38549     
38550     if (mgr.el) {
38551         this.onRender(mgr.el);   
38552     }
38553      
38554     this.visible = true;
38555     this.collapsed = false;
38556     this.unrendered_panels = [];
38557 };
38558
38559 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38560
38561     position: '', // set by wrapper (eg. north/south etc..)
38562     unrendered_panels : null,  // unrendered panels.
38563     
38564     tabPosition : false,
38565     
38566     mgr: false, // points to 'Border'
38567     
38568     
38569     createBody : function(){
38570         /** This region's body element 
38571         * @type Roo.Element */
38572         this.bodyEl = this.el.createChild({
38573                 tag: "div",
38574                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38575         });
38576     },
38577
38578     onRender: function(ctr, pos)
38579     {
38580         var dh = Roo.DomHelper;
38581         /** This region's container element 
38582         * @type Roo.Element */
38583         this.el = dh.append(ctr.dom, {
38584                 tag: "div",
38585                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38586             }, true);
38587         /** This region's title element 
38588         * @type Roo.Element */
38589     
38590         this.titleEl = dh.append(this.el.dom,  {
38591                 tag: "div",
38592                 unselectable: "on",
38593                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38594                 children:[
38595                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38596                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38597                 ]
38598             }, true);
38599         
38600         this.titleEl.enableDisplayMode();
38601         /** This region's title text element 
38602         * @type HTMLElement */
38603         this.titleTextEl = this.titleEl.dom.firstChild;
38604         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38605         /*
38606         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38607         this.closeBtn.enableDisplayMode();
38608         this.closeBtn.on("click", this.closeClicked, this);
38609         this.closeBtn.hide();
38610     */
38611         this.createBody(this.config);
38612         if(this.config.hideWhenEmpty){
38613             this.hide();
38614             this.on("paneladded", this.validateVisibility, this);
38615             this.on("panelremoved", this.validateVisibility, this);
38616         }
38617         if(this.autoScroll){
38618             this.bodyEl.setStyle("overflow", "auto");
38619         }else{
38620             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38621         }
38622         //if(c.titlebar !== false){
38623             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38624                 this.titleEl.hide();
38625             }else{
38626                 this.titleEl.show();
38627                 if(this.config.title){
38628                     this.titleTextEl.innerHTML = this.config.title;
38629                 }
38630             }
38631         //}
38632         if(this.config.collapsed){
38633             this.collapse(true);
38634         }
38635         if(this.config.hidden){
38636             this.hide();
38637         }
38638         
38639         if (this.unrendered_panels && this.unrendered_panels.length) {
38640             for (var i =0;i< this.unrendered_panels.length; i++) {
38641                 this.add(this.unrendered_panels[i]);
38642             }
38643             this.unrendered_panels = null;
38644             
38645         }
38646         
38647     },
38648     
38649     applyConfig : function(c)
38650     {
38651         /*
38652          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38653             var dh = Roo.DomHelper;
38654             if(c.titlebar !== false){
38655                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38656                 this.collapseBtn.on("click", this.collapse, this);
38657                 this.collapseBtn.enableDisplayMode();
38658                 /*
38659                 if(c.showPin === true || this.showPin){
38660                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38661                     this.stickBtn.enableDisplayMode();
38662                     this.stickBtn.on("click", this.expand, this);
38663                     this.stickBtn.hide();
38664                 }
38665                 
38666             }
38667             */
38668             /** This region's collapsed element
38669             * @type Roo.Element */
38670             /*
38671              *
38672             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38673                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38674             ]}, true);
38675             
38676             if(c.floatable !== false){
38677                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38678                this.collapsedEl.on("click", this.collapseClick, this);
38679             }
38680
38681             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38682                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38683                    id: "message", unselectable: "on", style:{"float":"left"}});
38684                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38685              }
38686             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38687             this.expandBtn.on("click", this.expand, this);
38688             
38689         }
38690         
38691         if(this.collapseBtn){
38692             this.collapseBtn.setVisible(c.collapsible == true);
38693         }
38694         
38695         this.cmargins = c.cmargins || this.cmargins ||
38696                          (this.position == "west" || this.position == "east" ?
38697                              {top: 0, left: 2, right:2, bottom: 0} :
38698                              {top: 2, left: 0, right:0, bottom: 2});
38699         */
38700         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38701         
38702         
38703         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38704         
38705         this.autoScroll = c.autoScroll || false;
38706         
38707         
38708        
38709         
38710         this.duration = c.duration || .30;
38711         this.slideDuration = c.slideDuration || .45;
38712         this.config = c;
38713        
38714     },
38715     /**
38716      * Returns true if this region is currently visible.
38717      * @return {Boolean}
38718      */
38719     isVisible : function(){
38720         return this.visible;
38721     },
38722
38723     /**
38724      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38725      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38726      */
38727     //setCollapsedTitle : function(title){
38728     //    title = title || "&#160;";
38729      //   if(this.collapsedTitleTextEl){
38730       //      this.collapsedTitleTextEl.innerHTML = title;
38731        // }
38732     //},
38733
38734     getBox : function(){
38735         var b;
38736       //  if(!this.collapsed){
38737             b = this.el.getBox(false, true);
38738        // }else{
38739           //  b = this.collapsedEl.getBox(false, true);
38740         //}
38741         return b;
38742     },
38743
38744     getMargins : function(){
38745         return this.margins;
38746         //return this.collapsed ? this.cmargins : this.margins;
38747     },
38748 /*
38749     highlight : function(){
38750         this.el.addClass("x-layout-panel-dragover");
38751     },
38752
38753     unhighlight : function(){
38754         this.el.removeClass("x-layout-panel-dragover");
38755     },
38756 */
38757     updateBox : function(box)
38758     {
38759         if (!this.bodyEl) {
38760             return; // not rendered yet..
38761         }
38762         
38763         this.box = box;
38764         if(!this.collapsed){
38765             this.el.dom.style.left = box.x + "px";
38766             this.el.dom.style.top = box.y + "px";
38767             this.updateBody(box.width, box.height);
38768         }else{
38769             this.collapsedEl.dom.style.left = box.x + "px";
38770             this.collapsedEl.dom.style.top = box.y + "px";
38771             this.collapsedEl.setSize(box.width, box.height);
38772         }
38773         if(this.tabs){
38774             this.tabs.autoSizeTabs();
38775         }
38776     },
38777
38778     updateBody : function(w, h)
38779     {
38780         if(w !== null){
38781             this.el.setWidth(w);
38782             w -= this.el.getBorderWidth("rl");
38783             if(this.config.adjustments){
38784                 w += this.config.adjustments[0];
38785             }
38786         }
38787         if(h !== null && h > 0){
38788             this.el.setHeight(h);
38789             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38790             h -= this.el.getBorderWidth("tb");
38791             if(this.config.adjustments){
38792                 h += this.config.adjustments[1];
38793             }
38794             this.bodyEl.setHeight(h);
38795             if(this.tabs){
38796                 h = this.tabs.syncHeight(h);
38797             }
38798         }
38799         if(this.panelSize){
38800             w = w !== null ? w : this.panelSize.width;
38801             h = h !== null ? h : this.panelSize.height;
38802         }
38803         if(this.activePanel){
38804             var el = this.activePanel.getEl();
38805             w = w !== null ? w : el.getWidth();
38806             h = h !== null ? h : el.getHeight();
38807             this.panelSize = {width: w, height: h};
38808             this.activePanel.setSize(w, h);
38809         }
38810         if(Roo.isIE && this.tabs){
38811             this.tabs.el.repaint();
38812         }
38813     },
38814
38815     /**
38816      * Returns the container element for this region.
38817      * @return {Roo.Element}
38818      */
38819     getEl : function(){
38820         return this.el;
38821     },
38822
38823     /**
38824      * Hides this region.
38825      */
38826     hide : function(){
38827         //if(!this.collapsed){
38828             this.el.dom.style.left = "-2000px";
38829             this.el.hide();
38830         //}else{
38831          //   this.collapsedEl.dom.style.left = "-2000px";
38832          //   this.collapsedEl.hide();
38833        // }
38834         this.visible = false;
38835         this.fireEvent("visibilitychange", this, false);
38836     },
38837
38838     /**
38839      * Shows this region if it was previously hidden.
38840      */
38841     show : function(){
38842         //if(!this.collapsed){
38843             this.el.show();
38844         //}else{
38845         //    this.collapsedEl.show();
38846        // }
38847         this.visible = true;
38848         this.fireEvent("visibilitychange", this, true);
38849     },
38850 /*
38851     closeClicked : function(){
38852         if(this.activePanel){
38853             this.remove(this.activePanel);
38854         }
38855     },
38856
38857     collapseClick : function(e){
38858         if(this.isSlid){
38859            e.stopPropagation();
38860            this.slideIn();
38861         }else{
38862            e.stopPropagation();
38863            this.slideOut();
38864         }
38865     },
38866 */
38867     /**
38868      * Collapses this region.
38869      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38870      */
38871     /*
38872     collapse : function(skipAnim, skipCheck = false){
38873         if(this.collapsed) {
38874             return;
38875         }
38876         
38877         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38878             
38879             this.collapsed = true;
38880             if(this.split){
38881                 this.split.el.hide();
38882             }
38883             if(this.config.animate && skipAnim !== true){
38884                 this.fireEvent("invalidated", this);
38885                 this.animateCollapse();
38886             }else{
38887                 this.el.setLocation(-20000,-20000);
38888                 this.el.hide();
38889                 this.collapsedEl.show();
38890                 this.fireEvent("collapsed", this);
38891                 this.fireEvent("invalidated", this);
38892             }
38893         }
38894         
38895     },
38896 */
38897     animateCollapse : function(){
38898         // overridden
38899     },
38900
38901     /**
38902      * Expands this region if it was previously collapsed.
38903      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38904      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38905      */
38906     /*
38907     expand : function(e, skipAnim){
38908         if(e) {
38909             e.stopPropagation();
38910         }
38911         if(!this.collapsed || this.el.hasActiveFx()) {
38912             return;
38913         }
38914         if(this.isSlid){
38915             this.afterSlideIn();
38916             skipAnim = true;
38917         }
38918         this.collapsed = false;
38919         if(this.config.animate && skipAnim !== true){
38920             this.animateExpand();
38921         }else{
38922             this.el.show();
38923             if(this.split){
38924                 this.split.el.show();
38925             }
38926             this.collapsedEl.setLocation(-2000,-2000);
38927             this.collapsedEl.hide();
38928             this.fireEvent("invalidated", this);
38929             this.fireEvent("expanded", this);
38930         }
38931     },
38932 */
38933     animateExpand : function(){
38934         // overridden
38935     },
38936
38937     initTabs : function()
38938     {
38939         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38940         
38941         var ts = new Roo.bootstrap.panel.Tabs({
38942             el: this.bodyEl.dom,
38943             region : this,
38944             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38945             disableTooltips: this.config.disableTabTips,
38946             toolbar : this.config.toolbar
38947         });
38948         
38949         if(this.config.hideTabs){
38950             ts.stripWrap.setDisplayed(false);
38951         }
38952         this.tabs = ts;
38953         ts.resizeTabs = this.config.resizeTabs === true;
38954         ts.minTabWidth = this.config.minTabWidth || 40;
38955         ts.maxTabWidth = this.config.maxTabWidth || 250;
38956         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38957         ts.monitorResize = false;
38958         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38959         ts.bodyEl.addClass('roo-layout-tabs-body');
38960         this.panels.each(this.initPanelAsTab, this);
38961     },
38962
38963     initPanelAsTab : function(panel){
38964         var ti = this.tabs.addTab(
38965             panel.getEl().id,
38966             panel.getTitle(),
38967             null,
38968             this.config.closeOnTab && panel.isClosable(),
38969             panel.tpl
38970         );
38971         if(panel.tabTip !== undefined){
38972             ti.setTooltip(panel.tabTip);
38973         }
38974         ti.on("activate", function(){
38975               this.setActivePanel(panel);
38976         }, this);
38977         
38978         if(this.config.closeOnTab){
38979             ti.on("beforeclose", function(t, e){
38980                 e.cancel = true;
38981                 this.remove(panel);
38982             }, this);
38983         }
38984         
38985         panel.tabItem = ti;
38986         
38987         return ti;
38988     },
38989
38990     updatePanelTitle : function(panel, title)
38991     {
38992         if(this.activePanel == panel){
38993             this.updateTitle(title);
38994         }
38995         if(this.tabs){
38996             var ti = this.tabs.getTab(panel.getEl().id);
38997             ti.setText(title);
38998             if(panel.tabTip !== undefined){
38999                 ti.setTooltip(panel.tabTip);
39000             }
39001         }
39002     },
39003
39004     updateTitle : function(title){
39005         if(this.titleTextEl && !this.config.title){
39006             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39007         }
39008     },
39009
39010     setActivePanel : function(panel)
39011     {
39012         panel = this.getPanel(panel);
39013         if(this.activePanel && this.activePanel != panel){
39014             if(this.activePanel.setActiveState(false) === false){
39015                 return;
39016             }
39017         }
39018         this.activePanel = panel;
39019         panel.setActiveState(true);
39020         if(this.panelSize){
39021             panel.setSize(this.panelSize.width, this.panelSize.height);
39022         }
39023         if(this.closeBtn){
39024             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39025         }
39026         this.updateTitle(panel.getTitle());
39027         if(this.tabs){
39028             this.fireEvent("invalidated", this);
39029         }
39030         this.fireEvent("panelactivated", this, panel);
39031     },
39032
39033     /**
39034      * Shows the specified panel.
39035      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39036      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39037      */
39038     showPanel : function(panel)
39039     {
39040         panel = this.getPanel(panel);
39041         if(panel){
39042             if(this.tabs){
39043                 var tab = this.tabs.getTab(panel.getEl().id);
39044                 if(tab.isHidden()){
39045                     this.tabs.unhideTab(tab.id);
39046                 }
39047                 tab.activate();
39048             }else{
39049                 this.setActivePanel(panel);
39050             }
39051         }
39052         return panel;
39053     },
39054
39055     /**
39056      * Get the active panel for this region.
39057      * @return {Roo.ContentPanel} The active panel or null
39058      */
39059     getActivePanel : function(){
39060         return this.activePanel;
39061     },
39062
39063     validateVisibility : function(){
39064         if(this.panels.getCount() < 1){
39065             this.updateTitle("&#160;");
39066             this.closeBtn.hide();
39067             this.hide();
39068         }else{
39069             if(!this.isVisible()){
39070                 this.show();
39071             }
39072         }
39073     },
39074
39075     /**
39076      * Adds the passed ContentPanel(s) to this region.
39077      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39078      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39079      */
39080     add : function(panel)
39081     {
39082         if(arguments.length > 1){
39083             for(var i = 0, len = arguments.length; i < len; i++) {
39084                 this.add(arguments[i]);
39085             }
39086             return null;
39087         }
39088         
39089         // if we have not been rendered yet, then we can not really do much of this..
39090         if (!this.bodyEl) {
39091             this.unrendered_panels.push(panel);
39092             return panel;
39093         }
39094         
39095         
39096         
39097         
39098         if(this.hasPanel(panel)){
39099             this.showPanel(panel);
39100             return panel;
39101         }
39102         panel.setRegion(this);
39103         this.panels.add(panel);
39104        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39105             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39106             // and hide them... ???
39107             this.bodyEl.dom.appendChild(panel.getEl().dom);
39108             if(panel.background !== true){
39109                 this.setActivePanel(panel);
39110             }
39111             this.fireEvent("paneladded", this, panel);
39112             return panel;
39113         }
39114         */
39115         if(!this.tabs){
39116             this.initTabs();
39117         }else{
39118             this.initPanelAsTab(panel);
39119         }
39120         
39121         
39122         if(panel.background !== true){
39123             this.tabs.activate(panel.getEl().id);
39124         }
39125         this.fireEvent("paneladded", this, panel);
39126         return panel;
39127     },
39128
39129     /**
39130      * Hides the tab for the specified panel.
39131      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39132      */
39133     hidePanel : function(panel){
39134         if(this.tabs && (panel = this.getPanel(panel))){
39135             this.tabs.hideTab(panel.getEl().id);
39136         }
39137     },
39138
39139     /**
39140      * Unhides the tab for a previously hidden panel.
39141      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39142      */
39143     unhidePanel : function(panel){
39144         if(this.tabs && (panel = this.getPanel(panel))){
39145             this.tabs.unhideTab(panel.getEl().id);
39146         }
39147     },
39148
39149     clearPanels : function(){
39150         while(this.panels.getCount() > 0){
39151              this.remove(this.panels.first());
39152         }
39153     },
39154
39155     /**
39156      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39157      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39158      * @param {Boolean} preservePanel Overrides the config preservePanel option
39159      * @return {Roo.ContentPanel} The panel that was removed
39160      */
39161     remove : function(panel, preservePanel)
39162     {
39163         panel = this.getPanel(panel);
39164         if(!panel){
39165             return null;
39166         }
39167         var e = {};
39168         this.fireEvent("beforeremove", this, panel, e);
39169         if(e.cancel === true){
39170             return null;
39171         }
39172         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39173         var panelId = panel.getId();
39174         this.panels.removeKey(panelId);
39175         if(preservePanel){
39176             document.body.appendChild(panel.getEl().dom);
39177         }
39178         if(this.tabs){
39179             this.tabs.removeTab(panel.getEl().id);
39180         }else if (!preservePanel){
39181             this.bodyEl.dom.removeChild(panel.getEl().dom);
39182         }
39183         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39184             var p = this.panels.first();
39185             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39186             tempEl.appendChild(p.getEl().dom);
39187             this.bodyEl.update("");
39188             this.bodyEl.dom.appendChild(p.getEl().dom);
39189             tempEl = null;
39190             this.updateTitle(p.getTitle());
39191             this.tabs = null;
39192             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39193             this.setActivePanel(p);
39194         }
39195         panel.setRegion(null);
39196         if(this.activePanel == panel){
39197             this.activePanel = null;
39198         }
39199         if(this.config.autoDestroy !== false && preservePanel !== true){
39200             try{panel.destroy();}catch(e){}
39201         }
39202         this.fireEvent("panelremoved", this, panel);
39203         return panel;
39204     },
39205
39206     /**
39207      * Returns the TabPanel component used by this region
39208      * @return {Roo.TabPanel}
39209      */
39210     getTabs : function(){
39211         return this.tabs;
39212     },
39213
39214     createTool : function(parentEl, className){
39215         var btn = Roo.DomHelper.append(parentEl, {
39216             tag: "div",
39217             cls: "x-layout-tools-button",
39218             children: [ {
39219                 tag: "div",
39220                 cls: "roo-layout-tools-button-inner " + className,
39221                 html: "&#160;"
39222             }]
39223         }, true);
39224         btn.addClassOnOver("roo-layout-tools-button-over");
39225         return btn;
39226     }
39227 });/*
39228  * Based on:
39229  * Ext JS Library 1.1.1
39230  * Copyright(c) 2006-2007, Ext JS, LLC.
39231  *
39232  * Originally Released Under LGPL - original licence link has changed is not relivant.
39233  *
39234  * Fork - LGPL
39235  * <script type="text/javascript">
39236  */
39237  
39238
39239
39240 /**
39241  * @class Roo.SplitLayoutRegion
39242  * @extends Roo.LayoutRegion
39243  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39244  */
39245 Roo.bootstrap.layout.Split = function(config){
39246     this.cursor = config.cursor;
39247     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39248 };
39249
39250 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39251 {
39252     splitTip : "Drag to resize.",
39253     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39254     useSplitTips : false,
39255
39256     applyConfig : function(config){
39257         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39258     },
39259     
39260     onRender : function(ctr,pos) {
39261         
39262         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39263         if(!this.config.split){
39264             return;
39265         }
39266         if(!this.split){
39267             
39268             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39269                             tag: "div",
39270                             id: this.el.id + "-split",
39271                             cls: "roo-layout-split roo-layout-split-"+this.position,
39272                             html: "&#160;"
39273             });
39274             /** The SplitBar for this region 
39275             * @type Roo.SplitBar */
39276             // does not exist yet...
39277             Roo.log([this.position, this.orientation]);
39278             
39279             this.split = new Roo.bootstrap.SplitBar({
39280                 dragElement : splitEl,
39281                 resizingElement: this.el,
39282                 orientation : this.orientation
39283             });
39284             
39285             this.split.on("moved", this.onSplitMove, this);
39286             this.split.useShim = this.config.useShim === true;
39287             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39288             if(this.useSplitTips){
39289                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39290             }
39291             //if(config.collapsible){
39292             //    this.split.el.on("dblclick", this.collapse,  this);
39293             //}
39294         }
39295         if(typeof this.config.minSize != "undefined"){
39296             this.split.minSize = this.config.minSize;
39297         }
39298         if(typeof this.config.maxSize != "undefined"){
39299             this.split.maxSize = this.config.maxSize;
39300         }
39301         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39302             this.hideSplitter();
39303         }
39304         
39305     },
39306
39307     getHMaxSize : function(){
39308          var cmax = this.config.maxSize || 10000;
39309          var center = this.mgr.getRegion("center");
39310          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39311     },
39312
39313     getVMaxSize : function(){
39314          var cmax = this.config.maxSize || 10000;
39315          var center = this.mgr.getRegion("center");
39316          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39317     },
39318
39319     onSplitMove : function(split, newSize){
39320         this.fireEvent("resized", this, newSize);
39321     },
39322     
39323     /** 
39324      * Returns the {@link Roo.SplitBar} for this region.
39325      * @return {Roo.SplitBar}
39326      */
39327     getSplitBar : function(){
39328         return this.split;
39329     },
39330     
39331     hide : function(){
39332         this.hideSplitter();
39333         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39334     },
39335
39336     hideSplitter : function(){
39337         if(this.split){
39338             this.split.el.setLocation(-2000,-2000);
39339             this.split.el.hide();
39340         }
39341     },
39342
39343     show : function(){
39344         if(this.split){
39345             this.split.el.show();
39346         }
39347         Roo.bootstrap.layout.Split.superclass.show.call(this);
39348     },
39349     
39350     beforeSlide: function(){
39351         if(Roo.isGecko){// firefox overflow auto bug workaround
39352             this.bodyEl.clip();
39353             if(this.tabs) {
39354                 this.tabs.bodyEl.clip();
39355             }
39356             if(this.activePanel){
39357                 this.activePanel.getEl().clip();
39358                 
39359                 if(this.activePanel.beforeSlide){
39360                     this.activePanel.beforeSlide();
39361                 }
39362             }
39363         }
39364     },
39365     
39366     afterSlide : function(){
39367         if(Roo.isGecko){// firefox overflow auto bug workaround
39368             this.bodyEl.unclip();
39369             if(this.tabs) {
39370                 this.tabs.bodyEl.unclip();
39371             }
39372             if(this.activePanel){
39373                 this.activePanel.getEl().unclip();
39374                 if(this.activePanel.afterSlide){
39375                     this.activePanel.afterSlide();
39376                 }
39377             }
39378         }
39379     },
39380
39381     initAutoHide : function(){
39382         if(this.autoHide !== false){
39383             if(!this.autoHideHd){
39384                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39385                 this.autoHideHd = {
39386                     "mouseout": function(e){
39387                         if(!e.within(this.el, true)){
39388                             st.delay(500);
39389                         }
39390                     },
39391                     "mouseover" : function(e){
39392                         st.cancel();
39393                     },
39394                     scope : this
39395                 };
39396             }
39397             this.el.on(this.autoHideHd);
39398         }
39399     },
39400
39401     clearAutoHide : function(){
39402         if(this.autoHide !== false){
39403             this.el.un("mouseout", this.autoHideHd.mouseout);
39404             this.el.un("mouseover", this.autoHideHd.mouseover);
39405         }
39406     },
39407
39408     clearMonitor : function(){
39409         Roo.get(document).un("click", this.slideInIf, this);
39410     },
39411
39412     // these names are backwards but not changed for compat
39413     slideOut : function(){
39414         if(this.isSlid || this.el.hasActiveFx()){
39415             return;
39416         }
39417         this.isSlid = true;
39418         if(this.collapseBtn){
39419             this.collapseBtn.hide();
39420         }
39421         this.closeBtnState = this.closeBtn.getStyle('display');
39422         this.closeBtn.hide();
39423         if(this.stickBtn){
39424             this.stickBtn.show();
39425         }
39426         this.el.show();
39427         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39428         this.beforeSlide();
39429         this.el.setStyle("z-index", 10001);
39430         this.el.slideIn(this.getSlideAnchor(), {
39431             callback: function(){
39432                 this.afterSlide();
39433                 this.initAutoHide();
39434                 Roo.get(document).on("click", this.slideInIf, this);
39435                 this.fireEvent("slideshow", this);
39436             },
39437             scope: this,
39438             block: true
39439         });
39440     },
39441
39442     afterSlideIn : function(){
39443         this.clearAutoHide();
39444         this.isSlid = false;
39445         this.clearMonitor();
39446         this.el.setStyle("z-index", "");
39447         if(this.collapseBtn){
39448             this.collapseBtn.show();
39449         }
39450         this.closeBtn.setStyle('display', this.closeBtnState);
39451         if(this.stickBtn){
39452             this.stickBtn.hide();
39453         }
39454         this.fireEvent("slidehide", this);
39455     },
39456
39457     slideIn : function(cb){
39458         if(!this.isSlid || this.el.hasActiveFx()){
39459             Roo.callback(cb);
39460             return;
39461         }
39462         this.isSlid = false;
39463         this.beforeSlide();
39464         this.el.slideOut(this.getSlideAnchor(), {
39465             callback: function(){
39466                 this.el.setLeftTop(-10000, -10000);
39467                 this.afterSlide();
39468                 this.afterSlideIn();
39469                 Roo.callback(cb);
39470             },
39471             scope: this,
39472             block: true
39473         });
39474     },
39475     
39476     slideInIf : function(e){
39477         if(!e.within(this.el)){
39478             this.slideIn();
39479         }
39480     },
39481
39482     animateCollapse : function(){
39483         this.beforeSlide();
39484         this.el.setStyle("z-index", 20000);
39485         var anchor = this.getSlideAnchor();
39486         this.el.slideOut(anchor, {
39487             callback : function(){
39488                 this.el.setStyle("z-index", "");
39489                 this.collapsedEl.slideIn(anchor, {duration:.3});
39490                 this.afterSlide();
39491                 this.el.setLocation(-10000,-10000);
39492                 this.el.hide();
39493                 this.fireEvent("collapsed", this);
39494             },
39495             scope: this,
39496             block: true
39497         });
39498     },
39499
39500     animateExpand : function(){
39501         this.beforeSlide();
39502         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39503         this.el.setStyle("z-index", 20000);
39504         this.collapsedEl.hide({
39505             duration:.1
39506         });
39507         this.el.slideIn(this.getSlideAnchor(), {
39508             callback : function(){
39509                 this.el.setStyle("z-index", "");
39510                 this.afterSlide();
39511                 if(this.split){
39512                     this.split.el.show();
39513                 }
39514                 this.fireEvent("invalidated", this);
39515                 this.fireEvent("expanded", this);
39516             },
39517             scope: this,
39518             block: true
39519         });
39520     },
39521
39522     anchors : {
39523         "west" : "left",
39524         "east" : "right",
39525         "north" : "top",
39526         "south" : "bottom"
39527     },
39528
39529     sanchors : {
39530         "west" : "l",
39531         "east" : "r",
39532         "north" : "t",
39533         "south" : "b"
39534     },
39535
39536     canchors : {
39537         "west" : "tl-tr",
39538         "east" : "tr-tl",
39539         "north" : "tl-bl",
39540         "south" : "bl-tl"
39541     },
39542
39543     getAnchor : function(){
39544         return this.anchors[this.position];
39545     },
39546
39547     getCollapseAnchor : function(){
39548         return this.canchors[this.position];
39549     },
39550
39551     getSlideAnchor : function(){
39552         return this.sanchors[this.position];
39553     },
39554
39555     getAlignAdj : function(){
39556         var cm = this.cmargins;
39557         switch(this.position){
39558             case "west":
39559                 return [0, 0];
39560             break;
39561             case "east":
39562                 return [0, 0];
39563             break;
39564             case "north":
39565                 return [0, 0];
39566             break;
39567             case "south":
39568                 return [0, 0];
39569             break;
39570         }
39571     },
39572
39573     getExpandAdj : function(){
39574         var c = this.collapsedEl, cm = this.cmargins;
39575         switch(this.position){
39576             case "west":
39577                 return [-(cm.right+c.getWidth()+cm.left), 0];
39578             break;
39579             case "east":
39580                 return [cm.right+c.getWidth()+cm.left, 0];
39581             break;
39582             case "north":
39583                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39584             break;
39585             case "south":
39586                 return [0, cm.top+cm.bottom+c.getHeight()];
39587             break;
39588         }
39589     }
39590 });/*
39591  * Based on:
39592  * Ext JS Library 1.1.1
39593  * Copyright(c) 2006-2007, Ext JS, LLC.
39594  *
39595  * Originally Released Under LGPL - original licence link has changed is not relivant.
39596  *
39597  * Fork - LGPL
39598  * <script type="text/javascript">
39599  */
39600 /*
39601  * These classes are private internal classes
39602  */
39603 Roo.bootstrap.layout.Center = function(config){
39604     config.region = "center";
39605     Roo.bootstrap.layout.Region.call(this, config);
39606     this.visible = true;
39607     this.minWidth = config.minWidth || 20;
39608     this.minHeight = config.minHeight || 20;
39609 };
39610
39611 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39612     hide : function(){
39613         // center panel can't be hidden
39614     },
39615     
39616     show : function(){
39617         // center panel can't be hidden
39618     },
39619     
39620     getMinWidth: function(){
39621         return this.minWidth;
39622     },
39623     
39624     getMinHeight: function(){
39625         return this.minHeight;
39626     }
39627 });
39628
39629
39630
39631
39632  
39633
39634
39635
39636
39637
39638
39639 Roo.bootstrap.layout.North = function(config)
39640 {
39641     config.region = 'north';
39642     config.cursor = 'n-resize';
39643     
39644     Roo.bootstrap.layout.Split.call(this, config);
39645     
39646     
39647     if(this.split){
39648         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39649         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39650         this.split.el.addClass("roo-layout-split-v");
39651     }
39652     //var size = config.initialSize || config.height;
39653     //if(this.el && typeof size != "undefined"){
39654     //    this.el.setHeight(size);
39655     //}
39656 };
39657 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39658 {
39659     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39660      
39661      
39662     onRender : function(ctr, pos)
39663     {
39664         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39665         var size = this.config.initialSize || this.config.height;
39666         if(this.el && typeof size != "undefined"){
39667             this.el.setHeight(size);
39668         }
39669     
39670     },
39671     
39672     getBox : function(){
39673         if(this.collapsed){
39674             return this.collapsedEl.getBox();
39675         }
39676         var box = this.el.getBox();
39677         if(this.split){
39678             box.height += this.split.el.getHeight();
39679         }
39680         return box;
39681     },
39682     
39683     updateBox : function(box){
39684         if(this.split && !this.collapsed){
39685             box.height -= this.split.el.getHeight();
39686             this.split.el.setLeft(box.x);
39687             this.split.el.setTop(box.y+box.height);
39688             this.split.el.setWidth(box.width);
39689         }
39690         if(this.collapsed){
39691             this.updateBody(box.width, null);
39692         }
39693         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39694     }
39695 });
39696
39697
39698
39699
39700
39701 Roo.bootstrap.layout.South = function(config){
39702     config.region = 'south';
39703     config.cursor = 's-resize';
39704     Roo.bootstrap.layout.Split.call(this, config);
39705     if(this.split){
39706         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39707         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39708         this.split.el.addClass("roo-layout-split-v");
39709     }
39710     
39711 };
39712
39713 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39714     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39715     
39716     onRender : function(ctr, pos)
39717     {
39718         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39719         var size = this.config.initialSize || this.config.height;
39720         if(this.el && typeof size != "undefined"){
39721             this.el.setHeight(size);
39722         }
39723     
39724     },
39725     
39726     getBox : function(){
39727         if(this.collapsed){
39728             return this.collapsedEl.getBox();
39729         }
39730         var box = this.el.getBox();
39731         if(this.split){
39732             var sh = this.split.el.getHeight();
39733             box.height += sh;
39734             box.y -= sh;
39735         }
39736         return box;
39737     },
39738     
39739     updateBox : function(box){
39740         if(this.split && !this.collapsed){
39741             var sh = this.split.el.getHeight();
39742             box.height -= sh;
39743             box.y += sh;
39744             this.split.el.setLeft(box.x);
39745             this.split.el.setTop(box.y-sh);
39746             this.split.el.setWidth(box.width);
39747         }
39748         if(this.collapsed){
39749             this.updateBody(box.width, null);
39750         }
39751         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39752     }
39753 });
39754
39755 Roo.bootstrap.layout.East = function(config){
39756     config.region = "east";
39757     config.cursor = "e-resize";
39758     Roo.bootstrap.layout.Split.call(this, config);
39759     if(this.split){
39760         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39761         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39762         this.split.el.addClass("roo-layout-split-h");
39763     }
39764     
39765 };
39766 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39767     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39768     
39769     onRender : function(ctr, pos)
39770     {
39771         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39772         var size = this.config.initialSize || this.config.width;
39773         if(this.el && typeof size != "undefined"){
39774             this.el.setWidth(size);
39775         }
39776     
39777     },
39778     
39779     getBox : function(){
39780         if(this.collapsed){
39781             return this.collapsedEl.getBox();
39782         }
39783         var box = this.el.getBox();
39784         if(this.split){
39785             var sw = this.split.el.getWidth();
39786             box.width += sw;
39787             box.x -= sw;
39788         }
39789         return box;
39790     },
39791
39792     updateBox : function(box){
39793         if(this.split && !this.collapsed){
39794             var sw = this.split.el.getWidth();
39795             box.width -= sw;
39796             this.split.el.setLeft(box.x);
39797             this.split.el.setTop(box.y);
39798             this.split.el.setHeight(box.height);
39799             box.x += sw;
39800         }
39801         if(this.collapsed){
39802             this.updateBody(null, box.height);
39803         }
39804         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39805     }
39806 });
39807
39808 Roo.bootstrap.layout.West = function(config){
39809     config.region = "west";
39810     config.cursor = "w-resize";
39811     
39812     Roo.bootstrap.layout.Split.call(this, config);
39813     if(this.split){
39814         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39815         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39816         this.split.el.addClass("roo-layout-split-h");
39817     }
39818     
39819 };
39820 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39821     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39822     
39823     onRender: function(ctr, pos)
39824     {
39825         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39826         var size = this.config.initialSize || this.config.width;
39827         if(typeof size != "undefined"){
39828             this.el.setWidth(size);
39829         }
39830     },
39831     
39832     getBox : function(){
39833         if(this.collapsed){
39834             return this.collapsedEl.getBox();
39835         }
39836         var box = this.el.getBox();
39837         if (box.width == 0) {
39838             box.width = this.config.width; // kludge?
39839         }
39840         if(this.split){
39841             box.width += this.split.el.getWidth();
39842         }
39843         return box;
39844     },
39845     
39846     updateBox : function(box){
39847         if(this.split && !this.collapsed){
39848             var sw = this.split.el.getWidth();
39849             box.width -= sw;
39850             this.split.el.setLeft(box.x+box.width);
39851             this.split.el.setTop(box.y);
39852             this.split.el.setHeight(box.height);
39853         }
39854         if(this.collapsed){
39855             this.updateBody(null, box.height);
39856         }
39857         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39858     }
39859 });Roo.namespace("Roo.bootstrap.panel");/*
39860  * Based on:
39861  * Ext JS Library 1.1.1
39862  * Copyright(c) 2006-2007, Ext JS, LLC.
39863  *
39864  * Originally Released Under LGPL - original licence link has changed is not relivant.
39865  *
39866  * Fork - LGPL
39867  * <script type="text/javascript">
39868  */
39869 /**
39870  * @class Roo.ContentPanel
39871  * @extends Roo.util.Observable
39872  * A basic ContentPanel element.
39873  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39874  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39875  * @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
39876  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39877  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39878  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39879  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39880  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39881  * @cfg {String} title          The title for this panel
39882  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39883  * @cfg {String} url            Calls {@link #setUrl} with this value
39884  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39885  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39886  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39887  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39888  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39889  * @cfg {Boolean} badges render the badges
39890  * @cfg {String} cls  extra classes to use  
39891  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39892
39893  * @constructor
39894  * Create a new ContentPanel.
39895  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39896  * @param {String/Object} config A string to set only the title or a config object
39897  * @param {String} content (optional) Set the HTML content for this panel
39898  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39899  */
39900 Roo.bootstrap.panel.Content = function( config){
39901     
39902     this.tpl = config.tpl || false;
39903     
39904     var el = config.el;
39905     var content = config.content;
39906
39907     if(config.autoCreate){ // xtype is available if this is called from factory
39908         el = Roo.id();
39909     }
39910     this.el = Roo.get(el);
39911     if(!this.el && config && config.autoCreate){
39912         if(typeof config.autoCreate == "object"){
39913             if(!config.autoCreate.id){
39914                 config.autoCreate.id = config.id||el;
39915             }
39916             this.el = Roo.DomHelper.append(document.body,
39917                         config.autoCreate, true);
39918         }else{
39919             var elcfg =  {
39920                 tag: "div",
39921                 cls: (config.cls || '') +
39922                     (config.background ? ' bg-' + config.background : '') +
39923                     " roo-layout-inactive-content",
39924                 id: config.id||el
39925             };
39926             if (config.iframe) {
39927                 elcfg.cn = [
39928                     {
39929                         tag : 'iframe',
39930                         style : 'border: 0px',
39931                         src : 'about:blank'
39932                     }
39933                 ];
39934             }
39935               
39936             if (config.html) {
39937                 elcfg.html = config.html;
39938                 
39939             }
39940                         
39941             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39942             if (config.iframe) {
39943                 this.iframeEl = this.el.select('iframe',true).first();
39944             }
39945             
39946         }
39947     } 
39948     this.closable = false;
39949     this.loaded = false;
39950     this.active = false;
39951    
39952       
39953     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39954         
39955         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39956         
39957         this.wrapEl = this.el; //this.el.wrap();
39958         var ti = [];
39959         if (config.toolbar.items) {
39960             ti = config.toolbar.items ;
39961             delete config.toolbar.items ;
39962         }
39963         
39964         var nitems = [];
39965         this.toolbar.render(this.wrapEl, 'before');
39966         for(var i =0;i < ti.length;i++) {
39967           //  Roo.log(['add child', items[i]]);
39968             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39969         }
39970         this.toolbar.items = nitems;
39971         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39972         delete config.toolbar;
39973         
39974     }
39975     /*
39976     // xtype created footer. - not sure if will work as we normally have to render first..
39977     if (this.footer && !this.footer.el && this.footer.xtype) {
39978         if (!this.wrapEl) {
39979             this.wrapEl = this.el.wrap();
39980         }
39981     
39982         this.footer.container = this.wrapEl.createChild();
39983          
39984         this.footer = Roo.factory(this.footer, Roo);
39985         
39986     }
39987     */
39988     
39989      if(typeof config == "string"){
39990         this.title = config;
39991     }else{
39992         Roo.apply(this, config);
39993     }
39994     
39995     if(this.resizeEl){
39996         this.resizeEl = Roo.get(this.resizeEl, true);
39997     }else{
39998         this.resizeEl = this.el;
39999     }
40000     // handle view.xtype
40001     
40002  
40003     
40004     
40005     this.addEvents({
40006         /**
40007          * @event activate
40008          * Fires when this panel is activated. 
40009          * @param {Roo.ContentPanel} this
40010          */
40011         "activate" : true,
40012         /**
40013          * @event deactivate
40014          * Fires when this panel is activated. 
40015          * @param {Roo.ContentPanel} this
40016          */
40017         "deactivate" : true,
40018
40019         /**
40020          * @event resize
40021          * Fires when this panel is resized if fitToFrame is true.
40022          * @param {Roo.ContentPanel} this
40023          * @param {Number} width The width after any component adjustments
40024          * @param {Number} height The height after any component adjustments
40025          */
40026         "resize" : true,
40027         
40028          /**
40029          * @event render
40030          * Fires when this tab is created
40031          * @param {Roo.ContentPanel} this
40032          */
40033         "render" : true
40034         
40035         
40036         
40037     });
40038     
40039
40040     
40041     
40042     if(this.autoScroll && !this.iframe){
40043         this.resizeEl.setStyle("overflow", "auto");
40044     } else {
40045         // fix randome scrolling
40046         //this.el.on('scroll', function() {
40047         //    Roo.log('fix random scolling');
40048         //    this.scrollTo('top',0); 
40049         //});
40050     }
40051     content = content || this.content;
40052     if(content){
40053         this.setContent(content);
40054     }
40055     if(config && config.url){
40056         this.setUrl(this.url, this.params, this.loadOnce);
40057     }
40058     
40059     
40060     
40061     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40062     
40063     if (this.view && typeof(this.view.xtype) != 'undefined') {
40064         this.view.el = this.el.appendChild(document.createElement("div"));
40065         this.view = Roo.factory(this.view); 
40066         this.view.render  &&  this.view.render(false, '');  
40067     }
40068     
40069     
40070     this.fireEvent('render', this);
40071 };
40072
40073 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40074     
40075     cls : '',
40076     background : '',
40077     
40078     tabTip : '',
40079     
40080     iframe : false,
40081     iframeEl : false,
40082     
40083     setRegion : function(region){
40084         this.region = region;
40085         this.setActiveClass(region && !this.background);
40086     },
40087     
40088     
40089     setActiveClass: function(state)
40090     {
40091         if(state){
40092            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40093            this.el.setStyle('position','relative');
40094         }else{
40095            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40096            this.el.setStyle('position', 'absolute');
40097         } 
40098     },
40099     
40100     /**
40101      * Returns the toolbar for this Panel if one was configured. 
40102      * @return {Roo.Toolbar} 
40103      */
40104     getToolbar : function(){
40105         return this.toolbar;
40106     },
40107     
40108     setActiveState : function(active)
40109     {
40110         this.active = active;
40111         this.setActiveClass(active);
40112         if(!active){
40113             if(this.fireEvent("deactivate", this) === false){
40114                 return false;
40115             }
40116             return true;
40117         }
40118         this.fireEvent("activate", this);
40119         return true;
40120     },
40121     /**
40122      * Updates this panel's element (not for iframe)
40123      * @param {String} content The new content
40124      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40125     */
40126     setContent : function(content, loadScripts){
40127         if (this.iframe) {
40128             return;
40129         }
40130         
40131         this.el.update(content, loadScripts);
40132     },
40133
40134     ignoreResize : function(w, h){
40135         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40136             return true;
40137         }else{
40138             this.lastSize = {width: w, height: h};
40139             return false;
40140         }
40141     },
40142     /**
40143      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40144      * @return {Roo.UpdateManager} The UpdateManager
40145      */
40146     getUpdateManager : function(){
40147         if (this.iframe) {
40148             return false;
40149         }
40150         return this.el.getUpdateManager();
40151     },
40152      /**
40153      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40154      * Does not work with IFRAME contents
40155      * @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:
40156 <pre><code>
40157 panel.load({
40158     url: "your-url.php",
40159     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40160     callback: yourFunction,
40161     scope: yourObject, //(optional scope)
40162     discardUrl: false,
40163     nocache: false,
40164     text: "Loading...",
40165     timeout: 30,
40166     scripts: false
40167 });
40168 </code></pre>
40169      
40170      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40171      * 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.
40172      * @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}
40173      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40174      * @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.
40175      * @return {Roo.ContentPanel} this
40176      */
40177     load : function(){
40178         
40179         if (this.iframe) {
40180             return this;
40181         }
40182         
40183         var um = this.el.getUpdateManager();
40184         um.update.apply(um, arguments);
40185         return this;
40186     },
40187
40188
40189     /**
40190      * 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.
40191      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40192      * @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)
40193      * @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)
40194      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40195      */
40196     setUrl : function(url, params, loadOnce){
40197         if (this.iframe) {
40198             this.iframeEl.dom.src = url;
40199             return false;
40200         }
40201         
40202         if(this.refreshDelegate){
40203             this.removeListener("activate", this.refreshDelegate);
40204         }
40205         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40206         this.on("activate", this.refreshDelegate);
40207         return this.el.getUpdateManager();
40208     },
40209     
40210     _handleRefresh : function(url, params, loadOnce){
40211         if(!loadOnce || !this.loaded){
40212             var updater = this.el.getUpdateManager();
40213             updater.update(url, params, this._setLoaded.createDelegate(this));
40214         }
40215     },
40216     
40217     _setLoaded : function(){
40218         this.loaded = true;
40219     }, 
40220     
40221     /**
40222      * Returns this panel's id
40223      * @return {String} 
40224      */
40225     getId : function(){
40226         return this.el.id;
40227     },
40228     
40229     /** 
40230      * Returns this panel's element - used by regiosn to add.
40231      * @return {Roo.Element} 
40232      */
40233     getEl : function(){
40234         return this.wrapEl || this.el;
40235     },
40236     
40237    
40238     
40239     adjustForComponents : function(width, height)
40240     {
40241         //Roo.log('adjustForComponents ');
40242         if(this.resizeEl != this.el){
40243             width -= this.el.getFrameWidth('lr');
40244             height -= this.el.getFrameWidth('tb');
40245         }
40246         if(this.toolbar){
40247             var te = this.toolbar.getEl();
40248             te.setWidth(width);
40249             height -= te.getHeight();
40250         }
40251         if(this.footer){
40252             var te = this.footer.getEl();
40253             te.setWidth(width);
40254             height -= te.getHeight();
40255         }
40256         
40257         
40258         if(this.adjustments){
40259             width += this.adjustments[0];
40260             height += this.adjustments[1];
40261         }
40262         return {"width": width, "height": height};
40263     },
40264     
40265     setSize : function(width, height){
40266         if(this.fitToFrame && !this.ignoreResize(width, height)){
40267             if(this.fitContainer && this.resizeEl != this.el){
40268                 this.el.setSize(width, height);
40269             }
40270             var size = this.adjustForComponents(width, height);
40271             if (this.iframe) {
40272                 this.iframeEl.setSize(width,height);
40273             }
40274             
40275             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40276             this.fireEvent('resize', this, size.width, size.height);
40277             
40278             
40279         }
40280     },
40281     
40282     /**
40283      * Returns this panel's title
40284      * @return {String} 
40285      */
40286     getTitle : function(){
40287         
40288         if (typeof(this.title) != 'object') {
40289             return this.title;
40290         }
40291         
40292         var t = '';
40293         for (var k in this.title) {
40294             if (!this.title.hasOwnProperty(k)) {
40295                 continue;
40296             }
40297             
40298             if (k.indexOf('-') >= 0) {
40299                 var s = k.split('-');
40300                 for (var i = 0; i<s.length; i++) {
40301                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40302                 }
40303             } else {
40304                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40305             }
40306         }
40307         return t;
40308     },
40309     
40310     /**
40311      * Set this panel's title
40312      * @param {String} title
40313      */
40314     setTitle : function(title){
40315         this.title = title;
40316         if(this.region){
40317             this.region.updatePanelTitle(this, title);
40318         }
40319     },
40320     
40321     /**
40322      * Returns true is this panel was configured to be closable
40323      * @return {Boolean} 
40324      */
40325     isClosable : function(){
40326         return this.closable;
40327     },
40328     
40329     beforeSlide : function(){
40330         this.el.clip();
40331         this.resizeEl.clip();
40332     },
40333     
40334     afterSlide : function(){
40335         this.el.unclip();
40336         this.resizeEl.unclip();
40337     },
40338     
40339     /**
40340      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40341      *   Will fail silently if the {@link #setUrl} method has not been called.
40342      *   This does not activate the panel, just updates its content.
40343      */
40344     refresh : function(){
40345         if(this.refreshDelegate){
40346            this.loaded = false;
40347            this.refreshDelegate();
40348         }
40349     },
40350     
40351     /**
40352      * Destroys this panel
40353      */
40354     destroy : function(){
40355         this.el.removeAllListeners();
40356         var tempEl = document.createElement("span");
40357         tempEl.appendChild(this.el.dom);
40358         tempEl.innerHTML = "";
40359         this.el.remove();
40360         this.el = null;
40361     },
40362     
40363     /**
40364      * form - if the content panel contains a form - this is a reference to it.
40365      * @type {Roo.form.Form}
40366      */
40367     form : false,
40368     /**
40369      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40370      *    This contains a reference to it.
40371      * @type {Roo.View}
40372      */
40373     view : false,
40374     
40375       /**
40376      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40377      * <pre><code>
40378
40379 layout.addxtype({
40380        xtype : 'Form',
40381        items: [ .... ]
40382    }
40383 );
40384
40385 </code></pre>
40386      * @param {Object} cfg Xtype definition of item to add.
40387      */
40388     
40389     
40390     getChildContainer: function () {
40391         return this.getEl();
40392     }
40393     
40394     
40395     /*
40396         var  ret = new Roo.factory(cfg);
40397         return ret;
40398         
40399         
40400         // add form..
40401         if (cfg.xtype.match(/^Form$/)) {
40402             
40403             var el;
40404             //if (this.footer) {
40405             //    el = this.footer.container.insertSibling(false, 'before');
40406             //} else {
40407                 el = this.el.createChild();
40408             //}
40409
40410             this.form = new  Roo.form.Form(cfg);
40411             
40412             
40413             if ( this.form.allItems.length) {
40414                 this.form.render(el.dom);
40415             }
40416             return this.form;
40417         }
40418         // should only have one of theses..
40419         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40420             // views.. should not be just added - used named prop 'view''
40421             
40422             cfg.el = this.el.appendChild(document.createElement("div"));
40423             // factory?
40424             
40425             var ret = new Roo.factory(cfg);
40426              
40427              ret.render && ret.render(false, ''); // render blank..
40428             this.view = ret;
40429             return ret;
40430         }
40431         return false;
40432     }
40433     \*/
40434 });
40435  
40436 /**
40437  * @class Roo.bootstrap.panel.Grid
40438  * @extends Roo.bootstrap.panel.Content
40439  * @constructor
40440  * Create a new GridPanel.
40441  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40442  * @param {Object} config A the config object
40443   
40444  */
40445
40446
40447
40448 Roo.bootstrap.panel.Grid = function(config)
40449 {
40450     
40451       
40452     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40453         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40454
40455     config.el = this.wrapper;
40456     //this.el = this.wrapper;
40457     
40458       if (config.container) {
40459         // ctor'ed from a Border/panel.grid
40460         
40461         
40462         this.wrapper.setStyle("overflow", "hidden");
40463         this.wrapper.addClass('roo-grid-container');
40464
40465     }
40466     
40467     
40468     if(config.toolbar){
40469         var tool_el = this.wrapper.createChild();    
40470         this.toolbar = Roo.factory(config.toolbar);
40471         var ti = [];
40472         if (config.toolbar.items) {
40473             ti = config.toolbar.items ;
40474             delete config.toolbar.items ;
40475         }
40476         
40477         var nitems = [];
40478         this.toolbar.render(tool_el);
40479         for(var i =0;i < ti.length;i++) {
40480           //  Roo.log(['add child', items[i]]);
40481             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40482         }
40483         this.toolbar.items = nitems;
40484         
40485         delete config.toolbar;
40486     }
40487     
40488     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40489     config.grid.scrollBody = true;;
40490     config.grid.monitorWindowResize = false; // turn off autosizing
40491     config.grid.autoHeight = false;
40492     config.grid.autoWidth = false;
40493     
40494     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40495     
40496     if (config.background) {
40497         // render grid on panel activation (if panel background)
40498         this.on('activate', function(gp) {
40499             if (!gp.grid.rendered) {
40500                 gp.grid.render(this.wrapper);
40501                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40502             }
40503         });
40504             
40505     } else {
40506         this.grid.render(this.wrapper);
40507         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40508
40509     }
40510     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40511     // ??? needed ??? config.el = this.wrapper;
40512     
40513     
40514     
40515   
40516     // xtype created footer. - not sure if will work as we normally have to render first..
40517     if (this.footer && !this.footer.el && this.footer.xtype) {
40518         
40519         var ctr = this.grid.getView().getFooterPanel(true);
40520         this.footer.dataSource = this.grid.dataSource;
40521         this.footer = Roo.factory(this.footer, Roo);
40522         this.footer.render(ctr);
40523         
40524     }
40525     
40526     
40527     
40528     
40529      
40530 };
40531
40532 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40533     getId : function(){
40534         return this.grid.id;
40535     },
40536     
40537     /**
40538      * Returns the grid for this panel
40539      * @return {Roo.bootstrap.Table} 
40540      */
40541     getGrid : function(){
40542         return this.grid;    
40543     },
40544     
40545     setSize : function(width, height){
40546         if(!this.ignoreResize(width, height)){
40547             var grid = this.grid;
40548             var size = this.adjustForComponents(width, height);
40549             // tfoot is not a footer?
40550           
40551             
40552             var gridel = grid.getGridEl();
40553             gridel.setSize(size.width, size.height);
40554             
40555             var tbd = grid.getGridEl().select('tbody', true).first();
40556             var thd = grid.getGridEl().select('thead',true).first();
40557             var tbf= grid.getGridEl().select('tfoot', true).first();
40558
40559             if (tbf) {
40560                 size.height -= tbf.getHeight();
40561             }
40562             if (thd) {
40563                 size.height -= thd.getHeight();
40564             }
40565             
40566             tbd.setSize(size.width, size.height );
40567             // this is for the account management tab -seems to work there.
40568             var thd = grid.getGridEl().select('thead',true).first();
40569             //if (tbd) {
40570             //    tbd.setSize(size.width, size.height - thd.getHeight());
40571             //}
40572              
40573             grid.autoSize();
40574         }
40575     },
40576      
40577     
40578     
40579     beforeSlide : function(){
40580         this.grid.getView().scroller.clip();
40581     },
40582     
40583     afterSlide : function(){
40584         this.grid.getView().scroller.unclip();
40585     },
40586     
40587     destroy : function(){
40588         this.grid.destroy();
40589         delete this.grid;
40590         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40591     }
40592 });
40593
40594 /**
40595  * @class Roo.bootstrap.panel.Nest
40596  * @extends Roo.bootstrap.panel.Content
40597  * @constructor
40598  * Create a new Panel, that can contain a layout.Border.
40599  * 
40600  * 
40601  * @param {Roo.BorderLayout} layout The layout for this panel
40602  * @param {String/Object} config A string to set only the title or a config object
40603  */
40604 Roo.bootstrap.panel.Nest = function(config)
40605 {
40606     // construct with only one argument..
40607     /* FIXME - implement nicer consturctors
40608     if (layout.layout) {
40609         config = layout;
40610         layout = config.layout;
40611         delete config.layout;
40612     }
40613     if (layout.xtype && !layout.getEl) {
40614         // then layout needs constructing..
40615         layout = Roo.factory(layout, Roo);
40616     }
40617     */
40618     
40619     config.el =  config.layout.getEl();
40620     
40621     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40622     
40623     config.layout.monitorWindowResize = false; // turn off autosizing
40624     this.layout = config.layout;
40625     this.layout.getEl().addClass("roo-layout-nested-layout");
40626     this.layout.parent = this;
40627     
40628     
40629     
40630     
40631 };
40632
40633 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40634
40635     setSize : function(width, height){
40636         if(!this.ignoreResize(width, height)){
40637             var size = this.adjustForComponents(width, height);
40638             var el = this.layout.getEl();
40639             if (size.height < 1) {
40640                 el.setWidth(size.width);   
40641             } else {
40642                 el.setSize(size.width, size.height);
40643             }
40644             var touch = el.dom.offsetWidth;
40645             this.layout.layout();
40646             // ie requires a double layout on the first pass
40647             if(Roo.isIE && !this.initialized){
40648                 this.initialized = true;
40649                 this.layout.layout();
40650             }
40651         }
40652     },
40653     
40654     // activate all subpanels if not currently active..
40655     
40656     setActiveState : function(active){
40657         this.active = active;
40658         this.setActiveClass(active);
40659         
40660         if(!active){
40661             this.fireEvent("deactivate", this);
40662             return;
40663         }
40664         
40665         this.fireEvent("activate", this);
40666         // not sure if this should happen before or after..
40667         if (!this.layout) {
40668             return; // should not happen..
40669         }
40670         var reg = false;
40671         for (var r in this.layout.regions) {
40672             reg = this.layout.getRegion(r);
40673             if (reg.getActivePanel()) {
40674                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40675                 reg.setActivePanel(reg.getActivePanel());
40676                 continue;
40677             }
40678             if (!reg.panels.length) {
40679                 continue;
40680             }
40681             reg.showPanel(reg.getPanel(0));
40682         }
40683         
40684         
40685         
40686         
40687     },
40688     
40689     /**
40690      * Returns the nested BorderLayout for this panel
40691      * @return {Roo.BorderLayout} 
40692      */
40693     getLayout : function(){
40694         return this.layout;
40695     },
40696     
40697      /**
40698      * Adds a xtype elements to the layout of the nested panel
40699      * <pre><code>
40700
40701 panel.addxtype({
40702        xtype : 'ContentPanel',
40703        region: 'west',
40704        items: [ .... ]
40705    }
40706 );
40707
40708 panel.addxtype({
40709         xtype : 'NestedLayoutPanel',
40710         region: 'west',
40711         layout: {
40712            center: { },
40713            west: { }   
40714         },
40715         items : [ ... list of content panels or nested layout panels.. ]
40716    }
40717 );
40718 </code></pre>
40719      * @param {Object} cfg Xtype definition of item to add.
40720      */
40721     addxtype : function(cfg) {
40722         return this.layout.addxtype(cfg);
40723     
40724     }
40725 });/*
40726  * Based on:
40727  * Ext JS Library 1.1.1
40728  * Copyright(c) 2006-2007, Ext JS, LLC.
40729  *
40730  * Originally Released Under LGPL - original licence link has changed is not relivant.
40731  *
40732  * Fork - LGPL
40733  * <script type="text/javascript">
40734  */
40735 /**
40736  * @class Roo.TabPanel
40737  * @extends Roo.util.Observable
40738  * A lightweight tab container.
40739  * <br><br>
40740  * Usage:
40741  * <pre><code>
40742 // basic tabs 1, built from existing content
40743 var tabs = new Roo.TabPanel("tabs1");
40744 tabs.addTab("script", "View Script");
40745 tabs.addTab("markup", "View Markup");
40746 tabs.activate("script");
40747
40748 // more advanced tabs, built from javascript
40749 var jtabs = new Roo.TabPanel("jtabs");
40750 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40751
40752 // set up the UpdateManager
40753 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40754 var updater = tab2.getUpdateManager();
40755 updater.setDefaultUrl("ajax1.htm");
40756 tab2.on('activate', updater.refresh, updater, true);
40757
40758 // Use setUrl for Ajax loading
40759 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40760 tab3.setUrl("ajax2.htm", null, true);
40761
40762 // Disabled tab
40763 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40764 tab4.disable();
40765
40766 jtabs.activate("jtabs-1");
40767  * </code></pre>
40768  * @constructor
40769  * Create a new TabPanel.
40770  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40771  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40772  */
40773 Roo.bootstrap.panel.Tabs = function(config){
40774     /**
40775     * The container element for this TabPanel.
40776     * @type Roo.Element
40777     */
40778     this.el = Roo.get(config.el);
40779     delete config.el;
40780     if(config){
40781         if(typeof config == "boolean"){
40782             this.tabPosition = config ? "bottom" : "top";
40783         }else{
40784             Roo.apply(this, config);
40785         }
40786     }
40787     
40788     if(this.tabPosition == "bottom"){
40789         // if tabs are at the bottom = create the body first.
40790         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40791         this.el.addClass("roo-tabs-bottom");
40792     }
40793     // next create the tabs holders
40794     
40795     if (this.tabPosition == "west"){
40796         
40797         var reg = this.region; // fake it..
40798         while (reg) {
40799             if (!reg.mgr.parent) {
40800                 break;
40801             }
40802             reg = reg.mgr.parent.region;
40803         }
40804         Roo.log("got nest?");
40805         Roo.log(reg);
40806         if (reg.mgr.getRegion('west')) {
40807             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40808             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40809             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40810             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40811             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40812         
40813             
40814         }
40815         
40816         
40817     } else {
40818      
40819         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40820         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40821         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40822         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40823     }
40824     
40825     
40826     if(Roo.isIE){
40827         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40828     }
40829     
40830     // finally - if tabs are at the top, then create the body last..
40831     if(this.tabPosition != "bottom"){
40832         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40833          * @type Roo.Element
40834          */
40835         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40836         this.el.addClass("roo-tabs-top");
40837     }
40838     this.items = [];
40839
40840     this.bodyEl.setStyle("position", "relative");
40841
40842     this.active = null;
40843     this.activateDelegate = this.activate.createDelegate(this);
40844
40845     this.addEvents({
40846         /**
40847          * @event tabchange
40848          * Fires when the active tab changes
40849          * @param {Roo.TabPanel} this
40850          * @param {Roo.TabPanelItem} activePanel The new active tab
40851          */
40852         "tabchange": true,
40853         /**
40854          * @event beforetabchange
40855          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40856          * @param {Roo.TabPanel} this
40857          * @param {Object} e Set cancel to true on this object to cancel the tab change
40858          * @param {Roo.TabPanelItem} tab The tab being changed to
40859          */
40860         "beforetabchange" : true
40861     });
40862
40863     Roo.EventManager.onWindowResize(this.onResize, this);
40864     this.cpad = this.el.getPadding("lr");
40865     this.hiddenCount = 0;
40866
40867
40868     // toolbar on the tabbar support...
40869     if (this.toolbar) {
40870         alert("no toolbar support yet");
40871         this.toolbar  = false;
40872         /*
40873         var tcfg = this.toolbar;
40874         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40875         this.toolbar = new Roo.Toolbar(tcfg);
40876         if (Roo.isSafari) {
40877             var tbl = tcfg.container.child('table', true);
40878             tbl.setAttribute('width', '100%');
40879         }
40880         */
40881         
40882     }
40883    
40884
40885
40886     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40887 };
40888
40889 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40890     /*
40891      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40892      */
40893     tabPosition : "top",
40894     /*
40895      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40896      */
40897     currentTabWidth : 0,
40898     /*
40899      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40900      */
40901     minTabWidth : 40,
40902     /*
40903      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40904      */
40905     maxTabWidth : 250,
40906     /*
40907      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40908      */
40909     preferredTabWidth : 175,
40910     /*
40911      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40912      */
40913     resizeTabs : false,
40914     /*
40915      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40916      */
40917     monitorResize : true,
40918     /*
40919      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40920      */
40921     toolbar : false,  // set by caller..
40922     
40923     region : false, /// set by caller
40924     
40925     disableTooltips : true, // not used yet...
40926
40927     /**
40928      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40929      * @param {String} id The id of the div to use <b>or create</b>
40930      * @param {String} text The text for the tab
40931      * @param {String} content (optional) Content to put in the TabPanelItem body
40932      * @param {Boolean} closable (optional) True to create a close icon on the tab
40933      * @return {Roo.TabPanelItem} The created TabPanelItem
40934      */
40935     addTab : function(id, text, content, closable, tpl)
40936     {
40937         var item = new Roo.bootstrap.panel.TabItem({
40938             panel: this,
40939             id : id,
40940             text : text,
40941             closable : closable,
40942             tpl : tpl
40943         });
40944         this.addTabItem(item);
40945         if(content){
40946             item.setContent(content);
40947         }
40948         return item;
40949     },
40950
40951     /**
40952      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40953      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40954      * @return {Roo.TabPanelItem}
40955      */
40956     getTab : function(id){
40957         return this.items[id];
40958     },
40959
40960     /**
40961      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40962      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40963      */
40964     hideTab : function(id){
40965         var t = this.items[id];
40966         if(!t.isHidden()){
40967            t.setHidden(true);
40968            this.hiddenCount++;
40969            this.autoSizeTabs();
40970         }
40971     },
40972
40973     /**
40974      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40975      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40976      */
40977     unhideTab : function(id){
40978         var t = this.items[id];
40979         if(t.isHidden()){
40980            t.setHidden(false);
40981            this.hiddenCount--;
40982            this.autoSizeTabs();
40983         }
40984     },
40985
40986     /**
40987      * Adds an existing {@link Roo.TabPanelItem}.
40988      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40989      */
40990     addTabItem : function(item)
40991     {
40992         this.items[item.id] = item;
40993         this.items.push(item);
40994         this.autoSizeTabs();
40995       //  if(this.resizeTabs){
40996     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40997   //         this.autoSizeTabs();
40998 //        }else{
40999 //            item.autoSize();
41000        // }
41001     },
41002
41003     /**
41004      * Removes a {@link Roo.TabPanelItem}.
41005      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41006      */
41007     removeTab : function(id){
41008         var items = this.items;
41009         var tab = items[id];
41010         if(!tab) { return; }
41011         var index = items.indexOf(tab);
41012         if(this.active == tab && items.length > 1){
41013             var newTab = this.getNextAvailable(index);
41014             if(newTab) {
41015                 newTab.activate();
41016             }
41017         }
41018         this.stripEl.dom.removeChild(tab.pnode.dom);
41019         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41020             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41021         }
41022         items.splice(index, 1);
41023         delete this.items[tab.id];
41024         tab.fireEvent("close", tab);
41025         tab.purgeListeners();
41026         this.autoSizeTabs();
41027     },
41028
41029     getNextAvailable : function(start){
41030         var items = this.items;
41031         var index = start;
41032         // look for a next tab that will slide over to
41033         // replace the one being removed
41034         while(index < items.length){
41035             var item = items[++index];
41036             if(item && !item.isHidden()){
41037                 return item;
41038             }
41039         }
41040         // if one isn't found select the previous tab (on the left)
41041         index = start;
41042         while(index >= 0){
41043             var item = items[--index];
41044             if(item && !item.isHidden()){
41045                 return item;
41046             }
41047         }
41048         return null;
41049     },
41050
41051     /**
41052      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41053      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41054      */
41055     disableTab : function(id){
41056         var tab = this.items[id];
41057         if(tab && this.active != tab){
41058             tab.disable();
41059         }
41060     },
41061
41062     /**
41063      * Enables a {@link Roo.TabPanelItem} that is disabled.
41064      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41065      */
41066     enableTab : function(id){
41067         var tab = this.items[id];
41068         tab.enable();
41069     },
41070
41071     /**
41072      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41073      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41074      * @return {Roo.TabPanelItem} The TabPanelItem.
41075      */
41076     activate : function(id)
41077     {
41078         //Roo.log('activite:'  + id);
41079         
41080         var tab = this.items[id];
41081         if(!tab){
41082             return null;
41083         }
41084         if(tab == this.active || tab.disabled){
41085             return tab;
41086         }
41087         var e = {};
41088         this.fireEvent("beforetabchange", this, e, tab);
41089         if(e.cancel !== true && !tab.disabled){
41090             if(this.active){
41091                 this.active.hide();
41092             }
41093             this.active = this.items[id];
41094             this.active.show();
41095             this.fireEvent("tabchange", this, this.active);
41096         }
41097         return tab;
41098     },
41099
41100     /**
41101      * Gets the active {@link Roo.TabPanelItem}.
41102      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41103      */
41104     getActiveTab : function(){
41105         return this.active;
41106     },
41107
41108     /**
41109      * Updates the tab body element to fit the height of the container element
41110      * for overflow scrolling
41111      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41112      */
41113     syncHeight : function(targetHeight){
41114         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41115         var bm = this.bodyEl.getMargins();
41116         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41117         this.bodyEl.setHeight(newHeight);
41118         return newHeight;
41119     },
41120
41121     onResize : function(){
41122         if(this.monitorResize){
41123             this.autoSizeTabs();
41124         }
41125     },
41126
41127     /**
41128      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41129      */
41130     beginUpdate : function(){
41131         this.updating = true;
41132     },
41133
41134     /**
41135      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41136      */
41137     endUpdate : function(){
41138         this.updating = false;
41139         this.autoSizeTabs();
41140     },
41141
41142     /**
41143      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41144      */
41145     autoSizeTabs : function()
41146     {
41147         var count = this.items.length;
41148         var vcount = count - this.hiddenCount;
41149         
41150         if (vcount < 2) {
41151             this.stripEl.hide();
41152         } else {
41153             this.stripEl.show();
41154         }
41155         
41156         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41157             return;
41158         }
41159         
41160         
41161         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41162         var availWidth = Math.floor(w / vcount);
41163         var b = this.stripBody;
41164         if(b.getWidth() > w){
41165             var tabs = this.items;
41166             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41167             if(availWidth < this.minTabWidth){
41168                 /*if(!this.sleft){    // incomplete scrolling code
41169                     this.createScrollButtons();
41170                 }
41171                 this.showScroll();
41172                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41173             }
41174         }else{
41175             if(this.currentTabWidth < this.preferredTabWidth){
41176                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41177             }
41178         }
41179     },
41180
41181     /**
41182      * Returns the number of tabs in this TabPanel.
41183      * @return {Number}
41184      */
41185      getCount : function(){
41186          return this.items.length;
41187      },
41188
41189     /**
41190      * Resizes all the tabs to the passed width
41191      * @param {Number} The new width
41192      */
41193     setTabWidth : function(width){
41194         this.currentTabWidth = width;
41195         for(var i = 0, len = this.items.length; i < len; i++) {
41196                 if(!this.items[i].isHidden()) {
41197                 this.items[i].setWidth(width);
41198             }
41199         }
41200     },
41201
41202     /**
41203      * Destroys this TabPanel
41204      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41205      */
41206     destroy : function(removeEl){
41207         Roo.EventManager.removeResizeListener(this.onResize, this);
41208         for(var i = 0, len = this.items.length; i < len; i++){
41209             this.items[i].purgeListeners();
41210         }
41211         if(removeEl === true){
41212             this.el.update("");
41213             this.el.remove();
41214         }
41215     },
41216     
41217     createStrip : function(container)
41218     {
41219         var strip = document.createElement("nav");
41220         strip.className = Roo.bootstrap.version == 4 ?
41221             "navbar-light bg-light" : 
41222             "navbar navbar-default"; //"x-tabs-wrap";
41223         container.appendChild(strip);
41224         return strip;
41225     },
41226     
41227     createStripList : function(strip)
41228     {
41229         // div wrapper for retard IE
41230         // returns the "tr" element.
41231         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41232         //'<div class="x-tabs-strip-wrap">'+
41233           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41234           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41235         return strip.firstChild; //.firstChild.firstChild.firstChild;
41236     },
41237     createBody : function(container)
41238     {
41239         var body = document.createElement("div");
41240         Roo.id(body, "tab-body");
41241         //Roo.fly(body).addClass("x-tabs-body");
41242         Roo.fly(body).addClass("tab-content");
41243         container.appendChild(body);
41244         return body;
41245     },
41246     createItemBody :function(bodyEl, id){
41247         var body = Roo.getDom(id);
41248         if(!body){
41249             body = document.createElement("div");
41250             body.id = id;
41251         }
41252         //Roo.fly(body).addClass("x-tabs-item-body");
41253         Roo.fly(body).addClass("tab-pane");
41254          bodyEl.insertBefore(body, bodyEl.firstChild);
41255         return body;
41256     },
41257     /** @private */
41258     createStripElements :  function(stripEl, text, closable, tpl)
41259     {
41260         var td = document.createElement("li"); // was td..
41261         td.className = 'nav-item';
41262         
41263         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41264         
41265         
41266         stripEl.appendChild(td);
41267         /*if(closable){
41268             td.className = "x-tabs-closable";
41269             if(!this.closeTpl){
41270                 this.closeTpl = new Roo.Template(
41271                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41272                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41273                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41274                 );
41275             }
41276             var el = this.closeTpl.overwrite(td, {"text": text});
41277             var close = el.getElementsByTagName("div")[0];
41278             var inner = el.getElementsByTagName("em")[0];
41279             return {"el": el, "close": close, "inner": inner};
41280         } else {
41281         */
41282         // not sure what this is..
41283 //            if(!this.tabTpl){
41284                 //this.tabTpl = new Roo.Template(
41285                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41286                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41287                 //);
41288 //                this.tabTpl = new Roo.Template(
41289 //                   '<a href="#">' +
41290 //                   '<span unselectable="on"' +
41291 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41292 //                            ' >{text}</span></a>'
41293 //                );
41294 //                
41295 //            }
41296
41297
41298             var template = tpl || this.tabTpl || false;
41299             
41300             if(!template){
41301                 template =  new Roo.Template(
41302                         Roo.bootstrap.version == 4 ? 
41303                             (
41304                                 '<a class="nav-link" href="#" unselectable="on"' +
41305                                      (this.disableTooltips ? '' : ' title="{text}"') +
41306                                      ' >{text}</a>'
41307                             ) : (
41308                                 '<a class="nav-link" href="#">' +
41309                                 '<span unselectable="on"' +
41310                                          (this.disableTooltips ? '' : ' title="{text}"') +
41311                                     ' >{text}</span></a>'
41312                             )
41313                 );
41314             }
41315             
41316             switch (typeof(template)) {
41317                 case 'object' :
41318                     break;
41319                 case 'string' :
41320                     template = new Roo.Template(template);
41321                     break;
41322                 default :
41323                     break;
41324             }
41325             
41326             var el = template.overwrite(td, {"text": text});
41327             
41328             var inner = el.getElementsByTagName("span")[0];
41329             
41330             return {"el": el, "inner": inner};
41331             
41332     }
41333         
41334     
41335 });
41336
41337 /**
41338  * @class Roo.TabPanelItem
41339  * @extends Roo.util.Observable
41340  * Represents an individual item (tab plus body) in a TabPanel.
41341  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41342  * @param {String} id The id of this TabPanelItem
41343  * @param {String} text The text for the tab of this TabPanelItem
41344  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41345  */
41346 Roo.bootstrap.panel.TabItem = function(config){
41347     /**
41348      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41349      * @type Roo.TabPanel
41350      */
41351     this.tabPanel = config.panel;
41352     /**
41353      * The id for this TabPanelItem
41354      * @type String
41355      */
41356     this.id = config.id;
41357     /** @private */
41358     this.disabled = false;
41359     /** @private */
41360     this.text = config.text;
41361     /** @private */
41362     this.loaded = false;
41363     this.closable = config.closable;
41364
41365     /**
41366      * The body element for this TabPanelItem.
41367      * @type Roo.Element
41368      */
41369     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41370     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41371     this.bodyEl.setStyle("display", "block");
41372     this.bodyEl.setStyle("zoom", "1");
41373     //this.hideAction();
41374
41375     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41376     /** @private */
41377     this.el = Roo.get(els.el);
41378     this.inner = Roo.get(els.inner, true);
41379      this.textEl = Roo.bootstrap.version == 4 ?
41380         this.el : Roo.get(this.el.dom.firstChild, true);
41381
41382     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41383     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41384
41385     
41386 //    this.el.on("mousedown", this.onTabMouseDown, this);
41387     this.el.on("click", this.onTabClick, this);
41388     /** @private */
41389     if(config.closable){
41390         var c = Roo.get(els.close, true);
41391         c.dom.title = this.closeText;
41392         c.addClassOnOver("close-over");
41393         c.on("click", this.closeClick, this);
41394      }
41395
41396     this.addEvents({
41397          /**
41398          * @event activate
41399          * Fires when this tab becomes the active tab.
41400          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41401          * @param {Roo.TabPanelItem} this
41402          */
41403         "activate": true,
41404         /**
41405          * @event beforeclose
41406          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41407          * @param {Roo.TabPanelItem} this
41408          * @param {Object} e Set cancel to true on this object to cancel the close.
41409          */
41410         "beforeclose": true,
41411         /**
41412          * @event close
41413          * Fires when this tab is closed.
41414          * @param {Roo.TabPanelItem} this
41415          */
41416          "close": true,
41417         /**
41418          * @event deactivate
41419          * Fires when this tab is no longer the active tab.
41420          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41421          * @param {Roo.TabPanelItem} this
41422          */
41423          "deactivate" : true
41424     });
41425     this.hidden = false;
41426
41427     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41428 };
41429
41430 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41431            {
41432     purgeListeners : function(){
41433        Roo.util.Observable.prototype.purgeListeners.call(this);
41434        this.el.removeAllListeners();
41435     },
41436     /**
41437      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41438      */
41439     show : function(){
41440         this.status_node.addClass("active");
41441         this.showAction();
41442         if(Roo.isOpera){
41443             this.tabPanel.stripWrap.repaint();
41444         }
41445         this.fireEvent("activate", this.tabPanel, this);
41446     },
41447
41448     /**
41449      * Returns true if this tab is the active tab.
41450      * @return {Boolean}
41451      */
41452     isActive : function(){
41453         return this.tabPanel.getActiveTab() == this;
41454     },
41455
41456     /**
41457      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41458      */
41459     hide : function(){
41460         this.status_node.removeClass("active");
41461         this.hideAction();
41462         this.fireEvent("deactivate", this.tabPanel, this);
41463     },
41464
41465     hideAction : function(){
41466         this.bodyEl.hide();
41467         this.bodyEl.setStyle("position", "absolute");
41468         this.bodyEl.setLeft("-20000px");
41469         this.bodyEl.setTop("-20000px");
41470     },
41471
41472     showAction : function(){
41473         this.bodyEl.setStyle("position", "relative");
41474         this.bodyEl.setTop("");
41475         this.bodyEl.setLeft("");
41476         this.bodyEl.show();
41477     },
41478
41479     /**
41480      * Set the tooltip for the tab.
41481      * @param {String} tooltip The tab's tooltip
41482      */
41483     setTooltip : function(text){
41484         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41485             this.textEl.dom.qtip = text;
41486             this.textEl.dom.removeAttribute('title');
41487         }else{
41488             this.textEl.dom.title = text;
41489         }
41490     },
41491
41492     onTabClick : function(e){
41493         e.preventDefault();
41494         this.tabPanel.activate(this.id);
41495     },
41496
41497     onTabMouseDown : function(e){
41498         e.preventDefault();
41499         this.tabPanel.activate(this.id);
41500     },
41501 /*
41502     getWidth : function(){
41503         return this.inner.getWidth();
41504     },
41505
41506     setWidth : function(width){
41507         var iwidth = width - this.linode.getPadding("lr");
41508         this.inner.setWidth(iwidth);
41509         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41510         this.linode.setWidth(width);
41511     },
41512 */
41513     /**
41514      * Show or hide the tab
41515      * @param {Boolean} hidden True to hide or false to show.
41516      */
41517     setHidden : function(hidden){
41518         this.hidden = hidden;
41519         this.linode.setStyle("display", hidden ? "none" : "");
41520     },
41521
41522     /**
41523      * Returns true if this tab is "hidden"
41524      * @return {Boolean}
41525      */
41526     isHidden : function(){
41527         return this.hidden;
41528     },
41529
41530     /**
41531      * Returns the text for this tab
41532      * @return {String}
41533      */
41534     getText : function(){
41535         return this.text;
41536     },
41537     /*
41538     autoSize : function(){
41539         //this.el.beginMeasure();
41540         this.textEl.setWidth(1);
41541         /*
41542          *  #2804 [new] Tabs in Roojs
41543          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41544          */
41545         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41546         //this.el.endMeasure();
41547     //},
41548
41549     /**
41550      * Sets the text for the tab (Note: this also sets the tooltip text)
41551      * @param {String} text The tab's text and tooltip
41552      */
41553     setText : function(text){
41554         this.text = text;
41555         this.textEl.update(text);
41556         this.setTooltip(text);
41557         //if(!this.tabPanel.resizeTabs){
41558         //    this.autoSize();
41559         //}
41560     },
41561     /**
41562      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41563      */
41564     activate : function(){
41565         this.tabPanel.activate(this.id);
41566     },
41567
41568     /**
41569      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41570      */
41571     disable : function(){
41572         if(this.tabPanel.active != this){
41573             this.disabled = true;
41574             this.status_node.addClass("disabled");
41575         }
41576     },
41577
41578     /**
41579      * Enables this TabPanelItem if it was previously disabled.
41580      */
41581     enable : function(){
41582         this.disabled = false;
41583         this.status_node.removeClass("disabled");
41584     },
41585
41586     /**
41587      * Sets the content for this TabPanelItem.
41588      * @param {String} content The content
41589      * @param {Boolean} loadScripts true to look for and load scripts
41590      */
41591     setContent : function(content, loadScripts){
41592         this.bodyEl.update(content, loadScripts);
41593     },
41594
41595     /**
41596      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41597      * @return {Roo.UpdateManager} The UpdateManager
41598      */
41599     getUpdateManager : function(){
41600         return this.bodyEl.getUpdateManager();
41601     },
41602
41603     /**
41604      * Set a URL to be used to load the content for this TabPanelItem.
41605      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41606      * @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)
41607      * @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)
41608      * @return {Roo.UpdateManager} The UpdateManager
41609      */
41610     setUrl : function(url, params, loadOnce){
41611         if(this.refreshDelegate){
41612             this.un('activate', this.refreshDelegate);
41613         }
41614         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41615         this.on("activate", this.refreshDelegate);
41616         return this.bodyEl.getUpdateManager();
41617     },
41618
41619     /** @private */
41620     _handleRefresh : function(url, params, loadOnce){
41621         if(!loadOnce || !this.loaded){
41622             var updater = this.bodyEl.getUpdateManager();
41623             updater.update(url, params, this._setLoaded.createDelegate(this));
41624         }
41625     },
41626
41627     /**
41628      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41629      *   Will fail silently if the setUrl method has not been called.
41630      *   This does not activate the panel, just updates its content.
41631      */
41632     refresh : function(){
41633         if(this.refreshDelegate){
41634            this.loaded = false;
41635            this.refreshDelegate();
41636         }
41637     },
41638
41639     /** @private */
41640     _setLoaded : function(){
41641         this.loaded = true;
41642     },
41643
41644     /** @private */
41645     closeClick : function(e){
41646         var o = {};
41647         e.stopEvent();
41648         this.fireEvent("beforeclose", this, o);
41649         if(o.cancel !== true){
41650             this.tabPanel.removeTab(this.id);
41651         }
41652     },
41653     /**
41654      * The text displayed in the tooltip for the close icon.
41655      * @type String
41656      */
41657     closeText : "Close this tab"
41658 });
41659 /**
41660 *    This script refer to:
41661 *    Title: International Telephone Input
41662 *    Author: Jack O'Connor
41663 *    Code version:  v12.1.12
41664 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41665 **/
41666
41667 Roo.bootstrap.PhoneInputData = function() {
41668     var d = [
41669       [
41670         "Afghanistan (‫افغانستان‬‎)",
41671         "af",
41672         "93"
41673       ],
41674       [
41675         "Albania (Shqipëri)",
41676         "al",
41677         "355"
41678       ],
41679       [
41680         "Algeria (‫الجزائر‬‎)",
41681         "dz",
41682         "213"
41683       ],
41684       [
41685         "American Samoa",
41686         "as",
41687         "1684"
41688       ],
41689       [
41690         "Andorra",
41691         "ad",
41692         "376"
41693       ],
41694       [
41695         "Angola",
41696         "ao",
41697         "244"
41698       ],
41699       [
41700         "Anguilla",
41701         "ai",
41702         "1264"
41703       ],
41704       [
41705         "Antigua and Barbuda",
41706         "ag",
41707         "1268"
41708       ],
41709       [
41710         "Argentina",
41711         "ar",
41712         "54"
41713       ],
41714       [
41715         "Armenia (Հայաստան)",
41716         "am",
41717         "374"
41718       ],
41719       [
41720         "Aruba",
41721         "aw",
41722         "297"
41723       ],
41724       [
41725         "Australia",
41726         "au",
41727         "61",
41728         0
41729       ],
41730       [
41731         "Austria (Österreich)",
41732         "at",
41733         "43"
41734       ],
41735       [
41736         "Azerbaijan (Azərbaycan)",
41737         "az",
41738         "994"
41739       ],
41740       [
41741         "Bahamas",
41742         "bs",
41743         "1242"
41744       ],
41745       [
41746         "Bahrain (‫البحرين‬‎)",
41747         "bh",
41748         "973"
41749       ],
41750       [
41751         "Bangladesh (বাংলাদেশ)",
41752         "bd",
41753         "880"
41754       ],
41755       [
41756         "Barbados",
41757         "bb",
41758         "1246"
41759       ],
41760       [
41761         "Belarus (Беларусь)",
41762         "by",
41763         "375"
41764       ],
41765       [
41766         "Belgium (België)",
41767         "be",
41768         "32"
41769       ],
41770       [
41771         "Belize",
41772         "bz",
41773         "501"
41774       ],
41775       [
41776         "Benin (Bénin)",
41777         "bj",
41778         "229"
41779       ],
41780       [
41781         "Bermuda",
41782         "bm",
41783         "1441"
41784       ],
41785       [
41786         "Bhutan (འབྲུག)",
41787         "bt",
41788         "975"
41789       ],
41790       [
41791         "Bolivia",
41792         "bo",
41793         "591"
41794       ],
41795       [
41796         "Bosnia and Herzegovina (Босна и Херцеговина)",
41797         "ba",
41798         "387"
41799       ],
41800       [
41801         "Botswana",
41802         "bw",
41803         "267"
41804       ],
41805       [
41806         "Brazil (Brasil)",
41807         "br",
41808         "55"
41809       ],
41810       [
41811         "British Indian Ocean Territory",
41812         "io",
41813         "246"
41814       ],
41815       [
41816         "British Virgin Islands",
41817         "vg",
41818         "1284"
41819       ],
41820       [
41821         "Brunei",
41822         "bn",
41823         "673"
41824       ],
41825       [
41826         "Bulgaria (България)",
41827         "bg",
41828         "359"
41829       ],
41830       [
41831         "Burkina Faso",
41832         "bf",
41833         "226"
41834       ],
41835       [
41836         "Burundi (Uburundi)",
41837         "bi",
41838         "257"
41839       ],
41840       [
41841         "Cambodia (កម្ពុជា)",
41842         "kh",
41843         "855"
41844       ],
41845       [
41846         "Cameroon (Cameroun)",
41847         "cm",
41848         "237"
41849       ],
41850       [
41851         "Canada",
41852         "ca",
41853         "1",
41854         1,
41855         ["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"]
41856       ],
41857       [
41858         "Cape Verde (Kabu Verdi)",
41859         "cv",
41860         "238"
41861       ],
41862       [
41863         "Caribbean Netherlands",
41864         "bq",
41865         "599",
41866         1
41867       ],
41868       [
41869         "Cayman Islands",
41870         "ky",
41871         "1345"
41872       ],
41873       [
41874         "Central African Republic (République centrafricaine)",
41875         "cf",
41876         "236"
41877       ],
41878       [
41879         "Chad (Tchad)",
41880         "td",
41881         "235"
41882       ],
41883       [
41884         "Chile",
41885         "cl",
41886         "56"
41887       ],
41888       [
41889         "China (中国)",
41890         "cn",
41891         "86"
41892       ],
41893       [
41894         "Christmas Island",
41895         "cx",
41896         "61",
41897         2
41898       ],
41899       [
41900         "Cocos (Keeling) Islands",
41901         "cc",
41902         "61",
41903         1
41904       ],
41905       [
41906         "Colombia",
41907         "co",
41908         "57"
41909       ],
41910       [
41911         "Comoros (‫جزر القمر‬‎)",
41912         "km",
41913         "269"
41914       ],
41915       [
41916         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41917         "cd",
41918         "243"
41919       ],
41920       [
41921         "Congo (Republic) (Congo-Brazzaville)",
41922         "cg",
41923         "242"
41924       ],
41925       [
41926         "Cook Islands",
41927         "ck",
41928         "682"
41929       ],
41930       [
41931         "Costa Rica",
41932         "cr",
41933         "506"
41934       ],
41935       [
41936         "Côte d’Ivoire",
41937         "ci",
41938         "225"
41939       ],
41940       [
41941         "Croatia (Hrvatska)",
41942         "hr",
41943         "385"
41944       ],
41945       [
41946         "Cuba",
41947         "cu",
41948         "53"
41949       ],
41950       [
41951         "Curaçao",
41952         "cw",
41953         "599",
41954         0
41955       ],
41956       [
41957         "Cyprus (Κύπρος)",
41958         "cy",
41959         "357"
41960       ],
41961       [
41962         "Czech Republic (Česká republika)",
41963         "cz",
41964         "420"
41965       ],
41966       [
41967         "Denmark (Danmark)",
41968         "dk",
41969         "45"
41970       ],
41971       [
41972         "Djibouti",
41973         "dj",
41974         "253"
41975       ],
41976       [
41977         "Dominica",
41978         "dm",
41979         "1767"
41980       ],
41981       [
41982         "Dominican Republic (República Dominicana)",
41983         "do",
41984         "1",
41985         2,
41986         ["809", "829", "849"]
41987       ],
41988       [
41989         "Ecuador",
41990         "ec",
41991         "593"
41992       ],
41993       [
41994         "Egypt (‫مصر‬‎)",
41995         "eg",
41996         "20"
41997       ],
41998       [
41999         "El Salvador",
42000         "sv",
42001         "503"
42002       ],
42003       [
42004         "Equatorial Guinea (Guinea Ecuatorial)",
42005         "gq",
42006         "240"
42007       ],
42008       [
42009         "Eritrea",
42010         "er",
42011         "291"
42012       ],
42013       [
42014         "Estonia (Eesti)",
42015         "ee",
42016         "372"
42017       ],
42018       [
42019         "Ethiopia",
42020         "et",
42021         "251"
42022       ],
42023       [
42024         "Falkland Islands (Islas Malvinas)",
42025         "fk",
42026         "500"
42027       ],
42028       [
42029         "Faroe Islands (Føroyar)",
42030         "fo",
42031         "298"
42032       ],
42033       [
42034         "Fiji",
42035         "fj",
42036         "679"
42037       ],
42038       [
42039         "Finland (Suomi)",
42040         "fi",
42041         "358",
42042         0
42043       ],
42044       [
42045         "France",
42046         "fr",
42047         "33"
42048       ],
42049       [
42050         "French Guiana (Guyane française)",
42051         "gf",
42052         "594"
42053       ],
42054       [
42055         "French Polynesia (Polynésie française)",
42056         "pf",
42057         "689"
42058       ],
42059       [
42060         "Gabon",
42061         "ga",
42062         "241"
42063       ],
42064       [
42065         "Gambia",
42066         "gm",
42067         "220"
42068       ],
42069       [
42070         "Georgia (საქართველო)",
42071         "ge",
42072         "995"
42073       ],
42074       [
42075         "Germany (Deutschland)",
42076         "de",
42077         "49"
42078       ],
42079       [
42080         "Ghana (Gaana)",
42081         "gh",
42082         "233"
42083       ],
42084       [
42085         "Gibraltar",
42086         "gi",
42087         "350"
42088       ],
42089       [
42090         "Greece (Ελλάδα)",
42091         "gr",
42092         "30"
42093       ],
42094       [
42095         "Greenland (Kalaallit Nunaat)",
42096         "gl",
42097         "299"
42098       ],
42099       [
42100         "Grenada",
42101         "gd",
42102         "1473"
42103       ],
42104       [
42105         "Guadeloupe",
42106         "gp",
42107         "590",
42108         0
42109       ],
42110       [
42111         "Guam",
42112         "gu",
42113         "1671"
42114       ],
42115       [
42116         "Guatemala",
42117         "gt",
42118         "502"
42119       ],
42120       [
42121         "Guernsey",
42122         "gg",
42123         "44",
42124         1
42125       ],
42126       [
42127         "Guinea (Guinée)",
42128         "gn",
42129         "224"
42130       ],
42131       [
42132         "Guinea-Bissau (Guiné Bissau)",
42133         "gw",
42134         "245"
42135       ],
42136       [
42137         "Guyana",
42138         "gy",
42139         "592"
42140       ],
42141       [
42142         "Haiti",
42143         "ht",
42144         "509"
42145       ],
42146       [
42147         "Honduras",
42148         "hn",
42149         "504"
42150       ],
42151       [
42152         "Hong Kong (香港)",
42153         "hk",
42154         "852"
42155       ],
42156       [
42157         "Hungary (Magyarország)",
42158         "hu",
42159         "36"
42160       ],
42161       [
42162         "Iceland (Ísland)",
42163         "is",
42164         "354"
42165       ],
42166       [
42167         "India (भारत)",
42168         "in",
42169         "91"
42170       ],
42171       [
42172         "Indonesia",
42173         "id",
42174         "62"
42175       ],
42176       [
42177         "Iran (‫ایران‬‎)",
42178         "ir",
42179         "98"
42180       ],
42181       [
42182         "Iraq (‫العراق‬‎)",
42183         "iq",
42184         "964"
42185       ],
42186       [
42187         "Ireland",
42188         "ie",
42189         "353"
42190       ],
42191       [
42192         "Isle of Man",
42193         "im",
42194         "44",
42195         2
42196       ],
42197       [
42198         "Israel (‫ישראל‬‎)",
42199         "il",
42200         "972"
42201       ],
42202       [
42203         "Italy (Italia)",
42204         "it",
42205         "39",
42206         0
42207       ],
42208       [
42209         "Jamaica",
42210         "jm",
42211         "1876"
42212       ],
42213       [
42214         "Japan (日本)",
42215         "jp",
42216         "81"
42217       ],
42218       [
42219         "Jersey",
42220         "je",
42221         "44",
42222         3
42223       ],
42224       [
42225         "Jordan (‫الأردن‬‎)",
42226         "jo",
42227         "962"
42228       ],
42229       [
42230         "Kazakhstan (Казахстан)",
42231         "kz",
42232         "7",
42233         1
42234       ],
42235       [
42236         "Kenya",
42237         "ke",
42238         "254"
42239       ],
42240       [
42241         "Kiribati",
42242         "ki",
42243         "686"
42244       ],
42245       [
42246         "Kosovo",
42247         "xk",
42248         "383"
42249       ],
42250       [
42251         "Kuwait (‫الكويت‬‎)",
42252         "kw",
42253         "965"
42254       ],
42255       [
42256         "Kyrgyzstan (Кыргызстан)",
42257         "kg",
42258         "996"
42259       ],
42260       [
42261         "Laos (ລາວ)",
42262         "la",
42263         "856"
42264       ],
42265       [
42266         "Latvia (Latvija)",
42267         "lv",
42268         "371"
42269       ],
42270       [
42271         "Lebanon (‫لبنان‬‎)",
42272         "lb",
42273         "961"
42274       ],
42275       [
42276         "Lesotho",
42277         "ls",
42278         "266"
42279       ],
42280       [
42281         "Liberia",
42282         "lr",
42283         "231"
42284       ],
42285       [
42286         "Libya (‫ليبيا‬‎)",
42287         "ly",
42288         "218"
42289       ],
42290       [
42291         "Liechtenstein",
42292         "li",
42293         "423"
42294       ],
42295       [
42296         "Lithuania (Lietuva)",
42297         "lt",
42298         "370"
42299       ],
42300       [
42301         "Luxembourg",
42302         "lu",
42303         "352"
42304       ],
42305       [
42306         "Macau (澳門)",
42307         "mo",
42308         "853"
42309       ],
42310       [
42311         "Macedonia (FYROM) (Македонија)",
42312         "mk",
42313         "389"
42314       ],
42315       [
42316         "Madagascar (Madagasikara)",
42317         "mg",
42318         "261"
42319       ],
42320       [
42321         "Malawi",
42322         "mw",
42323         "265"
42324       ],
42325       [
42326         "Malaysia",
42327         "my",
42328         "60"
42329       ],
42330       [
42331         "Maldives",
42332         "mv",
42333         "960"
42334       ],
42335       [
42336         "Mali",
42337         "ml",
42338         "223"
42339       ],
42340       [
42341         "Malta",
42342         "mt",
42343         "356"
42344       ],
42345       [
42346         "Marshall Islands",
42347         "mh",
42348         "692"
42349       ],
42350       [
42351         "Martinique",
42352         "mq",
42353         "596"
42354       ],
42355       [
42356         "Mauritania (‫موريتانيا‬‎)",
42357         "mr",
42358         "222"
42359       ],
42360       [
42361         "Mauritius (Moris)",
42362         "mu",
42363         "230"
42364       ],
42365       [
42366         "Mayotte",
42367         "yt",
42368         "262",
42369         1
42370       ],
42371       [
42372         "Mexico (México)",
42373         "mx",
42374         "52"
42375       ],
42376       [
42377         "Micronesia",
42378         "fm",
42379         "691"
42380       ],
42381       [
42382         "Moldova (Republica Moldova)",
42383         "md",
42384         "373"
42385       ],
42386       [
42387         "Monaco",
42388         "mc",
42389         "377"
42390       ],
42391       [
42392         "Mongolia (Монгол)",
42393         "mn",
42394         "976"
42395       ],
42396       [
42397         "Montenegro (Crna Gora)",
42398         "me",
42399         "382"
42400       ],
42401       [
42402         "Montserrat",
42403         "ms",
42404         "1664"
42405       ],
42406       [
42407         "Morocco (‫المغرب‬‎)",
42408         "ma",
42409         "212",
42410         0
42411       ],
42412       [
42413         "Mozambique (Moçambique)",
42414         "mz",
42415         "258"
42416       ],
42417       [
42418         "Myanmar (Burma) (မြန်မာ)",
42419         "mm",
42420         "95"
42421       ],
42422       [
42423         "Namibia (Namibië)",
42424         "na",
42425         "264"
42426       ],
42427       [
42428         "Nauru",
42429         "nr",
42430         "674"
42431       ],
42432       [
42433         "Nepal (नेपाल)",
42434         "np",
42435         "977"
42436       ],
42437       [
42438         "Netherlands (Nederland)",
42439         "nl",
42440         "31"
42441       ],
42442       [
42443         "New Caledonia (Nouvelle-Calédonie)",
42444         "nc",
42445         "687"
42446       ],
42447       [
42448         "New Zealand",
42449         "nz",
42450         "64"
42451       ],
42452       [
42453         "Nicaragua",
42454         "ni",
42455         "505"
42456       ],
42457       [
42458         "Niger (Nijar)",
42459         "ne",
42460         "227"
42461       ],
42462       [
42463         "Nigeria",
42464         "ng",
42465         "234"
42466       ],
42467       [
42468         "Niue",
42469         "nu",
42470         "683"
42471       ],
42472       [
42473         "Norfolk Island",
42474         "nf",
42475         "672"
42476       ],
42477       [
42478         "North Korea (조선 민주주의 인민 공화국)",
42479         "kp",
42480         "850"
42481       ],
42482       [
42483         "Northern Mariana Islands",
42484         "mp",
42485         "1670"
42486       ],
42487       [
42488         "Norway (Norge)",
42489         "no",
42490         "47",
42491         0
42492       ],
42493       [
42494         "Oman (‫عُمان‬‎)",
42495         "om",
42496         "968"
42497       ],
42498       [
42499         "Pakistan (‫پاکستان‬‎)",
42500         "pk",
42501         "92"
42502       ],
42503       [
42504         "Palau",
42505         "pw",
42506         "680"
42507       ],
42508       [
42509         "Palestine (‫فلسطين‬‎)",
42510         "ps",
42511         "970"
42512       ],
42513       [
42514         "Panama (Panamá)",
42515         "pa",
42516         "507"
42517       ],
42518       [
42519         "Papua New Guinea",
42520         "pg",
42521         "675"
42522       ],
42523       [
42524         "Paraguay",
42525         "py",
42526         "595"
42527       ],
42528       [
42529         "Peru (Perú)",
42530         "pe",
42531         "51"
42532       ],
42533       [
42534         "Philippines",
42535         "ph",
42536         "63"
42537       ],
42538       [
42539         "Poland (Polska)",
42540         "pl",
42541         "48"
42542       ],
42543       [
42544         "Portugal",
42545         "pt",
42546         "351"
42547       ],
42548       [
42549         "Puerto Rico",
42550         "pr",
42551         "1",
42552         3,
42553         ["787", "939"]
42554       ],
42555       [
42556         "Qatar (‫قطر‬‎)",
42557         "qa",
42558         "974"
42559       ],
42560       [
42561         "Réunion (La Réunion)",
42562         "re",
42563         "262",
42564         0
42565       ],
42566       [
42567         "Romania (România)",
42568         "ro",
42569         "40"
42570       ],
42571       [
42572         "Russia (Россия)",
42573         "ru",
42574         "7",
42575         0
42576       ],
42577       [
42578         "Rwanda",
42579         "rw",
42580         "250"
42581       ],
42582       [
42583         "Saint Barthélemy",
42584         "bl",
42585         "590",
42586         1
42587       ],
42588       [
42589         "Saint Helena",
42590         "sh",
42591         "290"
42592       ],
42593       [
42594         "Saint Kitts and Nevis",
42595         "kn",
42596         "1869"
42597       ],
42598       [
42599         "Saint Lucia",
42600         "lc",
42601         "1758"
42602       ],
42603       [
42604         "Saint Martin (Saint-Martin (partie française))",
42605         "mf",
42606         "590",
42607         2
42608       ],
42609       [
42610         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42611         "pm",
42612         "508"
42613       ],
42614       [
42615         "Saint Vincent and the Grenadines",
42616         "vc",
42617         "1784"
42618       ],
42619       [
42620         "Samoa",
42621         "ws",
42622         "685"
42623       ],
42624       [
42625         "San Marino",
42626         "sm",
42627         "378"
42628       ],
42629       [
42630         "São Tomé and Príncipe (São Tomé e Príncipe)",
42631         "st",
42632         "239"
42633       ],
42634       [
42635         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42636         "sa",
42637         "966"
42638       ],
42639       [
42640         "Senegal (Sénégal)",
42641         "sn",
42642         "221"
42643       ],
42644       [
42645         "Serbia (Србија)",
42646         "rs",
42647         "381"
42648       ],
42649       [
42650         "Seychelles",
42651         "sc",
42652         "248"
42653       ],
42654       [
42655         "Sierra Leone",
42656         "sl",
42657         "232"
42658       ],
42659       [
42660         "Singapore",
42661         "sg",
42662         "65"
42663       ],
42664       [
42665         "Sint Maarten",
42666         "sx",
42667         "1721"
42668       ],
42669       [
42670         "Slovakia (Slovensko)",
42671         "sk",
42672         "421"
42673       ],
42674       [
42675         "Slovenia (Slovenija)",
42676         "si",
42677         "386"
42678       ],
42679       [
42680         "Solomon Islands",
42681         "sb",
42682         "677"
42683       ],
42684       [
42685         "Somalia (Soomaaliya)",
42686         "so",
42687         "252"
42688       ],
42689       [
42690         "South Africa",
42691         "za",
42692         "27"
42693       ],
42694       [
42695         "South Korea (대한민국)",
42696         "kr",
42697         "82"
42698       ],
42699       [
42700         "South Sudan (‫جنوب السودان‬‎)",
42701         "ss",
42702         "211"
42703       ],
42704       [
42705         "Spain (España)",
42706         "es",
42707         "34"
42708       ],
42709       [
42710         "Sri Lanka (ශ්‍රී ලංකාව)",
42711         "lk",
42712         "94"
42713       ],
42714       [
42715         "Sudan (‫السودان‬‎)",
42716         "sd",
42717         "249"
42718       ],
42719       [
42720         "Suriname",
42721         "sr",
42722         "597"
42723       ],
42724       [
42725         "Svalbard and Jan Mayen",
42726         "sj",
42727         "47",
42728         1
42729       ],
42730       [
42731         "Swaziland",
42732         "sz",
42733         "268"
42734       ],
42735       [
42736         "Sweden (Sverige)",
42737         "se",
42738         "46"
42739       ],
42740       [
42741         "Switzerland (Schweiz)",
42742         "ch",
42743         "41"
42744       ],
42745       [
42746         "Syria (‫سوريا‬‎)",
42747         "sy",
42748         "963"
42749       ],
42750       [
42751         "Taiwan (台灣)",
42752         "tw",
42753         "886"
42754       ],
42755       [
42756         "Tajikistan",
42757         "tj",
42758         "992"
42759       ],
42760       [
42761         "Tanzania",
42762         "tz",
42763         "255"
42764       ],
42765       [
42766         "Thailand (ไทย)",
42767         "th",
42768         "66"
42769       ],
42770       [
42771         "Timor-Leste",
42772         "tl",
42773         "670"
42774       ],
42775       [
42776         "Togo",
42777         "tg",
42778         "228"
42779       ],
42780       [
42781         "Tokelau",
42782         "tk",
42783         "690"
42784       ],
42785       [
42786         "Tonga",
42787         "to",
42788         "676"
42789       ],
42790       [
42791         "Trinidad and Tobago",
42792         "tt",
42793         "1868"
42794       ],
42795       [
42796         "Tunisia (‫تونس‬‎)",
42797         "tn",
42798         "216"
42799       ],
42800       [
42801         "Turkey (Türkiye)",
42802         "tr",
42803         "90"
42804       ],
42805       [
42806         "Turkmenistan",
42807         "tm",
42808         "993"
42809       ],
42810       [
42811         "Turks and Caicos Islands",
42812         "tc",
42813         "1649"
42814       ],
42815       [
42816         "Tuvalu",
42817         "tv",
42818         "688"
42819       ],
42820       [
42821         "U.S. Virgin Islands",
42822         "vi",
42823         "1340"
42824       ],
42825       [
42826         "Uganda",
42827         "ug",
42828         "256"
42829       ],
42830       [
42831         "Ukraine (Україна)",
42832         "ua",
42833         "380"
42834       ],
42835       [
42836         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42837         "ae",
42838         "971"
42839       ],
42840       [
42841         "United Kingdom",
42842         "gb",
42843         "44",
42844         0
42845       ],
42846       [
42847         "United States",
42848         "us",
42849         "1",
42850         0
42851       ],
42852       [
42853         "Uruguay",
42854         "uy",
42855         "598"
42856       ],
42857       [
42858         "Uzbekistan (Oʻzbekiston)",
42859         "uz",
42860         "998"
42861       ],
42862       [
42863         "Vanuatu",
42864         "vu",
42865         "678"
42866       ],
42867       [
42868         "Vatican City (Città del Vaticano)",
42869         "va",
42870         "39",
42871         1
42872       ],
42873       [
42874         "Venezuela",
42875         "ve",
42876         "58"
42877       ],
42878       [
42879         "Vietnam (Việt Nam)",
42880         "vn",
42881         "84"
42882       ],
42883       [
42884         "Wallis and Futuna (Wallis-et-Futuna)",
42885         "wf",
42886         "681"
42887       ],
42888       [
42889         "Western Sahara (‫الصحراء الغربية‬‎)",
42890         "eh",
42891         "212",
42892         1
42893       ],
42894       [
42895         "Yemen (‫اليمن‬‎)",
42896         "ye",
42897         "967"
42898       ],
42899       [
42900         "Zambia",
42901         "zm",
42902         "260"
42903       ],
42904       [
42905         "Zimbabwe",
42906         "zw",
42907         "263"
42908       ],
42909       [
42910         "Åland Islands",
42911         "ax",
42912         "358",
42913         1
42914       ]
42915   ];
42916   
42917   return d;
42918 }/**
42919 *    This script refer to:
42920 *    Title: International Telephone Input
42921 *    Author: Jack O'Connor
42922 *    Code version:  v12.1.12
42923 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42924 **/
42925
42926 /**
42927  * @class Roo.bootstrap.PhoneInput
42928  * @extends Roo.bootstrap.TriggerField
42929  * An input with International dial-code selection
42930  
42931  * @cfg {String} defaultDialCode default '+852'
42932  * @cfg {Array} preferedCountries default []
42933   
42934  * @constructor
42935  * Create a new PhoneInput.
42936  * @param {Object} config Configuration options
42937  */
42938
42939 Roo.bootstrap.PhoneInput = function(config) {
42940     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42941 };
42942
42943 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42944         
42945         listWidth: undefined,
42946         
42947         selectedClass: 'active',
42948         
42949         invalidClass : "has-warning",
42950         
42951         validClass: 'has-success',
42952         
42953         allowed: '0123456789',
42954         
42955         max_length: 15,
42956         
42957         /**
42958          * @cfg {String} defaultDialCode The default dial code when initializing the input
42959          */
42960         defaultDialCode: '+852',
42961         
42962         /**
42963          * @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
42964          */
42965         preferedCountries: false,
42966         
42967         getAutoCreate : function()
42968         {
42969             var data = Roo.bootstrap.PhoneInputData();
42970             var align = this.labelAlign || this.parentLabelAlign();
42971             var id = Roo.id();
42972             
42973             this.allCountries = [];
42974             this.dialCodeMapping = [];
42975             
42976             for (var i = 0; i < data.length; i++) {
42977               var c = data[i];
42978               this.allCountries[i] = {
42979                 name: c[0],
42980                 iso2: c[1],
42981                 dialCode: c[2],
42982                 priority: c[3] || 0,
42983                 areaCodes: c[4] || null
42984               };
42985               this.dialCodeMapping[c[2]] = {
42986                   name: c[0],
42987                   iso2: c[1],
42988                   priority: c[3] || 0,
42989                   areaCodes: c[4] || null
42990               };
42991             }
42992             
42993             var cfg = {
42994                 cls: 'form-group',
42995                 cn: []
42996             };
42997             
42998             var input =  {
42999                 tag: 'input',
43000                 id : id,
43001                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43002                 maxlength: this.max_length,
43003                 cls : 'form-control tel-input',
43004                 autocomplete: 'new-password'
43005             };
43006             
43007             var hiddenInput = {
43008                 tag: 'input',
43009                 type: 'hidden',
43010                 cls: 'hidden-tel-input'
43011             };
43012             
43013             if (this.name) {
43014                 hiddenInput.name = this.name;
43015             }
43016             
43017             if (this.disabled) {
43018                 input.disabled = true;
43019             }
43020             
43021             var flag_container = {
43022                 tag: 'div',
43023                 cls: 'flag-box',
43024                 cn: [
43025                     {
43026                         tag: 'div',
43027                         cls: 'flag'
43028                     },
43029                     {
43030                         tag: 'div',
43031                         cls: 'caret'
43032                     }
43033                 ]
43034             };
43035             
43036             var box = {
43037                 tag: 'div',
43038                 cls: this.hasFeedback ? 'has-feedback' : '',
43039                 cn: [
43040                     hiddenInput,
43041                     input,
43042                     {
43043                         tag: 'input',
43044                         cls: 'dial-code-holder',
43045                         disabled: true
43046                     }
43047                 ]
43048             };
43049             
43050             var container = {
43051                 cls: 'roo-select2-container input-group',
43052                 cn: [
43053                     flag_container,
43054                     box
43055                 ]
43056             };
43057             
43058             if (this.fieldLabel.length) {
43059                 var indicator = {
43060                     tag: 'i',
43061                     tooltip: 'This field is required'
43062                 };
43063                 
43064                 var label = {
43065                     tag: 'label',
43066                     'for':  id,
43067                     cls: 'control-label',
43068                     cn: []
43069                 };
43070                 
43071                 var label_text = {
43072                     tag: 'span',
43073                     html: this.fieldLabel
43074                 };
43075                 
43076                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43077                 label.cn = [
43078                     indicator,
43079                     label_text
43080                 ];
43081                 
43082                 if(this.indicatorpos == 'right') {
43083                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43084                     label.cn = [
43085                         label_text,
43086                         indicator
43087                     ];
43088                 }
43089                 
43090                 if(align == 'left') {
43091                     container = {
43092                         tag: 'div',
43093                         cn: [
43094                             container
43095                         ]
43096                     };
43097                     
43098                     if(this.labelWidth > 12){
43099                         label.style = "width: " + this.labelWidth + 'px';
43100                     }
43101                     if(this.labelWidth < 13 && this.labelmd == 0){
43102                         this.labelmd = this.labelWidth;
43103                     }
43104                     if(this.labellg > 0){
43105                         label.cls += ' col-lg-' + this.labellg;
43106                         input.cls += ' col-lg-' + (12 - this.labellg);
43107                     }
43108                     if(this.labelmd > 0){
43109                         label.cls += ' col-md-' + this.labelmd;
43110                         container.cls += ' col-md-' + (12 - this.labelmd);
43111                     }
43112                     if(this.labelsm > 0){
43113                         label.cls += ' col-sm-' + this.labelsm;
43114                         container.cls += ' col-sm-' + (12 - this.labelsm);
43115                     }
43116                     if(this.labelxs > 0){
43117                         label.cls += ' col-xs-' + this.labelxs;
43118                         container.cls += ' col-xs-' + (12 - this.labelxs);
43119                     }
43120                 }
43121             }
43122             
43123             cfg.cn = [
43124                 label,
43125                 container
43126             ];
43127             
43128             var settings = this;
43129             
43130             ['xs','sm','md','lg'].map(function(size){
43131                 if (settings[size]) {
43132                     cfg.cls += ' col-' + size + '-' + settings[size];
43133                 }
43134             });
43135             
43136             this.store = new Roo.data.Store({
43137                 proxy : new Roo.data.MemoryProxy({}),
43138                 reader : new Roo.data.JsonReader({
43139                     fields : [
43140                         {
43141                             'name' : 'name',
43142                             'type' : 'string'
43143                         },
43144                         {
43145                             'name' : 'iso2',
43146                             'type' : 'string'
43147                         },
43148                         {
43149                             'name' : 'dialCode',
43150                             'type' : 'string'
43151                         },
43152                         {
43153                             'name' : 'priority',
43154                             'type' : 'string'
43155                         },
43156                         {
43157                             'name' : 'areaCodes',
43158                             'type' : 'string'
43159                         }
43160                     ]
43161                 })
43162             });
43163             
43164             if(!this.preferedCountries) {
43165                 this.preferedCountries = [
43166                     'hk',
43167                     'gb',
43168                     'us'
43169                 ];
43170             }
43171             
43172             var p = this.preferedCountries.reverse();
43173             
43174             if(p) {
43175                 for (var i = 0; i < p.length; i++) {
43176                     for (var j = 0; j < this.allCountries.length; j++) {
43177                         if(this.allCountries[j].iso2 == p[i]) {
43178                             var t = this.allCountries[j];
43179                             this.allCountries.splice(j,1);
43180                             this.allCountries.unshift(t);
43181                         }
43182                     } 
43183                 }
43184             }
43185             
43186             this.store.proxy.data = {
43187                 success: true,
43188                 data: this.allCountries
43189             };
43190             
43191             return cfg;
43192         },
43193         
43194         initEvents : function()
43195         {
43196             this.createList();
43197             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43198             
43199             this.indicator = this.indicatorEl();
43200             this.flag = this.flagEl();
43201             this.dialCodeHolder = this.dialCodeHolderEl();
43202             
43203             this.trigger = this.el.select('div.flag-box',true).first();
43204             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43205             
43206             var _this = this;
43207             
43208             (function(){
43209                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43210                 _this.list.setWidth(lw);
43211             }).defer(100);
43212             
43213             this.list.on('mouseover', this.onViewOver, this);
43214             this.list.on('mousemove', this.onViewMove, this);
43215             this.inputEl().on("keyup", this.onKeyUp, this);
43216             this.inputEl().on("keypress", this.onKeyPress, this);
43217             
43218             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43219
43220             this.view = new Roo.View(this.list, this.tpl, {
43221                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43222             });
43223             
43224             this.view.on('click', this.onViewClick, this);
43225             this.setValue(this.defaultDialCode);
43226         },
43227         
43228         onTriggerClick : function(e)
43229         {
43230             Roo.log('trigger click');
43231             if(this.disabled){
43232                 return;
43233             }
43234             
43235             if(this.isExpanded()){
43236                 this.collapse();
43237                 this.hasFocus = false;
43238             }else {
43239                 this.store.load({});
43240                 this.hasFocus = true;
43241                 this.expand();
43242             }
43243         },
43244         
43245         isExpanded : function()
43246         {
43247             return this.list.isVisible();
43248         },
43249         
43250         collapse : function()
43251         {
43252             if(!this.isExpanded()){
43253                 return;
43254             }
43255             this.list.hide();
43256             Roo.get(document).un('mousedown', this.collapseIf, this);
43257             Roo.get(document).un('mousewheel', this.collapseIf, this);
43258             this.fireEvent('collapse', this);
43259             this.validate();
43260         },
43261         
43262         expand : function()
43263         {
43264             Roo.log('expand');
43265
43266             if(this.isExpanded() || !this.hasFocus){
43267                 return;
43268             }
43269             
43270             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43271             this.list.setWidth(lw);
43272             
43273             this.list.show();
43274             this.restrictHeight();
43275             
43276             Roo.get(document).on('mousedown', this.collapseIf, this);
43277             Roo.get(document).on('mousewheel', this.collapseIf, this);
43278             
43279             this.fireEvent('expand', this);
43280         },
43281         
43282         restrictHeight : function()
43283         {
43284             this.list.alignTo(this.inputEl(), this.listAlign);
43285             this.list.alignTo(this.inputEl(), this.listAlign);
43286         },
43287         
43288         onViewOver : function(e, t)
43289         {
43290             if(this.inKeyMode){
43291                 return;
43292             }
43293             var item = this.view.findItemFromChild(t);
43294             
43295             if(item){
43296                 var index = this.view.indexOf(item);
43297                 this.select(index, false);
43298             }
43299         },
43300
43301         // private
43302         onViewClick : function(view, doFocus, el, e)
43303         {
43304             var index = this.view.getSelectedIndexes()[0];
43305             
43306             var r = this.store.getAt(index);
43307             
43308             if(r){
43309                 this.onSelect(r, index);
43310             }
43311             if(doFocus !== false && !this.blockFocus){
43312                 this.inputEl().focus();
43313             }
43314         },
43315         
43316         onViewMove : function(e, t)
43317         {
43318             this.inKeyMode = false;
43319         },
43320         
43321         select : function(index, scrollIntoView)
43322         {
43323             this.selectedIndex = index;
43324             this.view.select(index);
43325             if(scrollIntoView !== false){
43326                 var el = this.view.getNode(index);
43327                 if(el){
43328                     this.list.scrollChildIntoView(el, false);
43329                 }
43330             }
43331         },
43332         
43333         createList : function()
43334         {
43335             this.list = Roo.get(document.body).createChild({
43336                 tag: 'ul',
43337                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43338                 style: 'display:none'
43339             });
43340             
43341             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43342         },
43343         
43344         collapseIf : function(e)
43345         {
43346             var in_combo  = e.within(this.el);
43347             var in_list =  e.within(this.list);
43348             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43349             
43350             if (in_combo || in_list || is_list) {
43351                 return;
43352             }
43353             this.collapse();
43354         },
43355         
43356         onSelect : function(record, index)
43357         {
43358             if(this.fireEvent('beforeselect', this, record, index) !== false){
43359                 
43360                 this.setFlagClass(record.data.iso2);
43361                 this.setDialCode(record.data.dialCode);
43362                 this.hasFocus = false;
43363                 this.collapse();
43364                 this.fireEvent('select', this, record, index);
43365             }
43366         },
43367         
43368         flagEl : function()
43369         {
43370             var flag = this.el.select('div.flag',true).first();
43371             if(!flag){
43372                 return false;
43373             }
43374             return flag;
43375         },
43376         
43377         dialCodeHolderEl : function()
43378         {
43379             var d = this.el.select('input.dial-code-holder',true).first();
43380             if(!d){
43381                 return false;
43382             }
43383             return d;
43384         },
43385         
43386         setDialCode : function(v)
43387         {
43388             this.dialCodeHolder.dom.value = '+'+v;
43389         },
43390         
43391         setFlagClass : function(n)
43392         {
43393             this.flag.dom.className = 'flag '+n;
43394         },
43395         
43396         getValue : function()
43397         {
43398             var v = this.inputEl().getValue();
43399             if(this.dialCodeHolder) {
43400                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43401             }
43402             return v;
43403         },
43404         
43405         setValue : function(v)
43406         {
43407             var d = this.getDialCode(v);
43408             
43409             //invalid dial code
43410             if(v.length == 0 || !d || d.length == 0) {
43411                 if(this.rendered){
43412                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43413                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43414                 }
43415                 return;
43416             }
43417             
43418             //valid dial code
43419             this.setFlagClass(this.dialCodeMapping[d].iso2);
43420             this.setDialCode(d);
43421             this.inputEl().dom.value = v.replace('+'+d,'');
43422             this.hiddenEl().dom.value = this.getValue();
43423             
43424             this.validate();
43425         },
43426         
43427         getDialCode : function(v)
43428         {
43429             v = v ||  '';
43430             
43431             if (v.length == 0) {
43432                 return this.dialCodeHolder.dom.value;
43433             }
43434             
43435             var dialCode = "";
43436             if (v.charAt(0) != "+") {
43437                 return false;
43438             }
43439             var numericChars = "";
43440             for (var i = 1; i < v.length; i++) {
43441               var c = v.charAt(i);
43442               if (!isNaN(c)) {
43443                 numericChars += c;
43444                 if (this.dialCodeMapping[numericChars]) {
43445                   dialCode = v.substr(1, i);
43446                 }
43447                 if (numericChars.length == 4) {
43448                   break;
43449                 }
43450               }
43451             }
43452             return dialCode;
43453         },
43454         
43455         reset : function()
43456         {
43457             this.setValue(this.defaultDialCode);
43458             this.validate();
43459         },
43460         
43461         hiddenEl : function()
43462         {
43463             return this.el.select('input.hidden-tel-input',true).first();
43464         },
43465         
43466         // after setting val
43467         onKeyUp : function(e){
43468             this.setValue(this.getValue());
43469         },
43470         
43471         onKeyPress : function(e){
43472             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43473                 e.stopEvent();
43474             }
43475         }
43476         
43477 });
43478 /**
43479  * @class Roo.bootstrap.MoneyField
43480  * @extends Roo.bootstrap.ComboBox
43481  * Bootstrap MoneyField class
43482  * 
43483  * @constructor
43484  * Create a new MoneyField.
43485  * @param {Object} config Configuration options
43486  */
43487
43488 Roo.bootstrap.MoneyField = function(config) {
43489     
43490     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43491     
43492 };
43493
43494 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43495     
43496     /**
43497      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43498      */
43499     allowDecimals : true,
43500     /**
43501      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43502      */
43503     decimalSeparator : ".",
43504     /**
43505      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43506      */
43507     decimalPrecision : 0,
43508     /**
43509      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43510      */
43511     allowNegative : true,
43512     /**
43513      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43514      */
43515     allowZero: true,
43516     /**
43517      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43518      */
43519     minValue : Number.NEGATIVE_INFINITY,
43520     /**
43521      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43522      */
43523     maxValue : Number.MAX_VALUE,
43524     /**
43525      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43526      */
43527     minText : "The minimum value for this field is {0}",
43528     /**
43529      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43530      */
43531     maxText : "The maximum value for this field is {0}",
43532     /**
43533      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43534      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43535      */
43536     nanText : "{0} is not a valid number",
43537     /**
43538      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43539      */
43540     castInt : true,
43541     /**
43542      * @cfg {String} defaults currency of the MoneyField
43543      * value should be in lkey
43544      */
43545     defaultCurrency : false,
43546     /**
43547      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43548      */
43549     thousandsDelimiter : false,
43550     /**
43551      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43552      */
43553     max_length: false,
43554     
43555     inputlg : 9,
43556     inputmd : 9,
43557     inputsm : 9,
43558     inputxs : 6,
43559     
43560     store : false,
43561     
43562     getAutoCreate : function()
43563     {
43564         var align = this.labelAlign || this.parentLabelAlign();
43565         
43566         var id = Roo.id();
43567
43568         var cfg = {
43569             cls: 'form-group',
43570             cn: []
43571         };
43572
43573         var input =  {
43574             tag: 'input',
43575             id : id,
43576             cls : 'form-control roo-money-amount-input',
43577             autocomplete: 'new-password'
43578         };
43579         
43580         var hiddenInput = {
43581             tag: 'input',
43582             type: 'hidden',
43583             id: Roo.id(),
43584             cls: 'hidden-number-input'
43585         };
43586         
43587         if(this.max_length) {
43588             input.maxlength = this.max_length; 
43589         }
43590         
43591         if (this.name) {
43592             hiddenInput.name = this.name;
43593         }
43594
43595         if (this.disabled) {
43596             input.disabled = true;
43597         }
43598
43599         var clg = 12 - this.inputlg;
43600         var cmd = 12 - this.inputmd;
43601         var csm = 12 - this.inputsm;
43602         var cxs = 12 - this.inputxs;
43603         
43604         var container = {
43605             tag : 'div',
43606             cls : 'row roo-money-field',
43607             cn : [
43608                 {
43609                     tag : 'div',
43610                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43611                     cn : [
43612                         {
43613                             tag : 'div',
43614                             cls: 'roo-select2-container input-group',
43615                             cn: [
43616                                 {
43617                                     tag : 'input',
43618                                     cls : 'form-control roo-money-currency-input',
43619                                     autocomplete: 'new-password',
43620                                     readOnly : 1,
43621                                     name : this.currencyName
43622                                 },
43623                                 {
43624                                     tag :'span',
43625                                     cls : 'input-group-addon',
43626                                     cn : [
43627                                         {
43628                                             tag: 'span',
43629                                             cls: 'caret'
43630                                         }
43631                                     ]
43632                                 }
43633                             ]
43634                         }
43635                     ]
43636                 },
43637                 {
43638                     tag : 'div',
43639                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43640                     cn : [
43641                         {
43642                             tag: 'div',
43643                             cls: this.hasFeedback ? 'has-feedback' : '',
43644                             cn: [
43645                                 input
43646                             ]
43647                         }
43648                     ]
43649                 }
43650             ]
43651             
43652         };
43653         
43654         if (this.fieldLabel.length) {
43655             var indicator = {
43656                 tag: 'i',
43657                 tooltip: 'This field is required'
43658             };
43659
43660             var label = {
43661                 tag: 'label',
43662                 'for':  id,
43663                 cls: 'control-label',
43664                 cn: []
43665             };
43666
43667             var label_text = {
43668                 tag: 'span',
43669                 html: this.fieldLabel
43670             };
43671
43672             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43673             label.cn = [
43674                 indicator,
43675                 label_text
43676             ];
43677
43678             if(this.indicatorpos == 'right') {
43679                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43680                 label.cn = [
43681                     label_text,
43682                     indicator
43683                 ];
43684             }
43685
43686             if(align == 'left') {
43687                 container = {
43688                     tag: 'div',
43689                     cn: [
43690                         container
43691                     ]
43692                 };
43693
43694                 if(this.labelWidth > 12){
43695                     label.style = "width: " + this.labelWidth + 'px';
43696                 }
43697                 if(this.labelWidth < 13 && this.labelmd == 0){
43698                     this.labelmd = this.labelWidth;
43699                 }
43700                 if(this.labellg > 0){
43701                     label.cls += ' col-lg-' + this.labellg;
43702                     input.cls += ' col-lg-' + (12 - this.labellg);
43703                 }
43704                 if(this.labelmd > 0){
43705                     label.cls += ' col-md-' + this.labelmd;
43706                     container.cls += ' col-md-' + (12 - this.labelmd);
43707                 }
43708                 if(this.labelsm > 0){
43709                     label.cls += ' col-sm-' + this.labelsm;
43710                     container.cls += ' col-sm-' + (12 - this.labelsm);
43711                 }
43712                 if(this.labelxs > 0){
43713                     label.cls += ' col-xs-' + this.labelxs;
43714                     container.cls += ' col-xs-' + (12 - this.labelxs);
43715                 }
43716             }
43717         }
43718
43719         cfg.cn = [
43720             label,
43721             container,
43722             hiddenInput
43723         ];
43724         
43725         var settings = this;
43726
43727         ['xs','sm','md','lg'].map(function(size){
43728             if (settings[size]) {
43729                 cfg.cls += ' col-' + size + '-' + settings[size];
43730             }
43731         });
43732         
43733         return cfg;
43734     },
43735     
43736     initEvents : function()
43737     {
43738         this.indicator = this.indicatorEl();
43739         
43740         this.initCurrencyEvent();
43741         
43742         this.initNumberEvent();
43743     },
43744     
43745     initCurrencyEvent : function()
43746     {
43747         if (!this.store) {
43748             throw "can not find store for combo";
43749         }
43750         
43751         this.store = Roo.factory(this.store, Roo.data);
43752         this.store.parent = this;
43753         
43754         this.createList();
43755         
43756         this.triggerEl = this.el.select('.input-group-addon', true).first();
43757         
43758         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43759         
43760         var _this = this;
43761         
43762         (function(){
43763             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43764             _this.list.setWidth(lw);
43765         }).defer(100);
43766         
43767         this.list.on('mouseover', this.onViewOver, this);
43768         this.list.on('mousemove', this.onViewMove, this);
43769         this.list.on('scroll', this.onViewScroll, this);
43770         
43771         if(!this.tpl){
43772             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43773         }
43774         
43775         this.view = new Roo.View(this.list, this.tpl, {
43776             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43777         });
43778         
43779         this.view.on('click', this.onViewClick, this);
43780         
43781         this.store.on('beforeload', this.onBeforeLoad, this);
43782         this.store.on('load', this.onLoad, this);
43783         this.store.on('loadexception', this.onLoadException, this);
43784         
43785         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43786             "up" : function(e){
43787                 this.inKeyMode = true;
43788                 this.selectPrev();
43789             },
43790
43791             "down" : function(e){
43792                 if(!this.isExpanded()){
43793                     this.onTriggerClick();
43794                 }else{
43795                     this.inKeyMode = true;
43796                     this.selectNext();
43797                 }
43798             },
43799
43800             "enter" : function(e){
43801                 this.collapse();
43802                 
43803                 if(this.fireEvent("specialkey", this, e)){
43804                     this.onViewClick(false);
43805                 }
43806                 
43807                 return true;
43808             },
43809
43810             "esc" : function(e){
43811                 this.collapse();
43812             },
43813
43814             "tab" : function(e){
43815                 this.collapse();
43816                 
43817                 if(this.fireEvent("specialkey", this, e)){
43818                     this.onViewClick(false);
43819                 }
43820                 
43821                 return true;
43822             },
43823
43824             scope : this,
43825
43826             doRelay : function(foo, bar, hname){
43827                 if(hname == 'down' || this.scope.isExpanded()){
43828                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43829                 }
43830                 return true;
43831             },
43832
43833             forceKeyDown: true
43834         });
43835         
43836         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43837         
43838     },
43839     
43840     initNumberEvent : function(e)
43841     {
43842         this.inputEl().on("keydown" , this.fireKey,  this);
43843         this.inputEl().on("focus", this.onFocus,  this);
43844         this.inputEl().on("blur", this.onBlur,  this);
43845         
43846         this.inputEl().relayEvent('keyup', this);
43847         
43848         if(this.indicator){
43849             this.indicator.addClass('invisible');
43850         }
43851  
43852         this.originalValue = this.getValue();
43853         
43854         if(this.validationEvent == 'keyup'){
43855             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43856             this.inputEl().on('keyup', this.filterValidation, this);
43857         }
43858         else if(this.validationEvent !== false){
43859             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43860         }
43861         
43862         if(this.selectOnFocus){
43863             this.on("focus", this.preFocus, this);
43864             
43865         }
43866         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43867             this.inputEl().on("keypress", this.filterKeys, this);
43868         } else {
43869             this.inputEl().relayEvent('keypress', this);
43870         }
43871         
43872         var allowed = "0123456789";
43873         
43874         if(this.allowDecimals){
43875             allowed += this.decimalSeparator;
43876         }
43877         
43878         if(this.allowNegative){
43879             allowed += "-";
43880         }
43881         
43882         if(this.thousandsDelimiter) {
43883             allowed += ",";
43884         }
43885         
43886         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43887         
43888         var keyPress = function(e){
43889             
43890             var k = e.getKey();
43891             
43892             var c = e.getCharCode();
43893             
43894             if(
43895                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43896                     allowed.indexOf(String.fromCharCode(c)) === -1
43897             ){
43898                 e.stopEvent();
43899                 return;
43900             }
43901             
43902             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43903                 return;
43904             }
43905             
43906             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43907                 e.stopEvent();
43908             }
43909         };
43910         
43911         this.inputEl().on("keypress", keyPress, this);
43912         
43913     },
43914     
43915     onTriggerClick : function(e)
43916     {   
43917         if(this.disabled){
43918             return;
43919         }
43920         
43921         this.page = 0;
43922         this.loadNext = false;
43923         
43924         if(this.isExpanded()){
43925             this.collapse();
43926             return;
43927         }
43928         
43929         this.hasFocus = true;
43930         
43931         if(this.triggerAction == 'all') {
43932             this.doQuery(this.allQuery, true);
43933             return;
43934         }
43935         
43936         this.doQuery(this.getRawValue());
43937     },
43938     
43939     getCurrency : function()
43940     {   
43941         var v = this.currencyEl().getValue();
43942         
43943         return v;
43944     },
43945     
43946     restrictHeight : function()
43947     {
43948         this.list.alignTo(this.currencyEl(), this.listAlign);
43949         this.list.alignTo(this.currencyEl(), this.listAlign);
43950     },
43951     
43952     onViewClick : function(view, doFocus, el, e)
43953     {
43954         var index = this.view.getSelectedIndexes()[0];
43955         
43956         var r = this.store.getAt(index);
43957         
43958         if(r){
43959             this.onSelect(r, index);
43960         }
43961     },
43962     
43963     onSelect : function(record, index){
43964         
43965         if(this.fireEvent('beforeselect', this, record, index) !== false){
43966         
43967             this.setFromCurrencyData(index > -1 ? record.data : false);
43968             
43969             this.collapse();
43970             
43971             this.fireEvent('select', this, record, index);
43972         }
43973     },
43974     
43975     setFromCurrencyData : function(o)
43976     {
43977         var currency = '';
43978         
43979         this.lastCurrency = o;
43980         
43981         if (this.currencyField) {
43982             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43983         } else {
43984             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43985         }
43986         
43987         this.lastSelectionText = currency;
43988         
43989         //setting default currency
43990         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43991             this.setCurrency(this.defaultCurrency);
43992             return;
43993         }
43994         
43995         this.setCurrency(currency);
43996     },
43997     
43998     setFromData : function(o)
43999     {
44000         var c = {};
44001         
44002         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44003         
44004         this.setFromCurrencyData(c);
44005         
44006         var value = '';
44007         
44008         if (this.name) {
44009             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44010         } else {
44011             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44012         }
44013         
44014         this.setValue(value);
44015         
44016     },
44017     
44018     setCurrency : function(v)
44019     {   
44020         this.currencyValue = v;
44021         
44022         if(this.rendered){
44023             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44024             this.validate();
44025         }
44026     },
44027     
44028     setValue : function(v)
44029     {
44030         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44031         
44032         this.value = v;
44033         
44034         if(this.rendered){
44035             
44036             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44037             
44038             this.inputEl().dom.value = (v == '') ? '' :
44039                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44040             
44041             if(!this.allowZero && v === '0') {
44042                 this.hiddenEl().dom.value = '';
44043                 this.inputEl().dom.value = '';
44044             }
44045             
44046             this.validate();
44047         }
44048     },
44049     
44050     getRawValue : function()
44051     {
44052         var v = this.inputEl().getValue();
44053         
44054         return v;
44055     },
44056     
44057     getValue : function()
44058     {
44059         return this.fixPrecision(this.parseValue(this.getRawValue()));
44060     },
44061     
44062     parseValue : function(value)
44063     {
44064         if(this.thousandsDelimiter) {
44065             value += "";
44066             r = new RegExp(",", "g");
44067             value = value.replace(r, "");
44068         }
44069         
44070         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44071         return isNaN(value) ? '' : value;
44072         
44073     },
44074     
44075     fixPrecision : function(value)
44076     {
44077         if(this.thousandsDelimiter) {
44078             value += "";
44079             r = new RegExp(",", "g");
44080             value = value.replace(r, "");
44081         }
44082         
44083         var nan = isNaN(value);
44084         
44085         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44086             return nan ? '' : value;
44087         }
44088         return parseFloat(value).toFixed(this.decimalPrecision);
44089     },
44090     
44091     decimalPrecisionFcn : function(v)
44092     {
44093         return Math.floor(v);
44094     },
44095     
44096     validateValue : function(value)
44097     {
44098         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44099             return false;
44100         }
44101         
44102         var num = this.parseValue(value);
44103         
44104         if(isNaN(num)){
44105             this.markInvalid(String.format(this.nanText, value));
44106             return false;
44107         }
44108         
44109         if(num < this.minValue){
44110             this.markInvalid(String.format(this.minText, this.minValue));
44111             return false;
44112         }
44113         
44114         if(num > this.maxValue){
44115             this.markInvalid(String.format(this.maxText, this.maxValue));
44116             return false;
44117         }
44118         
44119         return true;
44120     },
44121     
44122     validate : function()
44123     {
44124         if(this.disabled || this.allowBlank){
44125             this.markValid();
44126             return true;
44127         }
44128         
44129         var currency = this.getCurrency();
44130         
44131         if(this.validateValue(this.getRawValue()) && currency.length){
44132             this.markValid();
44133             return true;
44134         }
44135         
44136         this.markInvalid();
44137         return false;
44138     },
44139     
44140     getName: function()
44141     {
44142         return this.name;
44143     },
44144     
44145     beforeBlur : function()
44146     {
44147         if(!this.castInt){
44148             return;
44149         }
44150         
44151         var v = this.parseValue(this.getRawValue());
44152         
44153         if(v || v == 0){
44154             this.setValue(v);
44155         }
44156     },
44157     
44158     onBlur : function()
44159     {
44160         this.beforeBlur();
44161         
44162         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44163             //this.el.removeClass(this.focusClass);
44164         }
44165         
44166         this.hasFocus = false;
44167         
44168         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44169             this.validate();
44170         }
44171         
44172         var v = this.getValue();
44173         
44174         if(String(v) !== String(this.startValue)){
44175             this.fireEvent('change', this, v, this.startValue);
44176         }
44177         
44178         this.fireEvent("blur", this);
44179     },
44180     
44181     inputEl : function()
44182     {
44183         return this.el.select('.roo-money-amount-input', true).first();
44184     },
44185     
44186     currencyEl : function()
44187     {
44188         return this.el.select('.roo-money-currency-input', true).first();
44189     },
44190     
44191     hiddenEl : function()
44192     {
44193         return this.el.select('input.hidden-number-input',true).first();
44194     }
44195     
44196 });/**
44197  * @class Roo.bootstrap.BezierSignature
44198  * @extends Roo.bootstrap.Component
44199  * Bootstrap BezierSignature class
44200  * This script refer to:
44201  *    Title: Signature Pad
44202  *    Author: szimek
44203  *    Availability: https://github.com/szimek/signature_pad
44204  *
44205  * @constructor
44206  * Create a new BezierSignature
44207  * @param {Object} config The config object
44208  */
44209
44210 Roo.bootstrap.BezierSignature = function(config){
44211     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44212     this.addEvents({
44213         "resize" : true
44214     });
44215 };
44216
44217 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44218 {
44219      
44220     curve_data: [],
44221     
44222     is_empty: true,
44223     
44224     mouse_btn_down: true,
44225     
44226     /**
44227      * @cfg {int} canvas height
44228      */
44229     canvas_height: '200px',
44230     
44231     /**
44232      * @cfg {float|function} Radius of a single dot.
44233      */ 
44234     dot_size: false,
44235     
44236     /**
44237      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44238      */
44239     min_width: 0.5,
44240     
44241     /**
44242      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44243      */
44244     max_width: 2.5,
44245     
44246     /**
44247      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44248      */
44249     throttle: 16,
44250     
44251     /**
44252      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44253      */
44254     min_distance: 5,
44255     
44256     /**
44257      * @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.
44258      */
44259     bg_color: 'rgba(0, 0, 0, 0)',
44260     
44261     /**
44262      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44263      */
44264     dot_color: 'black',
44265     
44266     /**
44267      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44268      */ 
44269     velocity_filter_weight: 0.7,
44270     
44271     /**
44272      * @cfg {function} Callback when stroke begin. 
44273      */
44274     onBegin: false,
44275     
44276     /**
44277      * @cfg {function} Callback when stroke end.
44278      */
44279     onEnd: false,
44280     
44281     getAutoCreate : function()
44282     {
44283         var cls = 'roo-signature column';
44284         
44285         if(this.cls){
44286             cls += ' ' + this.cls;
44287         }
44288         
44289         var col_sizes = [
44290             'lg',
44291             'md',
44292             'sm',
44293             'xs'
44294         ];
44295         
44296         for(var i = 0; i < col_sizes.length; i++) {
44297             if(this[col_sizes[i]]) {
44298                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44299             }
44300         }
44301         
44302         var cfg = {
44303             tag: 'div',
44304             cls: cls,
44305             cn: [
44306                 {
44307                     tag: 'div',
44308                     cls: 'roo-signature-body',
44309                     cn: [
44310                         {
44311                             tag: 'canvas',
44312                             cls: 'roo-signature-body-canvas',
44313                             height: this.canvas_height,
44314                             width: this.canvas_width
44315                         }
44316                     ]
44317                 },
44318                 {
44319                     tag: 'input',
44320                     type: 'file',
44321                     style: 'display: none'
44322                 }
44323             ]
44324         };
44325         
44326         return cfg;
44327     },
44328     
44329     initEvents: function() 
44330     {
44331         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44332         
44333         var canvas = this.canvasEl();
44334         
44335         // mouse && touch event swapping...
44336         canvas.dom.style.touchAction = 'none';
44337         canvas.dom.style.msTouchAction = 'none';
44338         
44339         this.mouse_btn_down = false;
44340         canvas.on('mousedown', this._handleMouseDown, this);
44341         canvas.on('mousemove', this._handleMouseMove, this);
44342         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44343         
44344         if (window.PointerEvent) {
44345             canvas.on('pointerdown', this._handleMouseDown, this);
44346             canvas.on('pointermove', this._handleMouseMove, this);
44347             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44348         }
44349         
44350         if ('ontouchstart' in window) {
44351             canvas.on('touchstart', this._handleTouchStart, this);
44352             canvas.on('touchmove', this._handleTouchMove, this);
44353             canvas.on('touchend', this._handleTouchEnd, this);
44354         }
44355         
44356         Roo.EventManager.onWindowResize(this.resize, this, true);
44357         
44358         // file input event
44359         this.fileEl().on('change', this.uploadImage, this);
44360         
44361         this.clear();
44362         
44363         this.resize();
44364     },
44365     
44366     resize: function(){
44367         
44368         var canvas = this.canvasEl().dom;
44369         var ctx = this.canvasElCtx();
44370         var img_data = false;
44371         
44372         if(canvas.width > 0) {
44373             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44374         }
44375         // setting canvas width will clean img data
44376         canvas.width = 0;
44377         
44378         var style = window.getComputedStyle ? 
44379             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44380             
44381         var padding_left = parseInt(style.paddingLeft) || 0;
44382         var padding_right = parseInt(style.paddingRight) || 0;
44383         
44384         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44385         
44386         if(img_data) {
44387             ctx.putImageData(img_data, 0, 0);
44388         }
44389     },
44390     
44391     _handleMouseDown: function(e)
44392     {
44393         if (e.browserEvent.which === 1) {
44394             this.mouse_btn_down = true;
44395             this.strokeBegin(e);
44396         }
44397     },
44398     
44399     _handleMouseMove: function (e)
44400     {
44401         if (this.mouse_btn_down) {
44402             this.strokeMoveUpdate(e);
44403         }
44404     },
44405     
44406     _handleMouseUp: function (e)
44407     {
44408         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44409             this.mouse_btn_down = false;
44410             this.strokeEnd(e);
44411         }
44412     },
44413     
44414     _handleTouchStart: function (e) {
44415         
44416         e.preventDefault();
44417         if (e.browserEvent.targetTouches.length === 1) {
44418             // var touch = e.browserEvent.changedTouches[0];
44419             // this.strokeBegin(touch);
44420             
44421              this.strokeBegin(e); // assume e catching the correct xy...
44422         }
44423     },
44424     
44425     _handleTouchMove: function (e) {
44426         e.preventDefault();
44427         // var touch = event.targetTouches[0];
44428         // _this._strokeMoveUpdate(touch);
44429         this.strokeMoveUpdate(e);
44430     },
44431     
44432     _handleTouchEnd: function (e) {
44433         var wasCanvasTouched = e.target === this.canvasEl().dom;
44434         if (wasCanvasTouched) {
44435             e.preventDefault();
44436             // var touch = event.changedTouches[0];
44437             // _this._strokeEnd(touch);
44438             this.strokeEnd(e);
44439         }
44440     },
44441     
44442     reset: function () {
44443         this._lastPoints = [];
44444         this._lastVelocity = 0;
44445         this._lastWidth = (this.min_width + this.max_width) / 2;
44446         this.canvasElCtx().fillStyle = this.dot_color;
44447     },
44448     
44449     strokeMoveUpdate: function(e)
44450     {
44451         this.strokeUpdate(e);
44452         
44453         if (this.throttle) {
44454             this.throttleStroke(this.strokeUpdate, this.throttle);
44455         }
44456         else {
44457             this.strokeUpdate(e);
44458         }
44459     },
44460     
44461     strokeBegin: function(e)
44462     {
44463         var newPointGroup = {
44464             color: this.dot_color,
44465             points: []
44466         };
44467         
44468         if (typeof this.onBegin === 'function') {
44469             this.onBegin(e);
44470         }
44471         
44472         this.curve_data.push(newPointGroup);
44473         this.reset();
44474         this.strokeUpdate(e);
44475     },
44476     
44477     strokeUpdate: function(e)
44478     {
44479         var rect = this.canvasEl().dom.getBoundingClientRect();
44480         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44481         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44482         var lastPoints = lastPointGroup.points;
44483         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44484         var isLastPointTooClose = lastPoint
44485             ? point.distanceTo(lastPoint) <= this.min_distance
44486             : false;
44487         var color = lastPointGroup.color;
44488         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44489             var curve = this.addPoint(point);
44490             if (!lastPoint) {
44491                 this.drawDot({color: color, point: point});
44492             }
44493             else if (curve) {
44494                 this.drawCurve({color: color, curve: curve});
44495             }
44496             lastPoints.push({
44497                 time: point.time,
44498                 x: point.x,
44499                 y: point.y
44500             });
44501         }
44502     },
44503     
44504     strokeEnd: function(e)
44505     {
44506         this.strokeUpdate(e);
44507         if (typeof this.onEnd === 'function') {
44508             this.onEnd(e);
44509         }
44510     },
44511     
44512     addPoint:  function (point) {
44513         var _lastPoints = this._lastPoints;
44514         _lastPoints.push(point);
44515         if (_lastPoints.length > 2) {
44516             if (_lastPoints.length === 3) {
44517                 _lastPoints.unshift(_lastPoints[0]);
44518             }
44519             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44520             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44521             _lastPoints.shift();
44522             return curve;
44523         }
44524         return null;
44525     },
44526     
44527     calculateCurveWidths: function (startPoint, endPoint) {
44528         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44529             (1 - this.velocity_filter_weight) * this._lastVelocity;
44530
44531         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44532         var widths = {
44533             end: newWidth,
44534             start: this._lastWidth
44535         };
44536         
44537         this._lastVelocity = velocity;
44538         this._lastWidth = newWidth;
44539         return widths;
44540     },
44541     
44542     drawDot: function (_a) {
44543         var color = _a.color, point = _a.point;
44544         var ctx = this.canvasElCtx();
44545         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44546         ctx.beginPath();
44547         this.drawCurveSegment(point.x, point.y, width);
44548         ctx.closePath();
44549         ctx.fillStyle = color;
44550         ctx.fill();
44551     },
44552     
44553     drawCurve: function (_a) {
44554         var color = _a.color, curve = _a.curve;
44555         var ctx = this.canvasElCtx();
44556         var widthDelta = curve.endWidth - curve.startWidth;
44557         var drawSteps = Math.floor(curve.length()) * 2;
44558         ctx.beginPath();
44559         ctx.fillStyle = color;
44560         for (var i = 0; i < drawSteps; i += 1) {
44561         var t = i / drawSteps;
44562         var tt = t * t;
44563         var ttt = tt * t;
44564         var u = 1 - t;
44565         var uu = u * u;
44566         var uuu = uu * u;
44567         var x = uuu * curve.startPoint.x;
44568         x += 3 * uu * t * curve.control1.x;
44569         x += 3 * u * tt * curve.control2.x;
44570         x += ttt * curve.endPoint.x;
44571         var y = uuu * curve.startPoint.y;
44572         y += 3 * uu * t * curve.control1.y;
44573         y += 3 * u * tt * curve.control2.y;
44574         y += ttt * curve.endPoint.y;
44575         var width = curve.startWidth + ttt * widthDelta;
44576         this.drawCurveSegment(x, y, width);
44577         }
44578         ctx.closePath();
44579         ctx.fill();
44580     },
44581     
44582     drawCurveSegment: function (x, y, width) {
44583         var ctx = this.canvasElCtx();
44584         ctx.moveTo(x, y);
44585         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44586         this.is_empty = false;
44587     },
44588     
44589     clear: function()
44590     {
44591         var ctx = this.canvasElCtx();
44592         var canvas = this.canvasEl().dom;
44593         ctx.fillStyle = this.bg_color;
44594         ctx.clearRect(0, 0, canvas.width, canvas.height);
44595         ctx.fillRect(0, 0, canvas.width, canvas.height);
44596         this.curve_data = [];
44597         this.reset();
44598         this.is_empty = true;
44599     },
44600     
44601     fileEl: function()
44602     {
44603         return  this.el.select('input',true).first();
44604     },
44605     
44606     canvasEl: function()
44607     {
44608         return this.el.select('canvas',true).first();
44609     },
44610     
44611     canvasElCtx: function()
44612     {
44613         return this.el.select('canvas',true).first().dom.getContext('2d');
44614     },
44615     
44616     getImage: function(type)
44617     {
44618         if(this.is_empty) {
44619             return false;
44620         }
44621         
44622         // encryption ?
44623         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44624     },
44625     
44626     drawFromImage: function(img_src)
44627     {
44628         var img = new Image();
44629         
44630         img.onload = function(){
44631             this.canvasElCtx().drawImage(img, 0, 0);
44632         }.bind(this);
44633         
44634         img.src = img_src;
44635         
44636         this.is_empty = false;
44637     },
44638     
44639     selectImage: function()
44640     {
44641         this.fileEl().dom.click();
44642     },
44643     
44644     uploadImage: function(e)
44645     {
44646         var reader = new FileReader();
44647         
44648         reader.onload = function(e){
44649             var img = new Image();
44650             img.onload = function(){
44651                 this.reset();
44652                 this.canvasElCtx().drawImage(img, 0, 0);
44653             }.bind(this);
44654             img.src = e.target.result;
44655         }.bind(this);
44656         
44657         reader.readAsDataURL(e.target.files[0]);
44658     },
44659     
44660     // Bezier Point Constructor
44661     Point: (function () {
44662         function Point(x, y, time) {
44663             this.x = x;
44664             this.y = y;
44665             this.time = time || Date.now();
44666         }
44667         Point.prototype.distanceTo = function (start) {
44668             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44669         };
44670         Point.prototype.equals = function (other) {
44671             return this.x === other.x && this.y === other.y && this.time === other.time;
44672         };
44673         Point.prototype.velocityFrom = function (start) {
44674             return this.time !== start.time
44675             ? this.distanceTo(start) / (this.time - start.time)
44676             : 0;
44677         };
44678         return Point;
44679     }()),
44680     
44681     
44682     // Bezier Constructor
44683     Bezier: (function () {
44684         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44685             this.startPoint = startPoint;
44686             this.control2 = control2;
44687             this.control1 = control1;
44688             this.endPoint = endPoint;
44689             this.startWidth = startWidth;
44690             this.endWidth = endWidth;
44691         }
44692         Bezier.fromPoints = function (points, widths, scope) {
44693             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44694             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44695             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44696         };
44697         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44698             var dx1 = s1.x - s2.x;
44699             var dy1 = s1.y - s2.y;
44700             var dx2 = s2.x - s3.x;
44701             var dy2 = s2.y - s3.y;
44702             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44703             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44704             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44705             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44706             var dxm = m1.x - m2.x;
44707             var dym = m1.y - m2.y;
44708             var k = l2 / (l1 + l2);
44709             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44710             var tx = s2.x - cm.x;
44711             var ty = s2.y - cm.y;
44712             return {
44713                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44714                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44715             };
44716         };
44717         Bezier.prototype.length = function () {
44718             var steps = 10;
44719             var length = 0;
44720             var px;
44721             var py;
44722             for (var i = 0; i <= steps; i += 1) {
44723                 var t = i / steps;
44724                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44725                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44726                 if (i > 0) {
44727                     var xdiff = cx - px;
44728                     var ydiff = cy - py;
44729                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44730                 }
44731                 px = cx;
44732                 py = cy;
44733             }
44734             return length;
44735         };
44736         Bezier.prototype.point = function (t, start, c1, c2, end) {
44737             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44738             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44739             + (3.0 * c2 * (1.0 - t) * t * t)
44740             + (end * t * t * t);
44741         };
44742         return Bezier;
44743     }()),
44744     
44745     throttleStroke: function(fn, wait) {
44746       if (wait === void 0) { wait = 250; }
44747       var previous = 0;
44748       var timeout = null;
44749       var result;
44750       var storedContext;
44751       var storedArgs;
44752       var later = function () {
44753           previous = Date.now();
44754           timeout = null;
44755           result = fn.apply(storedContext, storedArgs);
44756           if (!timeout) {
44757               storedContext = null;
44758               storedArgs = [];
44759           }
44760       };
44761       return function wrapper() {
44762           var args = [];
44763           for (var _i = 0; _i < arguments.length; _i++) {
44764               args[_i] = arguments[_i];
44765           }
44766           var now = Date.now();
44767           var remaining = wait - (now - previous);
44768           storedContext = this;
44769           storedArgs = args;
44770           if (remaining <= 0 || remaining > wait) {
44771               if (timeout) {
44772                   clearTimeout(timeout);
44773                   timeout = null;
44774               }
44775               previous = now;
44776               result = fn.apply(storedContext, storedArgs);
44777               if (!timeout) {
44778                   storedContext = null;
44779                   storedArgs = [];
44780               }
44781           }
44782           else if (!timeout) {
44783               timeout = window.setTimeout(later, remaining);
44784           }
44785           return result;
44786       };
44787   }
44788   
44789 });
44790
44791  
44792
44793